Idera nSoftware Compellent

Chapter 18. WMI: Windows Management Instrumentation

It might have escaped your attention, but the Windows Management Instrumentation (WMI) service introduced with Windows 2000 has been part of every Windows version since then. The WMI service is important because it can retrieve information about nearly every aspect of your system and can even make some modifications. However, it would be beyond the scope of this book to go into WMI in greater depth because that alone could fill another volume. For this reason, we will focus on how the WMI service basically works and how PowerShell handles it.

Topics Covered:

WMI Classes and Instances

WMI represents the insides of your computer in the form of classes. WMI provides classes for nearly everything: processor, BIOS, memory, user accounts, services, etc. The name of a class usually consists of the "Win32" prefix and the English-language name of what that class is meant to describe. For example, the Win32_Service describes services.

Instances of a Class

If you already know the name of a WMI class, Get-WmiObject will retrieve all instances of the class for you:

Get-WmiObject Win32_BIOS
SMBIOSBIOSVersion : RKYWSF21
Manufacturer : Phoenix Technologies LTD
Name : Phoenix TrustedCore(tm) NB Release SP1 1.0
SerialNumber : 701KIXB007922
Version : PTLTD - 6040000

If you can't remember the name of a WMI class, use the -list parameter:

Get-WmiObject -list
(...)
Win32_HeatPipe CIM_Refrigeration
Win32_Refrigeration CIM_Fan
Win32_Fan CIM_Printer
Win32_Printer CIM_Controller
CIM_ManagementController CIM_SCSIController
Win32_SCSIController CIM_InfraredController
Win32_InfraredDevice CIM_PCIController
(...)

You'll then get a list of all the WMI classes of the current namespaces. The list can be very long. The class names don't all begin with "Win32_". Classes that begin with an underline character are designated for internal purposes and would seldom be useful to you. Classes that begin with "CIM" are usually basic classes. Specialized classes derived from these begin with "Win32" and are more appropriate. So, if you're looking for a particular class, focus on class names that begin with "Win32". Here's a simple way to find all WMI classes that have anything to do with the subject of "printing":

Get-WmiObject -list | Select-String -InputObject { $_.Name } Win32_Print*
Win32_PrinterConfiguration
Win32_PrinterSetting
Win32_PrintJob
Win32_Printer
Win32_PrinterDriver
Win32_PrinterShare
Win32_PrinterDriverDll
Win32_PrinterController

Displaying All Properties

Often, only the most important properties of instances are displayed. You may remember the reason why from Chapter 5 and only if you want to display all properties. Specify one of the formatting cmdlets with an asterisk:

Get-WmiObject Win32_BIOS | Format-List *
Status : OK
Name : Phoenix TrustedCore(tm) NB Release SP1 1.0
Caption : Phoenix TrustedCore(tm) NB Release SP1 1.0
SMBIOSPresent : True
__GENUS : 2
__CLASS : Win32_BIOS
__SUPERCLASS : CIM_BIOSElement
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_BIOS.Name="Phoenix TrustedCore(tm) NB
Release SP1 1.0",SoftwareElementID="Phoenix
TrustedCore(tm) NB Release SP1 1.0",Software
ElementState=3,TargetOperatingSystem=0,Versi
on="PTLTD - 6040000"
__PROPERTY_COUNT : 27
__DERIVATION : {CIM_BIOSElement, CIM_SoftwareElement, CIM_L
ogicalElement, CIM_ManagedSystemElement}
__SERVER : JSMITH-PC
__NAMESPACE : root\cimv2
__PATH : \\JSMITH-PC\root\cimv2:Win32_BIOS.Name="Phoen
ix TrustedCore(tm) NB Release SP1 1.0",Softwa
reElementID="Phoenix TrustedCore(tm) NB Relea
se SP1 1.0",SoftwareElementState=3,TargetOper
atingSystem=0,Version="PTLTD - 6040000"
BiosCharacteristics : {4, 7, 8, 9...}
BIOSVersion : {PTLTD - 6040000, Phoenix TrustedCore(tm) NB
Release SP1 1.0, Ver 1.00PARTTBL}
BuildNumber :
CodeSet :
CurrentLanguage :
Description : Phoenix TrustedCore(tm) NB Release SP1 1.0
IdentificationCode :
InstallableLanguages :
InstallDate :
LanguageEdition :
ListOfLanguages :
Manufacturer : Phoenix Technologies LTD
OtherTargetOS :
PrimaryBIOS : True
ReleaseDate : 20061110000000.000000+000
SerialNumber : 701KIXB007922
SMBIOSBIOSVersion : RKYWSF21
SMBIOSMajorVersion : 2
SMBIOSMinorVersion : 4
SoftwareElementID : Phoenix TrustedCore(tm) NB Release SP1 1.0
SoftwareElementState : 3
TargetOperatingSystem : 0
Version : PTLTD - 6040000

Filtering Out PowerShell Properties

PowerShell binds to every WMI object a number of properties that begin with a double underline character but aren't actually part of the object. PowerShell uses these additional properties to manage WMI objects, something that will become very important a little later on in connection with the PowerShell Extended Type System. You can filter out any additional properties that distract you:

Get-WmiObject Win32_BIOS | Format-List [a-z]*

Now only those properties will be displayed that begin with a letter.

Selecting Particular Instances

It's seldom that you'll find a real use for all instances of a class. That's why you should use filters. The simplest (and slowest) filter is PowerShell itself. By using Where-Object, you can make sure that only those instances that have certain properties will be listed, such as all processes that have a specific name:

Get-WmiObject Win32_Process |
Where-Object { $_.Name -eq 'powershell.exe' }

It would be more efficient to pass this filter directly to WMI so that WMI will return only the instances you wanted right from the outset. To do so, use the -filter parameter. The filter that you specify with this parameter is not in PowerShell code but in the WMI query language (WQL), which in turn borrows a great deal from the SQL database query language.

Get-WmiObject Win32_Process -filter 'name = "powershell.exe"'

In addition, you can use the -query parameter if you would like to choose which properties of the instance should be returned by WMI. The next line returns the Caption and Commandline properties for all processes beginning with the letter "p":

Get-WmiObject -query `
'select caption,commandline from Win32_Process where name like "p%"'

The result is hard to understand because PowerShell supplements every WMI object with additional internal properties. For this reason, pass it to Format-Table and specify the properties that you want to make visible:

Get-WmiObject -query `
'select caption,commandline from Win32_Process where name like "p%"' |
Format-Table [a-z]* -wrap
Caption CommandL Scope Options ClassPa Propert SystemP Qualifi
ine th ies roperti ers
es
------- -------- ----- ------- ------- ------- ------- -------
PowerShe "C:\Prog System.M System.M Win32_P {Captio {__GENU {dynami
llPlus.e ram File anagemen anagemen rocess n, Comm S, __CL c, Loca
xe s\Idera\ t.Manage t.Object andLine ASS, __ le, pro
PowerShe mentScop GetOptio } SUPERCL vider,
llPlus\p e ns ASS, __ UUID}
owershel DYNASTY
lplus.ex ...}
e"

PowerShell supports the [WmiSearcher] type accelerator, which you can use to achieve basically the same thing you just did with the -query parameter:

$searcher = [WmiSearcher]"select caption,commandline from `
Win32_Process where name like 'p%'"
$searcher.Get() | Format-Table [a-z]* -wrap

Directly Accessing Instances

Every WMI instance has its own unique path. This path is important if you want to access a particular instance directly. The path of a WMI object is located in the __PATH property. First get a display of this property if you want to find out how the path of a specific object is structured:

Get-WmiObject Win32_Service | ForEach-Object { $_.__PATH }
\\JSMITH-PC\root\cimv2:Win32_Service.Name="AeLookupSvc"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="AgereModemAudio"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="ALG"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="Appinfo"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="AppMgmt"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="Ati External Event Utility"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="AudioEndpointBuilder"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="Audiosrv"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="Automatic LiveUpdate - Scheduler"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="BFE"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="BITS"
\\JSMITH-PC\root\cimv2:Win32_Service.Name="Browser"
(...)

The path consists basically of the class name as well as one or more key properties. For services, the key property is Name and is the English-language name of the service. If you want to work directly with a particular service through WMI, specify its path and do a type conversion. Use either the [wmi] type accelerator or the underlying [System.Management.ManagementObject] .NET type:

[wmi]"Win32_Service.Name='Fax'"
ExitCode : 1077
Name : Fax
ProcessId : 0
StartMode : Manual
State : Stopped
Status : OK

In fact, you don't necessarily need to specify the name of the key property as long as you at least specify its value. This way, you'll find all the properties of a specific WMI instance right away.

$disk = [wmi]'Win32_LogicalDisk="C:"'
$disk.FreeSpace
10181373952
[int]($disk.FreeSpace / 1MB)
9710
$disk | Format-List [a-z]*
Status :
Availability :
DeviceID : C:
StatusInfo :
Access : 0
BlockSize :
Caption : C:
Compressed : False
ConfigManagerErrorCode :
ConfigManagerUserConfig :
CreationClassName : Win32_LogicalDisk
Description : Local hard drive
DriveType : 3
ErrorCleared :
ErrorDescription :
ErrorMethodology :
FileSystem : NTFS
FreeSpace : 10181373952
InstallDate :
LastErrorCode :
MaximumComponentLength : 255
MediaType : 12
Name : C:
NumberOfBlocks :
PNPDeviceID :
PowerManagementCapabilities :
PowerManagementSupported :
ProviderName :
Purpose :
QuotasDisabled :
QuotasIncomplete :
QuotasRebuilding :
Size : 100944637952
SupportsDiskQuotas : False
SupportsFileBasedCompression : True
SystemCreationClassName : Win32_ComputerSystem
SystemName : JSMITH-PC
VolumeDirty :
VolumeName :
VolumeSerialNumber : AC039C05

Modifying Properties

Most of the properties that you find in WMI objects are read-only. There are few, though, that can be modified. For example, if you want to change the description of a drive, add new text to the VolumeName property of the drive:

$drive = [wmi]"Win32_logicaldisk='C:'"
$drive.VolumeName = "My Harddrive"
$drive.Put()
Path : \\.\root\cimv2:Win32_LogicalDisk.DeviceID="C:"
RelativePath : Win32_LogicalDisk.DeviceID="C:"
Server : .
NamespacePath : root\cimv2
ClassName : Win32_LogicalDisk
IsClass : False
IsInstance : True
IsSingleton : False

Three conditions must be met before you can modify a property:

  • The property must allow modifications in general. Most properties are read-only.
  • You require the proper permissions for modifications. The drive description applies to all users of a computer so only administrators may modify them.
  • You must use Put()to save the modification. Without Put(), the modification will not take effect.

Viewing Class Descriptions

Nearly every WMI class has a built-in description that explains its purpose. You can view this description only if you first set a hidden option called UseAmendedQualifiers to $true. Once that's done, the WMI class will readily supply information about its function:

$class = [wmiclass]'Win32_LogicalDisk'
$class.psbase.Options.UseAmendedQualifiers = $true
($class.psbase.qualifiers["description"]).Value
The Win32_LogicalDisk class represents a data source
that resolves to an actual local storage device on a
Win32 system. The class returns both local as well as
mapped logical disks. However, the recommended approach
is to use this class for obtaining information on local
disks and to use the Win32_MappedLogicalDisk for
information on mapped logical disk.

In a similarly thorough way, all the properties of the class are documented. Look it up if you want to know the intended aim of the VolumeDirty property:

$class = [wmiclass]'Win32_LogicalDisk'
$class.psbase.Options.UseAmendedQualifiers = $true
$voldirty = $class.psbase.properties["VolumeDirty"]
$voldirty.Type
Boolean
($voldirty.Qualifiers["Description"]).Value
The VolumeDirty property indicates whether the disk
requires chkdsk to be run at next boot up time. The
property is applicable to only those instances of
logical disk that represent a physical disk in the
machine. It is not applicable to mapped logical
drives.

Invoking WMI Methods

Basically, the same thing applies to WMI as it does to the .NET framework, and in Chapter 6 you learned that both classes and instances can provide methods, such as executable commands.

Instance-Based Methods

The instances of the Win32_Process class offer you, among other things, the Terminate() method with which you can force a process to stop running. To use Terminate(), you just need Win32_Process instances. Get-WmiObject can retrieve these for you. The next line ends all running Notepad instances. However, any unsaved work will be lost:

Get-WmiObject Win32_Process -filter "name='notepad.exe'" |
ForEach-Object { $_.Terminate() }

For every instance that Terminate() closes, it retrieves an object that is displayed in the console and that reports in ReturnValue whether the operation was carried out properly. If you want to verify its success, capture the object and inspect the ReturnValue property:

Get-WmiObject Win32_Process -filter "name='notepad.exe'" |
ForEach-Object {"Close all Notepads."; $good=0; $bad=0} {
$result=$_.Terminate();
if($result.ReturnValue -eq 0) {$good++} else {$bad++}
} { "Have closed $good instances. Problems arose in the `
case of $bad instances." }
Close all Notepads.
Have closed 2 instances. Problems arose in the case of 0 instances.

If you already know the process ID of a process, you can work on the process directly just as you did in the last section because the process ID is the key property of processes. For example, you could terminate the process with the ID 1234 like this:

([wmi]"Win32_Process='1234'").Terminate()

If you'd rather check your hard disk drive C:\ for errors, the proper invocation is:

([wmi]"Win32_LogicalDisk='C:'").Chkdsk(...

However, since this method requires additional arguments, the question here is what you should specify. Invoke the method without parentheses in order to get initial brief instructions:

([wmi]"Win32_LogicalDisk='C:'").Chkdsk
MemberType : Method
OverloadDefinitions : {System.Management.ManagementBaseObject
Chkdsk(System.Boolean FixErrors, System.
Boolean VigorousIndexCheck, System.Boole
an SkipFolderCycle, System.Boolean Force
Dismount, System.Boolean RecoverBadSecto
rs, System.Boolean OkToRunAtBootUp)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : System.Management.ManagementBaseObject
Chkdsk(System.Boolean FixErrors, System.
Boolean VigorousIndexCheck, System.Boole
an SkipFolderCycle, System.Boolean Force
Dismount, System.Boolean RecoverBadSecto
rs, System.Boolean OkToRunAtBootUp)
Name : Chkdsk
IsInstance : True

Listing Methods

Get-Member will tell you which methods a WMI object supports:

$object = Get-WmiObject Win32_Process |
Select-Object -first 1
$object | Get-Member -memberType Method
TypeName: System.Management.ManagementObject#
root\cimv2\Win32_Process

Name MemberType Definition
---- ---------- ----------
AttachDebugger Method System.Management.Management
BaseObject AttachDebugger()
GetOwner Method System.Management.Management
BaseObject GetOwner()
GetOwnerSid Method System.Management.Management
BaseObject GetOwnerSid()
SetPriority Method System.Management.Management
BaseObject SetPriority(System.
Int32 Priority)
Terminate Method System.Management.Management
BaseObject Terminate(System.
UInt32 Reason)

Static Methods

A WMI class directly supplies static methods, very much like static methods of a .NET class. If you want to renew the IP addresses of all network cards, use the Win32_NetworkAdapterConfiguration class and its static method RenewDHCPLeaseAll():

([wmiclass]"Win32_NetworkAdapterConfiguration").RenewDHCPLeaseAll()

You get the WMI class by using type conversion. You can either use the [wmiclass] type accelerator or the underlying [System.Management.ManagementClass] .NET type. Get-Member will again retrieve the methods of the class:

[wmiclass]"Win32_NetworkAdapterConfiguration" |
Get-Member -memberType Method
TypeName: System.Management.ManagementClass#
ROOT\cimv2\Win32_NetworkAdapterConfiguration

Name MemberType Definition
---- ---------- ----------
EnableDNS Method System.Management.Management
BaseObject EnableDNS(System.
String ...
EnableIPFilterSec Method System.Management.Management
BaseObject EnableIPFilterSec
(System...
EnableWINS Method System.Management.Management
BaseObject EnableWINS(System
.Boolea...
ReleaseDHCPLeaseAll Method System.Management.Management
BaseObject ReleaseDHCPLeaseA
ll()
RenewDHCPLeaseAll Method System.Management.Management
BaseObject RenewDHCPLeaseAll
()
SetArpAlwaysSourceRoute Method System.Management.Management
BaseObject SetArpAlwaysSourc
eRoute(...
SetArpUseEtherSNAP Method System.Management.Management
BaseObject SetArpUseEtherSNA
P(Syste...
SetDatabasePath Method System.Management.Management
BaseObject SetDatabasePath(S
ystem.S...
SetDeadGWDetect Method System.Management.Management
BaseObject SetDeadGWDetect(S
ystem.B...
SetDefaultTOS Method System.Management.Management
BaseObject SetDefaultTOS(Sys
tem.Byt...
SetDefaultTTL Method System.Management.Management
BaseObject SetDefaultTTL(Sys
tem.Byt...
SetDNSSuffixSearchOrder Method System.Management.Management
BaseObject SetDNSSuffixSearc
hOrder(...
SetForwardBufferMemory Method System.Management.Management
BaseObject SetForwardBufferM
emory(S...
SetIGMPLevel Method System.Management.Management
BaseObject SetIGMPLevel(Syst
em.Byte...
SetIPUseZeroBroadcast Method System.Management.Management
BaseObject SetIPUseZeroBroad
cast(Sy...
SetIPXVirtualNetworkNumber Method System.Management.Management
BaseObject SetIPXVirtualNetw
orkNumb...
SetKeepAliveInterval Method System.Management.Management
BaseObject SetKeepAliveInter
val(Sys...
SetKeepAliveTime Method System.Management.Management
BaseObject SetKeepAliveTime(
System....
SetMTU Method System.Management.Management
BaseObject SetMTU(System.UIn
t32 MTU)
SetNumForwardPackets Method System.Management.Management
BaseObject SetNumForwardPack
ets(Sys...
SetPMTUBHDetect Method System.Management.Management
BaseObject SetPMTUBHDetect(S
ystem.B...
SetPMTUDiscovery Method System.Management.Management
BaseObject SetPMTUDiscovery(
System....
SetTcpMaxConnectRetransmissions Method System.Management.Management
BaseObject SetTcpMaxConnectR
etransm...
SetTcpMaxDataRetransmissions Method System.Management.Management
BaseObject SetTcpMaxDataRetr
ansmiss...
SetTcpNumConnections Method System.Management.Management
BaseObject SetTcpNumConnecti
ons(Sys...
SetTcpUseRFC1122UrgentPointer Method System.Management.Management
BaseObject SetTcpUseRFC1122U
rgentPo...
SetTcpWindowSize Method System.Management.Management
BaseObject SetTcpWindowSize(
System....

Help with Classes and Methods

The methods of a WMI class are also documented in detail inside WMI. For example, you get the description of the Win32Shutdown() method of the Win32_OperatingSystem class like this:

$class = [wmiclass]'Win32_OperatingSystem'
$class.psbase.Options.UseAmendedQualifiers = $true
(($class.psbase.methods["Win32Shutdown"]).Qualifiers["Description"]).Value
The Win32Shutdown method provides the full set of shutdown
options supported by Win32 operating systems. The method returns
an integer value that can be interpretted as follows:
0 - Successful completion.
Other - For integer values other than those listed above, refer
to Win32 error code documentation.

Moreover, nearly all WMI classes have excellent documentation on the Internet. That's a good thing, too, because it's not easy to find out what the method wants from you, especially when WMI methods require arguments.

If you'd like to learn more about a WMI class or a method, navigate to an Internet search page like Google and specify as keyword the WMI class name, as well as the method. It's best to limit your search to the Microsoft MSDN pages: Win32_NetworkAdapterConfiguration RenewDHCPLeaseAll site:msdn2.microsoft.com.

Figure 18.1: WMI classes and their methods are documented in detail on the Internet

WMI Events

WMI returns not only information but can also wait for certain events. If the events occur, an action will be started. In the process, WMI can alert you when one of the following things involving a WMI instance happens:

  • __InstanceCreationEvent: A new instance was added such as a new process was started or a new file created.
  • __InstanceModificationEvent: The properties of an instance changed. For example, the FreeSpace property of a drive was modified.
  • __InstanceDeletionEvent: An instance was deleted, such as a program was shut down or a file deleted.
  • __InstanceOperationEvent: This is triggered in all three cases.

You can use these to set up an alarm signal. For example, if you want to be informed as soon as Notepad is started, type:

Select * from __InstanceCreationEvent WITHIN 1
WHERE targetinstance isa 'Win32_Process' AND
targetinstance.name = 'notepad.exe'

WITHIN specifies the time interval of the inspection and "WITHIN 1" means that you want to be informed no later than one second after the event occurs. The shorter you set the interval, the more effort involved, which means that WMI will require commensurately more computing power to perform your task. As long as the interval is kept at not less than one second, the computation effort will be scarcely perceptible. Here is an example:

$alarm = New-Object Management.EventQuery
$alarm.QueryString = "Select * from __InstanceCreationEvent `
WITHIN 1 WHERE targetinstance isa 'Win32_Process' AND `
targetinstance.name = 'notepad.exe'"
$watch = New-Object Management.ManagementEventWatcher $alarm

#Start Notepad after issuing a wait command:
$result = $watch.WaitForNextEvent()

#Get target instance of Notepad:
$result.targetinstance

#Access the live instance:
$path = $result.targetinstance.__path
$live = [wmi]$path

# Close Notepad using the live instance
$live.terminate()

Remote Access and Namespaces

Maybe you have the impression that WMI intersects with some cmdlets. That is in fact correct. Whether you use Get-WmiObject Win32_Process or better Get-Process to inspect running processes is often just a matter of taste. Even Get-WmiObject Win32_Service and Get-Service can return similar results—at least, at first glance.

WMI objects come from an entirely different source than the results of cmdlets, which is why they contain different, often additional information. Moreover, there are innumerable WMI classes for which cmdlets do not exist, because WMI is extensible and many third-party vendors create WMI extensions in additional namespaces. Finally, WMI works locally as well as remotely while most PowerShell cmdlets work only locally.

Accessing WMI Objects on another Computer

Using WMI makes remote access very easy and convenient provided that you have a network connection to the target system, sufficient permissions on that system, and no firewall is operating between you and the target system. Use the -ComputerName parameter of Get-WmiObject to access another computer system using WMI. Then specify the name of the computer after it:

Get-WmiObject -computername pc023 Win32_Process

If you want to log on to the target system using another user account, use the -Credential parameter to specify additional log on data as in this example:

$credential = Get-Credential
Get-WmiObject -computername pc023 -credential $credential Win32_Process

Namespaces: WMI Extensions

WMI has a hierarchical structure much like a file system does. Up to now, all the classes that you have used have come from the WMI "directory" root\cimv2. Third-party vendors can create additional WMI directories, known as Namespaces, and put in them their own classes, which you can use to control software, like Microsoft Office or hardware like switches and other equipment.

Because the topmost directory in WMI is always named root, from its location you can inspect existing namespaces. Get a display first of the namespaces on this level:

Get-WmiObject -Namespace root __Namespace | Format-Wide Name
subscription DEFAULT
MicrosoftDfs CIMV2
Cli nap
SECURITY RSOP
Infineon WMI
directory Policy
ServiceModel SecurityCenter
MSAPPS12 Microsoft
aspnet

As you can see, the cimv2 directory is only one of them. What other directories are shown here depends on the software and hardware that you use. For example, if you use Microsoft Office, you may find a directory called MSAPPS12. Take a look at the classes in it:

Get-WmiObject -Namespace root\msapps12 -list |
Where-Object { $_.Name.StartsWith("Win32_") }
Win32_PowerPoint12Tables Win32_Publisher12PageNumber
Win32_Publisher12Hyperlink Win32_PowerPointSummary
Win32_Word12Fonts Win32_PowerPointActivePresent...
Win32_OutlookDefaultFileLocation Win32_Word12Document
Win32_ExcelAddIns Win32_PowerPoint12Table
Win32_ADOCoreComponents Win32_Publisher12SelectedTable
Win32_Word12CharacterStyle Win32_Word12Styles
Win32_OutlookSummary Win32_Word12DefaultFileLocation
Win32_WordComAddins Win32_PowerPoint12AlternateSt...
Win32_OutlookComAddins Win32_ExcelCharts
Win32_Word12Settings Win32_FrontPageActiveWeb
Win32_OdbcDriver Win32_AccessProject
Win32_Word12StartupFileLocation Win32_ExcelActiveWorkbook
Win32_FrontPagePageProperty Win32_Publisher12MailMerge
Win32_Language Win32_FrontPageAddIns
Win32_Word12PageSetup Win32_Word12HeaderAndFooter
Win32_ServerExtension Win32_Publisher12ActiveDocume...
Win32_Word12Addin Win32_WordComAddin
Win32_PowerPoint12PageNumber Win32_JetCoreComponents
Win32_Publisher12Fonts Win32_Word12Table
Win32_OutlookAlternateStartupFile Win32_Word12Tables
Win32_Access12ComAddins Win32_Excel12AlternateStartup...
Win32_Word12FileConverters Win32_Access12StartupFolder
Win32_Word12ParagraphStyle Win32_Access12ComAddin
Win32_Excel12StartupFolder Win32_PowerPointPresentation
Win32_FrontPageWebProperty Win32_Publisher12Table
Win32_Publisher12StartupFolder Win32_WebConnectionErrorText
Win32_ExcelSheet Win32_Publisher12Tables
Win32_FrontPageTheme Win32_PowerPoint12ComAddins
Win32_Word12Template Win32_ExcelComAddins
Win32_Access12AlternateStartupFileLoc Win32_Word12ActiveDocument
Win32_PublisherSummary Win32_Publisher12DefaultFileL...
Win32_Word12Field Win32_Publisher12Hyperlinks
Win32_PowerPoint12ComAddin Win32_PowerPoint12Hyperlink
Win32_PowerPoint12DefaultFileLoc Win32_Publisher12Sections
Win32_OutlookStartupFolder Win32_Access12JetComponents
Win32_Word12ActiveDocumentNotable Win32_Publisher12CharacterStyle
Win32_Word12Hyperlinks Win32_Word12MailMerge
Win32_Word12FileConverter Win32_PowerPoint12Hyperlinks
Win32_FrontPageActivePage Win32_Word12Summary
Win32_OleDbProvider Win32_Publisher12PageSetup
Win32_Word12SelectedTable Win32_PowerPoint12StartupFolder
Win32_OdbcCoreComponent Win32_PowerPoint12PageSetup
Win32_FrontPageSummary Win32_AccessSummary
Win32_Word12Hyperlink Win32_OfficeWatsonLog
Win32_Publisher12Font Win32_WebConnectionErrorMessage
Win32_AccessDatabase Win32_Publisher12Styles
Win32_Publisher12ActiveDocument Win32_Word12AlternateStartupF...
Win32_PowerPoint12Fonts Win32_Word12Sections
Win32_ExcelComAddin Win32_Excel12DefaultFileLoc
Win32_Word12Fields Win32_ExcelActiveWorkbookNotable
Win32_Publisher12COMAddIn Win32_ExcelWorkbook
Win32_OutlookComAddin Win32_PowerPoint12Font
Win32_FrontPageAddIn Win32_ExcelChart
Win32_WebConnectionError Win32_Word12Font
Win32_RDOCoreComponents Win32_Word12PageNumber
Win32_Publisher12ParagraphStyle Win32_Publisher12COMAddIns
Win32_Transport Win32_Access12DefaultFileLoc
Win32_FrontPageThemes Win32_ExcelSummary
Win32_ExcelAddIn Win32_Publisher12AlternateSta...
Win32_PowerPoint12SelectedTable

WMI and the Extended Type System

In Chapters 5 and 6, you learned a few things about the PowerShell Extended Type System. With its help, you can give objects new properties and methods. That is very useful in the case of WMI. The Extended Type System can do more: it can be used to add on type converters. You'll see later why that's useful.

Converting the WMI Date Format

WMI includes some untypical date formats. Particularly, the date and time format looks very weird. For example, look at the example of the Win32_OperatingSystem class:

Get-WmiObject win32_Operatingsystem | Format-List *time*
CurrentTimeZone : 120
LastBootUpTime : 20071016085609.375199+120
LocalDateTime : 20071016153922.498000+120

The date and time are given as a sequence of numbers, first the year, then the month, and finally the day. Following this is the time in hours, minutes, and milliseconds, and then the time zone. This is the so-called DMTF, which is hard to read. However, you can use ToDateTime() of the ManagementDateTimeConverter.NET class to decipher this cryptic format:

$boottime = (Get-WmiObject win32_Operatingsystem).LastBootUpTime
$boottime
20071016085609.375199+120
$realtime = [System.Management.ManagementDateTimeConverter]::`
ToDateTime($boottime)
$realtime
Tuesday, October 16, 2007 8:56:09 AM

You only need to take one look to see what the date and the time are now. Moreover, you can also continue to work with the time indicator in various ways, such as using New-TimeSpan to calculate current system uptime:

New-TimeSpan $realtime (get-date)
Days : 0
Hours : 6
Minutes : 47
Seconds : 9
Milliseconds : 762
Ticks : 244297628189
TotalDays : 0.282751884478009
TotalHours : 6.78604522747222
TotalMinutes : 407.162713648333
TotalSeconds : 24429.7628189
TotalMilliseconds : 24429762.8189

Adding On a Type Converter

It would be much more logical if you could convert the WMI date format by type conversion into a DateTime format, such as like this:

[datetime]"20071016085609.375199+120"
Cannot convert value "20071016085609.375199+120" to
type "System.DateTime". Error: "String was not recognized
as a valid DateTime."
At line:1 char:11
+ [datetime]" <<<< 20071016085609.375199+120"

This fails because the standard type converter is not the right tool for this conversion. But the Extended Type System can add on not only properties and methods, but also type converters. To see how, make a type converter first:

notepad typeconverter.cs

Notepad offers to create a new file. Agree, and then type this code:

using System.Management.Automation;
using System;
using System.IO;
using System.Management;
namespace WMItoDate
{
public class DateTimeTypeConverter : PSTypeConverter
{
public override bool CanConvertFrom(Object sourceValue, Type destinationType)
{
string src = sourceValue as string;
if (src != null)
{
try
{
DateTime Date = ManagementDateTimeConverter.ToDateTime(src);
if (Date != null) return true;
}
catch (Exception)
{
return false;
}
}
return false;
}

public override object ConvertFrom(object sourceValue, Type destinationType,
IFormatProvider provider, bool IgnoreCase)
{
if (sourceValue == null) throw new InvalidCastException("Conversion error");
if (this.CanConvertFrom(sourceValue, destinationType))
{
try
{
string src = sourceValue as string;
DateTime Date = ManagementDateTimeConverter.ToDateTime(src);
return Date;
}
catch (Exception)
{
throw new InvalidCastException("Conversion error");
}
}
throw new InvalidCastException("Conversion error");
}

public override bool CanConvertTo(object Value, Type destinationType)
{
return false;
}

public override object ConvertTo(object Value, Type destinationType,
IFormatProvider provider, bool IgnoreCase)
{
throw new InvalidCastException("Conversion error");
}
}
}

Save the code as the code has to be compiled in a DLL library. PowerShell can take care of this chore for you:

$compiler = "$env:windir/Microsoft.NET/Framework/v2.0.50727/csc"
$ref = [PsObject].Assembly.Location
&$compiler /target:library /reference:$ref typeconverter.cs

The result is the file typeconverter.dll. Now all you have to do is load it in PowerShell:

$path = resolve-path .\typeconverter.dll
[void][System.Reflection.Assembly]::LoadFrom($path)

The Extended Type System must be informed that a new type converter is available. To do so, create a ps1xml file that basically contains XML data:

notepad typeconverter.wmi.ps1xml

Agree to creation of the file, and type this code:

<Types>
<Type>
<Name>System.DateTime</Name>
<TypeConverter>
<TypeName>WMItoDate.DateTimeTypeConverter</TypeName>
</TypeConverter>
</Type>
</Types>

Save the file and import the extension into the Extended Type System:

Update-TypeData typeconverter.wmi.ps1xml

Finally, try out the extension. From now on, PowerShell can use type conversion to convert WMI dates into DateTime specifications:

[datetime]"20071016085609.375199+120"
Tuesday, October 16, 2007 8:56:09 AM

So that the extension automatically remains in operation, you have to load your DLL library into one of your PowerShell start profiles. You also have to load the type extension into the profile using Update-TypeData.


Posted Apr 10 2009, 08:01 AM by ps1

Comments

Chapter 18. WMI: Windows Management Instrumentation - Master-PowerShell | With Dr. Tobias Weltner - PowerShell.com wrote Chapter 18. WMI: Windows Management Instrumentation - Master-PowerShell | With Dr. Tobias Weltner - PowerShell.com
on 04-10-2009 5:45 PM

Pingback from  Chapter 18. WMI: Windows Management Instrumentation - Master-PowerShell | With Dr. Tobias Weltner - PowerShell.com

Copyright 2010 PowerShell.com. All rights reserved.