WMI uses its own data types that differ from the data types used by PowerShell.
When WMI Data Types Matter
When you read data from WMI and output it in reports, you do not need to care much about data types because numbers look the same when outputted as text.
As soon as you process WMI output, though, correct data types matter. Both PowerShell and WMI can understand each other only when they understand how to interpret the data. Problems can occur both ways:
- When you call WMI methods and send arguments that use PowerShell data types, WMI may not interpret the data correctly. Make sure you convert your arguments to the exact data types the WMI method expects to receive.
- When PowerShell processes data received from WMI, it may not recognize the WMI data types. Make sure you convert the received WMI data to the corresponding PowerShell data types where appropriate.
Here is an example that illustrates the problem and ways to work around it:
Translating WMI Codes to Friendly Text
WMI information is often coded and uses numeric values instead of meaningful text.
For example, the WMI class Win32_ComputerSystem contains the property AdminPasswordStatus. When you look up this property, you learn that this property returns the hardware security settings for the administrator password status. The information uses the data type [UInt16] (unsigned 16-bit integer). The reference also returns a PowerShell hashtable to translate the values into text:
$AdminPasswordStatus_map = @{
0 = 'Disabled'
1 = 'Enabled'
2 = 'Not Implemented'
3 = 'Unknown'
}
You can now take the value returned by WMI and use it as a key to read the friendly text from the hashtable. However, when you ignore data types, this won’t work, and PowerShell just returns “nothing”:
# example returns the current admin password status
# this example contains an error and returns "nothing"
# this hashtable translates the codes to
# meaningful friendly text:
$AdminPasswordStatus_map = @{
0 = 'Disabled'
1 = 'Enabled'
2 = 'Not Implemented'
3 = 'Unknown'
}
# get instance of Win32_ComputerSystem
# representing the current computer:
$computer = Get-CimInstance -Class Win32_ComputerSystem
# query the property AdminPasswordStatus
# it yields a numeric value
$rawValue = $computer.AdminPasswordStatus
# translate raw value to friendly text
# this yields empty information because PowerShell uses the
# data type [Int32] (signed 32-bit integers)
# while WMI sends the numeric value
# using the data type [UInt16] (unsigned 16-bit integers)
# so PowerShell cannot find the key in its
# hashtable and returns nothing:
$friendlyName = $AdminPasswordStatus_map[$rawValue]
# output value
"Admin Password Status: $friendlyName"
This problem occurs because hashtable keys are type-sensitive. PowerShell cannot find a [UInt16] typed key in the hashtable because numbers automatically use the type [Int32] in PowerShell.
The solution is trivial, though: convert the WMI value into the appropriate PowerShell type so that it matches the types of the hashtable keys:
# example returns the current admin password status
# this example converts the returned WMI value
# into the appropriate PowerShell data type:
$AdminPasswordStatus_map = @{
0 = 'Disabled'
1 = 'Enabled'
2 = 'Not Implemented'
3 = 'Unknown'
}
$computer = Get-CimInstance -Class Win32_ComputerSystem
$rawValue = $computer.AdminPasswordStatus
# convert the raw value to type [Int]:
$friendlyName = $AdminPasswordStatus_map[[Int]$rawValue]
# output value
"Admin Password Status: $friendlyName"
Now the code works and may look like this:
Admin Password Status: Not Implemented
“Not Implemented” is one of the available return values and indicates that your security hardware does not provide the requested information.
Mapping PowerShell and WMI Data Types
Here is a quick table of type mappings that you can use to convert WMI data types to PowerShell data types (and vice versa):
WMI data type to PowerShell mappings
WMI Data Type | PowerShell / .NET Data Type | Description |
---|---|---|
[UInt8] | [byte] | Unsigned 8-Bit Integer |
[UInt16] | [uint16] | Unsigned 16-Bit Integer |
[UInt8] | [uint32] | Unsigned 32-Bit Integer |
[UInt64] | [uint64] | Unsigned 64-Bit Integer |
[SInt8] | [sbyte] | Signed 8-Bit Integer |
[SInt16] | [int16] | Signed 16-Bit Integer |
[SInt32] | [int] | Signed 32-Bit Integer |
[SInt64] | [int64] | Signed 64-Bit Integer |
[Real32] | [single] | Signed 32-Bit Floating Point |
[Real64] | [double] | Signed 64-Bit Floating Point |
[Boolean] | [bool] | $true, $false |
[DateTime] | [DateTime]/[TimeSpan] | Date and time, or time interval |
[Char16] | [char] | 16-Bit Unicode Character |
[String] | [string] | Text |
WMI Data Type Description
This is a detailed description of all WMI data types, including the covered range of numbers.
UInt8
Unsigned 8-bit integer value (positive numbers only). The equivalent PowerShell data type is [Byte] ([System.Byte]).
Examples
# converts value 132 to SInt32:
$UInt8 = [Byte]132
# check result type (System.Byte):
$UInt8.GetType().FullName
UInt16
Unsigned 16-bit integer value (positive numbers only). The equivalent PowerShell data type is [UInt16] ([System.UInt16]).
Examples
# converts value 43105 to SInt32:
$UInt16 = [UInt16]43105
# check result type (System.UInt16):
$UInt16.GetType().FullName
UInt32
UINT32 MIN: 0 MAX: 4,294,967,295
Unsigned 32-bit integer value (positive numbers only). The equivalent PowerShell data type is [UInt32] ([System.UInt32]).
Examples
# converts value 43105 to SInt32:
$UInt32 = [UInt32]43105
# check result type (System.UInt32):
$UInt32.GetType().FullName
UInt64
UINT64 MIN: 0 MAX: 17,179,869,184 GB
Unsigned 64-bit integer value (positive numbers only). The equivalent PowerShell data type is [UInt64] ([System.UInt64]).
Examples
# converts value 43105 to SInt32:
$UInt64 = [UInt64]43105
# check result type (System.UInt64):
$UInt64.GetType().FullName
SInt8
Signed 8-bit integer value (negative and positive numbers). The equivalent PowerShell data type is [SByte] ([System.SByte]).
Examples
# converts value 53 to SInt32:
$SInt8 = [SByte]53
# check result type (System.SByte):
$SInt8.GetType().FullName
SInt16
INT16 MIN: -32,768 MAX: 32,767
Signed 16-bit integer value (negative and positive numbers). The equivalent PowerShell data type is [Int16] ([System.Int16]).
Examples
# converts value 53 to SInt32:
$SInt16 = [Int16]53
# check result type (System.Int16):
$SInt16.GetType().FullName
SInt32
INT32 MIN: -2,147,483,648 MAX: 2,147,483,647
Signed 32-bit integer value (negative and positive numbers). The equivalent PowerShell data type is [Int32] ([System.Int32]).
Examples
# converts value 53 to SInt32:
$SInt32 = [Int32]53
# check result type (System.Int32):
$SInt32.GetType().FullName
SInt64
INT64 MIN: –8,589,934,592 GB MAX: 8,589,934,592 GB
Signed 64-bit integer value (negative and positive numbers). The equivalent PowerShell data type is [Int64] ([System.Int64]).
Examples
# converts value 53 to SInt32:
$SInt64 = [Int64]53
# check result type (System.Int64):
$SInt64.GetType().FullName
Real32
SINGLE MIN: -3.02231436889259E+23 PB MAX: 3.02231436889259E+23 PB
32-bit floating-point value (negative and positive numbers). The equivalent PowerShell data type is [Single] ([System.Single]).
Examples
# converts value 53 to SInt32:
$Real32 = [Single]53
# check result type (System.Single):
$Real32.GetType().FullName
Real64
DOUBLE MIN: –1.59667224762778E+293 PB MAX: 1.59667224762778E+293 PB
64-bit floating-point value (negative and positive numbers). The equivalent PowerShell data type is [Double] ([System.Double]).
Examples
# converts value 53 to SInt32:
$Real64 = [Double]53
# check result type (System.Double):
$Real64.GetType().FullName
Boolean
Unsigned XXX-bit integer value (negative and positive numbers). The equivalent PowerShell data type is [Bool] ([Boolean], [System.Boolean]).
Examples
# converts value 0 to SInt32:
$Boolean = [Bool]0
# check result type (System.Boolean):
$Boolean.GetType().FullName
In PowerShell, the values 0, NULL, and Empty Strings evaluate to $false. Anything else evaluates to $true.
Examples
# assign anything to $anyvalue:
$anyvalue = 'test it'
# always either $false or $true
[bool]$anyvalue
# always either "0" or "1"
[byte][bool]$anyvalue
DateTime
DATETIME MIN: Jan 1, 0001 MAX: Dec 31, 9999
Represents absolute date and time or a timespan (interval). The equivalent PowerShell data type for date and time is [DateTime] ([System.DateTime]). For intervals, the datatype [TimeSpan] ([System.Timespan]) is used.
WMI represents date and time internally in a string-based format: yyyymmddHHMMSS.mmmmmmsUUU. Cmdlets like Get-CimInstance
automatically convert WMI datetime and intervals to the proper .NET types [DateTime]/[TimeSpan].
Examples
# converts value 53 to SInt32:
$DateTime = [DateTime]53
# check result type (System.DateTime):
$DateTime.GetType().FullName
You may be forced to do the conversion manually when you call internal WMI methods and need to submit information as parameter, or when you work with the deprecated cmdlet Get-WmiObject
.
Convert PowerShell [DateTime] into WMF format (or vice-versa)
# convert WMI DateTime to PowerShell DateTime:
$dateTime = "20190408141835.999999-420"
[System.Management.ManagementDateTimeConverter]::ToDateTime($dateTime)
# convert WMI Interval to PowerShell Timespan:
$interval = "00000120125232:124150:000"
[System.Management.ManagementDateTimeConverter]::ToTimeSpan($interval)
# Convert PowerShell DateTime to WMI DateTime:
$today = Get-Date
[System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($today)
# Convert PowerShell Timespan to WMI Interval:
$timespan = New-TimeSpan -Hours 12 -Minutes 8
[System.Management.ManagementDateTimeConverter]::ToDmtfTimeInterval($timespan)
Char16
The equivalent PowerShell data type is [Char] ([System.Char]).
Examples
# converts value 35592 to SInt32:
$Char16 = [Char]35592
# check result type (System.Char):
$Char16.GetType().FullName
String
The equivalent PowerShell data type is [String] ([System.String]). Strings can store text of unlimited length. WMI typically limits the length of strings depending on the property where it is used.
Examples
# converts value 35592 to SInt32:
$String = [String]35592
# check result type (System.String):
$String.GetType().FullName
Arrays
When a data type is marked as Array, then the property can contain any number of values. In most scenarios, you need to process the values separately, or convert them to a string.
Examples
Dumping Array
The WMI class Win32_BIOS has a property BIOSCharacteristics of type UInt16 Array. It can contain any number of Uint16 values. Each value describes one BIOS characteristic.
You can dump all values separately:
# get instance
$bios = Get-CimInstance -Class Win32_BIOS
# dump content of property "BIOSCharacteristics"
$bios.BIOSCharacteristics
The result is a list of numbers.
If you want to know the meaning of these numbers, you need a translation table. Translation tables can be implemented as Hashtable or Enum, or you can use a switch statement. Follow the examples.
Converting Array to String
You can use the operator -join to convert array elements to one string. This is useful for reporting, especially when you plan to export the results to Excel.
# get instance
$bios = Get-CimInstance -Class Win32_BIOS
# convert array elements of property "BIOSCharacteristics" to string:
$delimiter = ", "
$text = $bios.BIOSCharacteristics -join $delimiter
The result is a single text string with all elements, separated by the delimiter you choose.
Maximum Length (String)
In PowerShell, strings have no length limit. In WMI, most properties with a string value do have a size limit.
For example, the property BuildNumber exposed by the WMI class Win32_BIOS is of type string with a maximum length of 64 characters.
For you, the length information is mostly of informal use because most WMI properties are read-only. Occasionally, properties do allow to be changed, though.
Changing String Values
When you assign a new value to a writeable property, you need to make sure the new value does not exceed the maximum length.
Example: Setting a new volume name
The WMI class Win32_LogicalDisk exposes the property VolumeName of type string with a maximum length of 32 characters. When you read the existing volume names, you will not find one that exceeds this limit:
# get all instances of "Win32_LogicalDisk"
# and list the "VolumeName" property:
Get-CimInstance -Class Win32_LogicalDisk |
Select-Object -Property Caption, VolumeName
Once you decide to change a volume name, the limit of 32 characters becomes an important information: WMI will reject any new name that exceeds the maximum length.
This assigns a new volume name to drive E:
# access the drive you want to rename:
$drive = Get-CimInstance -ClassName Win32_LogicalDisk -Filter 'Caption="E:"'
# assign the new name, and remember the maximum length of 32 characters:
$drive.VolumeName = 'MyUSBStick1'
# write back the changes. Only now will WMI check your changes for validity,
# and if the new name is valid, assign it to the drive(s) you selected:
$drive | Set-CimInstance
You can achieve the same with just one command, too:
# select the drive(s):
$wql = 'Select * From Win32_LogicalDisk Where Caption="E:"'
# define the property changes using a hashtable:
$changes = @{
VolumeName='MyNewLabel'
}
# send changes to WMI:
Set-CimInstance -Query $wql -Property $changes
Set-CimInstance
supports only WQL Queries to select the instances you want to change. -Property accepts a Hashtable so you can change as many (writeable) properties in one step as you wish.This efficiency is also a risk: if you do something wrong and for example select the wrong instances, you might end up changing too many or the wrong instances.
That’s why the multi-step approach in the first example may be safer: it provides you with extensive debugging opportunities, and you can check the selected instances before you change them.
The volume label of drive E: changes.
Keep in mind:
- You can change every single property on a WMI instance, even those that are read-only. Changes will not become effective until you send your changes back to WMI via
Set-CimInstance
. Only at this point will WMI check your changes for validity. - Any invalid change (exceeding the maximum string length, changing a read-only property, etc.) causes an exception only once you actually try to write back the change to WMI using
Set-CimInstance
.