arrayspowershelltype-conversionuint16

Converting a System.UInt16 array to a string


I'm experiencing a weird issue where I need to remove "blank characters" which are probably not spaces or tabs.

It's information from WMI. The "SerialNumberID" property is stored as a UInt16[] Array.

I gather it like this :

$SERIAL = (Get-WmiObject -ComputerName XXXX WmiMonitorID -Namespace root\wmi ).SerialNumberID

This now contains the following array:

72
86
76
81
66
48
50
53
52
50
0
0
0
0
0
0

To convert that to a readable format, I'm doing :

$SERIAL_AS_STRING = [System.Text.Encoding]::Default.GetString($SERIAL)

"+$SERIAL_AS_STRING+"

(I'm using the + character to easily identify if there are characters after the data)

This returns :

+HVLQB02542      +

I want to have just the serial number, so i'm trying to get rid of the spaces like this :

$SERIAL_AS_STRING = $SERIAL_AS_STRING.Trim()

"+$SERIAL_AS_STRING+"

and it STILL returns with the blank spaces! :

+HVLQB02542      +

Even if I try something like :

$SERIAL_AS_STRING = $SERIAL_AS_STRING -replace '(^\s+|\s+$)','' -replace '\s+',' '

the spaces are always there.

Now I see that in the System.UInt16 array I have SIX trailing zeros, which precisely fit with the SIX trailing spaces in my result string....

So I tried a different method to convert the array which is :

$SERIAL2 = ($SERIAL -notmatch 0 | ForEach{[char]$_}) -join ""

"+$SERIAL2+"

This works to remove the trailing spaces however it's unnaceptable because it messes up the serial like this :

+HVLQB054+

Which should be : +HVLQB02542+

What am I doing wrong here? It's strange because if i specially ask to replace SIX spaces, it doesn't work either. This is why i'm suspecting these "spaces" are not really space characters!

For now I'm making it work by using RegEx, which removes anything that is not letters or numbers :

$FINAL = $SERIAL_AS_STRING -replace "[^A-Za-z0-9]"

But how can it be done properly, as this feels like a workaround solution?


Solution

  • What .SerialNumberID outputs is an int [uint16[]] array (see the docs).

    If you want to convert these uint16 instances to a string, you can simply interpret them directly as Unicode UTF-16 code points (the Unicode equivalent of an ASCII code, of which it is a superset), which is how characters ([char]) instances are represented in memory in .NET.

    Thus, you can simply cast to [char[]] and join -join the resulting character array to form a [string].

    (That is, there is no need for [System.Text.Encoding]::Default.GetString(), which, incidentally, would interpret the values as bytes.[1])

    Your .SerialNumberID has trailing 0 instances, however, which both [char[]] and [System.Text.Encoding]::Default.GetString() retain in the conversion, as NUL characters.

    You can remove the unwanted 0 / NUL instances by simply filtering them out from the input array with -ne 0 before the conversion.

    To put it all together:

    # Simulate the result of your (Get-WmiObject ...).SerialNumberID call
    $SERIAL = [uint16[]] (72, 86, 76, 81, 66, 48, 50, 53, 52, 50, 0, 0, 0, 0, 0, 0)
    
    $SERIAL_AS_STRING = -join [char[]] ($SERIAL -ne 0)
    
    # Show the resulting string
    "+$SERIAL_AS_STRING+"
    

    The above yields +HVLQB02542+, as desired.


    [1] It is only if the input array represents a specific character encoding other than UTF-16 that you need the .GetString() method of the appropriate [System.Text.Encoding] instance; note that that method requires a [byte[]] array, and while PowerShell will try to automatically coerce a [uint16[]] to that, doing so will fail if any of the array elements have a value greater than 255 (0xFF), i.e., are too large to fit into a [byte].