Automating the world one-liner at a time…
Recently, I came across an interesting bug in PowerShell. Let’s create a repro.
First, we create a string “a” and generate an xml based representation of it using the Export-Clixml cmdlet. Since we need to have the class id, we pipe the string to format-table as shown in the example below.
PS> "a" | format-table -auto | Export-Clixml a.xml
Here is the content of the xml file.
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04"> <Obj RefId="0"> <TN RefId="0"> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData</T> <T>Microsoft.PowerShell.Commands.Internal.Format.PacketInfoData</T> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData</T> <T>System.Object</T> </TN> <ToString>Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData</ToString> <Props> <S N="ClassId2e4f51ef21dd47e99d3c952918aff9cd">27c87ef9bbda4f709f6b4002fa4af63c</S> <Obj N="formatEntryInfo" RefId="1"> <TN RefId="1"> <T>Microsoft.PowerShell.Commands.Internal.Format.RawTextFormatEntry</T> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatEntryInfo</T> <T>Microsoft.PowerShell.Commands.Internal.Format.FormatInfoData</T> <T>System.Object</T> </TN> <ToString>Microsoft.PowerShell.Commands.Internal.Format.RawTextFormatEntry</ToString> <Props> <S N="ClassId2e4f51ef21dd47e99d3c952918aff9cd">29ED81BA914544d4BC430F027EE053E9</S> <S N="text">a</S> </Props> </Obj> <B N="outOfBand">true</B> <B N="writeErrorStream">false</B> </Props> </Obj></Objs>
Then, we remove the class id (in blue) and save the xml file. Next, we recreate an object using the import-cliXml cmdlet and we assign it to a variable, i.e. $a. We should get an exception because the class id is missing but, we do not.
PS> $a = Import-Clixml .\a.xml
On the other hand, if we execute the same command without assigning it to a variable, the exception is thrown.
PS> Import-Clixml .\a.xml
Unknown class Id . + CategoryInfo : InvalidData: (Microsoft.Power...FormatEntryData:PS Object) [out-lineoutput], PSArgumentException + FullyQualifiedErrorId : FormatObjectDeserializerDeserializeInvalidClassId, Microsoft.PowerShell.Commands.OutLineOutputCommand
In the first instance, the object gets assigned to the variable and it never goes to the F&O system. Thus the terminating error is never generated.
The fix is fairly simple, Out-String will send the object to the host through the F&O system.
PS> $a = Import-Clixml .\a.xml | Out-String
out-lineoutput : Unknown class Id . + CategoryInfo : InvalidData: (Microsoft.Power...FormatEntryData:PSObject ) [out-lineoutput], PSArgumentException + FullyQualifiedErrorId : FormatObjectDeserializerDeserializeInvalidClassId,Micros oft.PowerShell.Commands.OutLineOutputCommand
To make sure our script ends nicely, we catch the terminating error.
$e = $null try{ Import-Clixml .\a.xml | Out-String } catch{ $e = $_ } If($e –ne $null) { $e.FullyQualifiedErrorId }
FormatObjectDeserializerDeserializeInvalidClassId,Microsoft.PowerShell.Commands.OutLineOut putCommand
Cheers, Francisco Gomez Gamino [MSFT]
I do not get your error on XP SP2 PoSH V2.
10:52 PS> Import-Clixml .\a.xml
a
10:53 PS>
No exception is thrown.
XML is identical.
What does F&O system mean?
Also, on a side note, the error should still be getting sent to the error stream when the output is getting assigned to the variable. The only thing that the variable is "catching" is the pipeline output from the expression on the right-hand side of the assignment operator.
I have a feeling that something else is going on here.