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

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 that this code must be placed at the top of your script before any other code.

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

$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]

22 Responses to “Workaround: The OS handle’s position is not what FileStream expected”

  1. Derek writes:

    Bug is still there in server 2008 R2 SP1! Microsoft fix this, please!!!

  2. David Fugate writes:

    The workarounds are quite useful, but will this ever get fixed?

  3. Jacques writes:

    Many thanks for this: I had a script running in an automation tool that was failing because of this
    bug, and now it works without error. It would be great for the issue to be fixed in Powershell though.

  4. Dave writes:

    Many thanks for this fix, it saved me lots of digging through msdn and it would have taken a while to spot the actual bug in my environment.

    As for this getting fixed in a later version… I’m going to go ahead and say doubtful. It would need to change the way that powershell handles being called from elsewhere, insofar as it acquires output streams.

    What I think is more likely is cmd.exe getting phased out and powershell ending up as the lowest-possible-level user of those streams.

  5. Robert Klujeff writes:

    Hello

    I used your fix for PowerShell 1.0, and it worked perfectly. Does anybody have any experience with this fix in relation to PowerShell 2.0, is it still needed?

    Robert

  6. Anonymous writes:

    Still there as f today in V2.0 on a recently patched server; for u it comes up when running from AutoSys. The fix for v2 above works ok.

  7. Anonymous writes:

    Same here, it causes problems in Autosys Jobs.

  8. Khatib writes:

    Same problem with Powershell V2 script called through Autsosy

  9. Vern Martin writes:

    Hard to believe that on 10/6/11 on Windows 2008 R2 that this is STILL an issue. But it is. And this page was a life saver. Thanks!

  10. Nikolay Klimchuk writes:

    Also experience this issue. This is VERY annoying!

  11. Lee Holmes writes:

    We hear you about this being very annoying. We had seen the bug once in a while in PowerShell version 1, but didn’t have a good example of what caused it (and didn’t see that it was impacting many people) We finally nailed down the cause late in PowerShell version 2, but were so close to releasing Windows 7 (still without evidence that it was impacting people) that we decided to not risk a fix. If you download the Windows 8 Developer Preview, you’ll see that we’ve fixed this now :)

    Lee

  12. Robert Klujeff writes:

    I reported this error to Microsoft years ago, and after a while it war recognized as an error. MS said it would NOT be fixed, it was too complicated. MS Case no. 110081742134557. It has cost us hundreds of hours, and still is, right now I am working on a new server, where PowerShell still fails with this error (2008 server). We are calling PowerShell from within a product called UC4, and the way they have integrated PowerShell makes this problem VERY big. I have spend hundreds of hours rewriting PowerShell code, in order to avoid this situation.

  13. Ruben Bartelink writes:

    Ditto the hundreds of hours – this sort of nonsense makes people rewrite log maintenance code in freaking Windows Workflow. I simply do not buy the nobody seeing it nonsense. I saw it in week 1 of using the product. You guys did too and these sorts of bugs are the ones we want fixed – global ones that only you can do. Any issue with a Task can be routed around – this is the single most painful issue I’ve experienced with MS products since the bad days of Visual C++ and it’s toolery in the last century

  14. Mike Lewis writes:

    I really appreciate your posting of this article. I am running PowerShell from within UC4 (a scheduler product), and I continually fought this. I always seemed to find something that worked for each scenario, but what worked for one did not work for the other.
    I probably spent more time trying to fix this issue than on trying solve any other single problem with PowerShell. Your work-around works great. Thanks!

  15. Tim Lovell-Smith writes:

    Wow, thanks for writing up an explanation of this that confirms an intuitive guess at was happening. And a workaround makes this 10 times better. Really appreciate it!

  16. Anonymous writes:

    No dice – the V2 fix doesn’t work. Like many of the other users, I’m seeing this issue when calling PowerShell scripts from CA Autosys.

    The error I’m seeing with the V2 fix listed above:
    You cannot call a method on a null-valued expression.
    At C:\Program Files\PowerGUI\actimize_daily_run.ps1:17 char:96
    + [void] $consoleHost.GetType().GetProperty(“IsStandardOutputRedirected”, $bindingFlags).GetValue <<<< ($consoleHost, @())
    + CategoryInfo : InvalidOperation: (GetValue:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Program Files\PowerGUI\actimize_daily_run.ps1:20 char:16
    + $field.SetValue <<<< ($consoleHost, [Console]::Out)
    + CategoryInfo : InvalidOperation: (SetValue:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    You cannot call a method on a null-valued expression.
    At C:\Program Files\PowerGUI\actimize_daily_run.ps1:22 char:17
    + $field2.SetValue <<<< ($consoleHost, [Console]::Out)
    + CategoryInfo : InvalidOperation: (SetValue:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

  17. Anonymous writes:

    While the V2 fix listed above doesn’t work, I found what appears to be a solution to my specific problem.

    Basically I was using Powershell as a wrapper around a vendor-provided batch file, in order to pass it some parameters. I’m using CA Autosys R11 to call “powershell.exe -f “x:\path\script.ps1″. In the batch file itself, a line containing “x:\path\script.bat $parm1 $parm2″ (no quotes) called the batch file. I appended the line to “x:\path\script1.bat $parm1 $parm2 | Out-Default” (no quotes) and all output appeared correctly in the CA Autosys R11 STDOUT log.

    YMMV, of course. I haven’t tested this with embedded code that creates STDERR yet.

  18. Harry Johnston writes:

    As another option, you could create a parent script to run the child script using this syntax:

    powershell .\child.ps1 2>&1 | more

    Hiding the fact that the output is going to a file should prevent the bug from happening. (You’re going to want to check whether output is actually redirected first.)

    It may well be possible to avoid invoking more.com, perhaps by making the parent script do the piping itself, but I’ll leave this to someone else to investigate. :-)

  19. astridas writes:

    For anyone still using this code it might be worth noting that this will not run in ISE. Wrap inside an if statement $host.Name -eq ‘ConsoleHost’ for console or $host.Name -ne ‘PowerShell’ to exclude ISE. Additionally I was still getting the error for Standard Error Redirection and had to add [void] $consoleHost.GetType().GetProperty(“IsStandardErrorRedirected”, $bindingFlags).GetValue($consoleHost, @()). I also updated $field2.SetValue($consoleHost, [Console]::Out) to $field2.SetValue($consoleHost, [Console]::Error) but have seen no difference in functionality.

  20. Ashraf Farok writes:

    Another workaround
    Before piping to a file you can redirect output to find and reprint again
    for example
    PowerShell -Command ‘”start”‘; Write-Error “Foo”; ‘”end”‘ | find /v “” > c:\temp\redirect.log 2>&1

  21. Goriot writes:

    This last workaround is fine but it will mask the error code returned by powershell.

  22. Logging in Powershell | run as me writes:

    […] There is an issue with v2 regarding redirection. […]

Leave a Reply