Hey, Scripting Guy! Can I Call WMI Methods More Easily with Windows PowerShell 2.0?

Hey, Scripting Guy! Can I Call WMI Methods More Easily with Windows PowerShell 2.0?

  • Comments 4
  • Likes

 Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I enjoy working with WMI, but it can be a bit complicated to make it do anything. It is great at retrieving information, and in conjunction with the Windows PowerShell Get-WMIObject cmdlet, it is really easy to obtain lots of information about our servers. In fact, WMI is one of the reasons we are deploying Windows PowerShell 2.0 to all of our 1,000 servers in our server farm. We feel that we will be able to get the ROI from the deployment project in a few months. I just wish it was easier to call the methods.

-- JB

 

Hey, Scripting Guy! AnswerHello JB,

Microsoft Scripting Guy Ed Wilson here. There was frost on my front lawn this morning! Frost is about as close as we get to snow in sunny Charlotte, North Carolina, in the United States. In honor of nearly having a “snow day,” I decided to make one of my favorite morning beverages. I brewed a small pot of espresso using coffee beans from Chanchamayo that I brought back from Peru. The coffee from high up in the Andes Mountains is especially rich and makes a fine espresso. I then heat milk in my double boiler, and as the temperature begins to rise, I take a bar of 85 percent cocoa chocolate and grate it into the milk. As I continue to stir the chocolate milk in the double boiler, I add two tablespoons of pure vanilla extract that I brought back with me from Cozumel, Mexico. While I am doing this, I fill a heavy stone coffee mug with water, and place it in the microwave and heat the water to warm my cup. I then pour the espresso into the heated stone coffee mug, and pour in my hot chocolate. I then grate a quarter of a stick of cinnamon and a little nutmeg to dust the top. It is a great way to start the morning. If you happen to have a slice of fresh baked banana nut bread to go with it, it is even better.

JB, with a frosty crisp December morning, a cup of my special mocha, a slice of banana nut bread, and a question about WMI, what more could I ask for? It is going to be a festive day. I will play some Elvis music on my Zune HD. Maybe I will even take the evening off and watch Blue Hawaii tonight. And…maybe not.

The great thing about using Windows PowerShell 2.0 to work with WMI methods is that it includes the Invoke-WMIMethod cmdlet. The Invoke-WMIMethod cmdlet makes it easier to call WMI methods, but it still requires an understanding of how WMI works. WMI has two types of methods: static methods, and instance methods. A static method is one that is always available, and an instance method is one that only works when an instance of the class is available—and it works on that instance. To illustrate the advantage of using the Invoke-WMIMethod cmdlet over the previous methods of working with WMI in Windows PowerShell 1.0, let us look at the old way and the new way of doing things. Keep in mind that both methodologies exist in Windows PowerShell 2.0, and it is perfectly legitimate to use the old style syntax. In certain cases, the old style syntax is actually easier to use and easier to understand. But we will get to that in a minute.

To create a new instance of a Win32_Process WMI class, you use the create method. The problem is that the create method is a static method. This fact is not mentioned on MSDN and is only discoverable by examining the qualifiers of the method in WbemTest as seen here:

Image of qualifier in WbemTest


It should not come as a surprise that the create method is static—if you think about it for a minute—to create a new process, you are not working with an individual process—you are working with the Win32_Process class. To delete a process however, you are working with an individual process—and the terminate method is an instance method. Understanding this difference helps to explain the two ways of working with WMI methods. Instance methods can be discovered by piping the results of the Get-WmiObject cmdlet to the Get-Member cmdlet. This is shown here:

PS C:\> Get-WmiObject -Class win32_process | Get-Member -MemberType method


   TypeName: System.Management.ManagementObject#root\cimv2\Win32_Process

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


PS C:\>


The static methods are found by using the [wmiclass] type accelerator. This is seen here:

PS C:\> [wmiclass]"Win32_Process" | Get-Member -MemberType method


   TypeName: System.Management.ManagementClass#ROOT\cimv2\Win32_Process

Name   MemberType Definition
----   ---------- ----------
Create Method     System.Management.ManagementBaseObject Create(System.String CommandLine, System.String CurrentDire...


PS C:\>


The difference between the Get-WMIObject cmdlet and the [wmiclass] type accelerator is that the Get-WMIObject cmdlet returns instances of the class, and the [wmiclass] type accelerator returns the class itself.

JB, let us put this together. To create a new process in Windows PowerShell 1.0, you use the the [wmiclass] type accelerator as seen here:

PS C:\> ([wmiclass]"Win32_Process").Create("notepad")


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 2
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ProcessId        : 4924
ReturnValue      : 0



PS C:\>

The syntax is rather terse, but is a bit confusing. In Windows PowerShell 2.0, if you want to create a new process, you can use the Invoke-WMIMethod cmdlet, which though a bit wordy is much easier to understand:

PS C:\> Invoke-WmiMethod -Class win32_process -Name create -ArgumentList "notepad"


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 2
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ProcessId        : 4592
ReturnValue      : 0



PS C:\>

In Windows PowerShell 1.0, if you want to terminate a process, you could use the Win32_Process WMI class and the Get-WMIObject cmdlet. Because Terminate is not a static method, it is available only on an instance of the class. To retrieve specific instances of the Win32_Process WMI class, you query for it by using the Get-WmiObject and a filter. The –filter parameter is used to limit the results to only processes named notepad.exe. Because multiple instances of the Win32_Process class were returned and an instance method only works on one instance, it is necessary to pipe the results to the ForEach-Object cmdlet. The $_ automatic variable is used to refer to a single instance of the class at a time as it comes across the Windows PowerShell pipeline, and therefore the Terminate method can be called. This is shown here:

PS C:\> Get-WmiObject -Class win32_Process -Filter "name = 'notepad.exe'" | ForEach-Object { $_.Terminate() }


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0

__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0

__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



PS C:\>

Using the Invoke-WMIMethod cmdlet to call instance methods is a bit more work. This is because the –path parameter must point to a specific instance of the class. Luckily, the path property is always available because it is a system property. In WMI all system properties are preceded by a double underscore, so they will filter to the top of a list of properties and can be easily discarded in search results. Note that the __RELPATH property refers to the WMI class name and a key property value on the local machine. The key property is always used to uniquely refer to instances of a WMI class. For the Win32_Process WMI class, the key property is Handle. The __PATH property refers to a specific instance of the WMI class on a specific computer. The full path uses machine name, WMI namespace, WMI class, and the key. The system properties for one instance of the Win32_Process are shown here:

__GENUS                    : 2
__CLASS                    : Win32_Process
__SUPERCLASS               : CIM_Process
__DYNASTY                  : CIM_ManagedSystemElement
__RELPATH                  : Win32_Process.Handle="2208"
__PROPERTY_COUNT           : 45
__DERIVATION               : {CIM_Process, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER                   : MRED1
__NAMESPACE                : root\cimv2
__PATH                     : \\MRED1\root\cimv2:Win32_Process.Handle="2208"

Because typing the path value can be a bit cumbersome, you can pick it up from the pipeline by using the $_ automatic variable and choosing the __Path property. This is shown here:

PS C:\> Get-WmiObject -Class win32_process -Filter "name = 'notepad.exe'" |
ForEach-Object { Invoke-WmiMethod -Path $_.__Path –Name Terminate }


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0

__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0

__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0

If you only have one instance of the WMI class you would like to delete, you can type the path to it directly. Because I am working on a local machine, I do not need to type the full __PATH value. I can use the __RELPATH value instead. The other thing that is significant is the –Path parameter of the Invoke-WMIMethod expects a WMI path for the value, and therefore quotation marks are not needed around the path. This is shown here:

PS C:\> Invoke-WmiMethod -Path win32_Process.Handle="3944" -name terminate


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



PS C:\>

In fact, you do not need to set off the numbers of the handle with quotation marks either if you do not wish to do so. Leaving out all quotation marks is illustrated here. This is really helpful because one of the things that has always been confusing to scripters is the escaping of quotation marks inside other quotation marks. With the Invoke-WMIMethod cmdlet, these types of issues are avoided altogether:

PS C:\> Invoke-WmiMethod -Path win32_Process.Handle=3252 -name terminate


__GENUS          : 2
__CLASS          : __PARAMETERS
__SUPERCLASS     :
__DYNASTY        : __PARAMETERS
__RELPATH        :
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         :
__NAMESPACE      :
__PATH           :
ReturnValue      : 0



PS C:\>

 

JB, this should get you started using WMI methods. This also concludes WMI Week. Join us tomorrow when we dig into our virtual mailbag for Quick-Hits Friday.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Nice.. this answers some of my questions..

    thanks..

  • What if the process requires arguments? I.e. "notepad.exe c:\users\public\text.txt"

  • I modified the code to keep trying, I think it's correct - will test more tomorrow.

    Do
    {
    # Get Heartbeat and run Hardware Inventory
    Write-host "Waiting for client to initialize and run HW inventory"
    Start-Sleep -s 10
    $SMSCli = [WmiClass]"\\localhost\ROOT\ccm:SMS_Client"
    $SMSCli.triggerschedule("{00000000-0000-0000-0000-000000000003}")
    } while (![boolean] ($([WmiClass]"\\localhost\ROOT\ccm:SMS_Client").triggerschedule("{00000000-0000-0000-0000-000000000003}")))

  • Sorry, wrong post