PowerShell Cookbook

Search

Categories

 

On this page

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: 262
This Year: 13
This Month: 0
This Week: 0
Comments: 818

Sign In

 Wednesday, July 30, 2008
Wednesday, July 30, 2008 11:15:16 AM (Pacific Standard Time, UTC-08: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 [4] | | # 
Wednesday, September 02, 2009 11:27:47 AM (Pacific Standard Time, UTC-08:00)
The workarounds are quite useful, but will this ever get fixed?
Friday, October 30, 2009 3:09:29 AM (Pacific Standard Time, UTC-08:00)
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.
Jacques
Wednesday, November 18, 2009 7:30:14 AM (Pacific Standard Time, UTC-08:00)
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.
Tuesday, June 01, 2010 1:17:24 AM (Pacific Standard Time, UTC-08:00)
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
Name
E-mail
Home page

Comment (Some html is allowed: b, blockquote@cite, em, i, strike, strong, sub, super, u)  

Enter the code shown (prevents robots):