Skip to main content Link Menu Expand (external link) Document Search Copy Copied

I came across a problem recently when I was writing a small test Powershell script that needed to inspect a binary message that contained bytes that represented bit flags. I decided to use the BitArray class from the .NET framework to make the job a little easier but instead of getting a bit array consisting of the 16 bits from 2 bytes it kept reporting only 3 bits.

This problem is when attempting to use the constructor override of the BitArray class that accepts a byte array.

Here is the example written long hand (i.e. original syntax with no Powershell syntax shortcuts):

[code lang=”text”] $b = [byte[]]@(0x03, 0x02) $ba = New-Object -TypeName 'System.Collections.BitArray' -ArgumentList $b Write-host “Count: $($ba.Count)” foreach ($bit in $ba) { Write-Host $bit } [/code]

That script block produces the following output:

[code lang=”text”] Count: 3 True True True [/code]

As you can see, instead of taking the two bytes in the array and displayed them as 16 bit flags as you’d expect, its taken the first byte from the array and used its value as the number of bits to store in the new BitArray object.

i.e. it looks like its used this constructor:

[code lang=”text”] public BitArray(int length, bool defaultValue) [/code]

Instead of the constuctor that I intended:

[code lang=”text”] public BitArray(byte[] bytes) [/code]

We can confirm that the value we passed in was of the correct type and of the expected size with the following code:

[code lang=”text”] $b = [byte[]]@(0x03, 0x02) Get-TypeName $b Write-Host “Count: $($b.Count)” [/code]

Which outputs:

[code lang=”text”] Byte[] Count: 2 [/code]

That looks correct. Lets confirm which override its using by tracing the member resolution as demonstrated in the following code:

[code lang=”text”] $b = [byte[]]@(0x03, 0x02) Trace-Command -psHost -name 'MemberResolution' { $ba = new-object -TypeName 'System.Collections.BitArray' -ArgumentList $b } [/code]

This produces the following output:

[code lang=”text”] DEBUG: MemberResolution Information: 0 : Method argument conversion. DEBUG: MemberResolution Information: 0 : Converting parameter “3” to “System.Int32”. DEBUG: MemberResolution Information: 0 : Method argument conversion. DEBUG: MemberResolution Information: 0 : Converting parameter “2” to “System.Boolean”. DEBUG: MemberResolution Information: 0 : Calling Constructor: .ctor(int length, bool defaultValue) DEBUG: MemberResolution Information: 0 : Checking for possible references. [/code]

This confirms the constructor choice that we suspected and also tells us that it broke apart our byte array to achieve it.

This is all down to the New-Object commandlet. The ArgumentList parameter expects an array of values that match the number of arguments in the method to call. It will then attempt to coerce the provided values into the expected types.

So, basically this situation only arose because our input array had a number of items matching a method/constructor override and it was able to convert the values to match.

If we’d have had 3 items in the array we would have seen the following error because there aren’t any constructor overrides that accept 3 arguments:

[code lang=”text”] New-Object : Cannot find an overload for “BitArray” and the argument count: “3”. At C:\Users\bradshawk\AppData\Local\Temp38fc27a-9de9-46c0-bc1f-d630a2d89b8e.ps1:2 char:7

  • $ba = New-Object -TypeName 'System.Collections.BitArray' -ArgumentList $b
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [New-Object], MethodException + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand [/code]

The solution is the same regardless of the number of items in the array, to pass an array of arguments, the first of which is our byte array:

[code lang=”text”] $b = [byte[]]@(0x03, 0x02) $ba = New-Object -TypeName 'System.Collections.BitArray' -ArgumentList @(,$b) [/code]