PowerShell Cookbook

Twitter Updates

    follow me on Twitter

    Search

    Categories

     

    On this page

    Workaround: The OS handle's position is not what FileStream expected

    Archive

    Blogroll

    Disclaimer
    I work for Microsoft.

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    RSS 2.0 | Atom 1.0 | CDF

    Send mail to the author(s) E-mail

    Total Posts: 235
    This Year: 12
    This Month: 0
    This Week: 0
    Comments: 634

    Sign In

     Wednesday, July 30, 2008
    Wednesday, July 30, 2008 7:15:16 PM (Pacific Daylight Time, UTC-07:00) ( )

    If you have a PowerShell script that you are calling from cmd.exe, you might run into the following error:

    Write-Host : The OS handle's position is not what FileStream expected. Do not use a handle simultaneously in one FileStream and in Win32 code or another FileStream. This may cause data loss.

    This is bug in PowerShell, and happens when:

    • a PowerShell command generates both regular and error output
    • you have used cmd.exe to redirect the output to a file
    • you have used cmd.exe to merge the output and error streams

    For example:

    PowerShell -Command '"start"'; Write-Error "Foo"; '"end"' > c:\temp\redirect.log 2>&1

    One workaround is to use Start-Transcript for file logging (rather than cmd.exe) or have PowerShell do the error stream redirection.

    However, if you don't have control over your logging, you can add the following snippet to any scripts that get launched this way.

    Note: this is an unsupported workaround. It will almost definitely break as future versions of PowerShell are released.

     

    V1

    $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
    $consoleHost = $host.GetType().GetField("externalHost", $bindingFlags).GetValue($host)
    [void] $consoleHost.GetType().GetProperty("IsStandardOutputRedirected", $bindingFlags).GetValue($consoleHost, @())

    $field = $consoleHost.GetType().GetField("standardOutputWriter", $bindingFlags)
    $field.SetValue($consoleHost, [Console]::Out)
    $field2 = $consoleHost.GetType().GetField("standardErrorWriter", $bindingFlags)
    $field2.SetValue($consoleHost, [Console]::Out)

    V2 CTP2

    $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
    $objectRef = $host.GetType().GetField("externalHostRef", $bindingFlags).GetValue($host)

    $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty"
    $consoleHost = $objectRef.GetType().GetProperty("Value", $bindingFlags).GetValue($objectRef, @())

    [void] $consoleHost.GetType().GetProperty("IsStandardOutputRedirected", $bindingFlags).GetValue($consoleHost, @())
    $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField"
    $field = $consoleHost.GetType().GetField("standardOutputWriter", $bindingFlags)
    $field.SetValue($consoleHost, [Console]::Out)
    $field2 = $consoleHost.GetType().GetField("standardErrorWriter", $bindingFlags)
    $field2.SetValue($consoleHost, [Console]::Out)

    How does this work, and why does this happen in the first place? When PowerShell sends output to its output stream the first time, it keeps a reference to the output stream for future use. However, this output stream is really a wrapper around a lower-level stream. When cmd.exe writes to the output stream, it writes to the lower-level stream. This makes the .NET wrapper complain that the underlying stream has changed from beneath it.

    This workaround modifies some private engine state to not keep a reference to the output stream -- but instead to re-examine the output stream on every use.

    [Edit 03/12/09 - Updated to also fix Standard Error, as mentioned in the PowerShellWizard Blog]

    Comments [0] | | #