Hey, Scripting Guy! Question

Hey, Scripting Guy! Using the Get-Member cmdlet with Windows PowerShell is pretty cool, but the one thing I do not like is the way that Windows PowerShell hides stuff from me. When I look in MSDN for information about a WMI class, I see all of these methods listed. When I use the Get-Member  cmdlet, some of the methods are not listed. So it seems that the WMI implementation in Windows PowerShell is incomplete. If this is the case, how can I find out where this stuff is documented? I would like to see a list of all the things I can do with Windows PowerShell and WMI.

-- DF

Hey, Scripting Guy! AnswerHello DF,

You already have the documentation. It is called (drumroll please) MSDN. All of the information about our products can be found in the reference documentation on MSDN. You may also want to check out some of the various team blogs as well. There are two things you need to know about Windows Management Instrumentation (WMI). The first is that the WMI team does not write all the WMI classes. They develop the core technology, and the various teams at Microsoft create their own WMI providers and write their own WMI classes. When you go to the WMI documentation (the software development kit [SDK] on MSDN), you only find information about the WMI core classes. For example, if you want to find out about the WMI classes for Hyper-V, you will find the documentation under virtualizationnot under WMI.

But when you talk about hidden WMI class methods, you are not talking about hidden WMI documentation. You are talking about the way the information is supplied via the Get-Member cmdlet. Let's take a common scenario: the Win32_Process cmdlet. This important WMI class has been written about by Microsoft Scripting Guys numerous times, and therefore our loyal informed readers know the things this class can do. It is also documented on MSDN with the WMI core classes. As seen in Table 1, the Win32_Process WMI class exposes 6 methods.

Table 1 Methods of the Win32_Process class

Method

Description

AttachDebugger

Launches the currently registered debugger for a process.

Create

Creates a new process.

GetOwner

Retrieves the user name and domain name under which the process is running.

GetOwnerSid

Retrieves the security identifier (SID) for the owner of a process.

SetPriority

Changes the execution priority of a process.

Terminate

Terminates a process and all of its threads.

 

When you use the Get-Member cmdlet to examine the methods of the Win32_Process WMI class, you see only five methods that are returned. As seen here, the Create method is missing:

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 AttachDebug...
GetOwner       Method     System.Management.ManagementBaseObject GetOwner()
GetOwnerSid    Method     System.Management.ManagementBaseObject GetOwnerSid()
SetPriority    Method     System.Management.ManagementBaseObject SetPriority...
Terminate      Method     System.Management.ManagementBaseObject Terminate(S...

A clue can be obtained by using the Method Editor from the Windows Management Instrumentation Tester (WbemTest). For more information about using WbemTest, refer to yesterday’s Hey, Scripting Guy! article.

As seen in the following image, the Create method has the Static qualifier.

Image of the Create method with the Static qualifier


Armed with the fact that the Create method is a static method from the Win32_Process WMI class, you might be tempted to once again return to the Get-Member cmdlet, this time to retrieve static methods. To retrieve static methods, you use the MemberType parameter and choose methods and the static switch. This is seen here:

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


   TypeName: System.Management.ManagementObject

Name            MemberType Definition
----            ---------- ----------
Equals          Method     static bool Equals(System.Object objA, System.Obj...
ReferenceEquals Method     static bool ReferenceEquals(System.Object objA, S...

Unfortunately, the two static methods that are returned by the Get-Member cmdlet do not solve our problem. In fact the two static methods that are returned by Get-Member do not even appear in the MSDN documentation for the Win32_Process WMI class (this is because they are inherited from the System.Object .NET Framework class).

What have we discovered so far? We have seen that the WbemTest utility states that the Create method from the Win32_Process WMI class is a static method. We have also seen that the Get-Member cmdlet did not appear to find the Create method. The reason that the Get-Member cmdlet did not find the Create method is that the Get-WmiObject cmdlet returns instances of Win32_Processes. This is seen here:

PS C:\> Get-WmiObject -Class win32_process | Select-Object -Property name

name
----
System Idle Process
System
smss.exe
csrss.exe
wininit.exe
csrss.exe
winlogon.exe
services.exe
lsass.exe
lsm.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
svchost.exe
SLsvc.exe
svchost.exe
svchost.exe
svchost.exe
spoolsv.exe
svchost.exe
vmsrvc.exe
inetinfo.exe
svchost.exe
vpcmap.exe
taskeng.exe
SearchIndexer.exe
taskeng.exe
dwm.exe
explorer.exe
vmusrvc.exe
SnagIt32.exe
TscHelp.exe
SnagPriv.exe
SnagItEditor.exe
powershell.exe
WmiPrvSE.exe

Each of the objects that are returned by the Get-WmiObject cmdlet is an instance of a Win32_Process WMI class. It makes sense that an instance of a WMI class cannot be used to create an instance of a class. We need to move up one step in the WMI hierarchy. We need to move to the class itself, not to an instance of the class. This process is true, whether you are using the Get-WmiObject cmdlet from Windows PowerShell, or whether you are using the GetObject method from VBScript. To try to keep things in perspective, you can think of using the Get-WmiObject cmdlet in a similar fashion to the way you used the ExecQuery method from within VBScript. You can certainly use the ExecQuery method from VBScript to return a collection of processes that match the name "Notepad" and you can certainly call the terminate method to stop such a process. This process makes sense because you are working with an instance of the Win32_Process class. You want to terminate an instance of the class. Terminating a process named “Notepad” can be as easy as using a WMI query that retrieves a single instance of the class and calling the terminate method. If the query returns more than one instance of the Win32_Process class, you will need to use the Foreach-Object cmdlet to walk through the collection. This is seen here:

PS C:\> (Get-WmiObject -Class win32_process -Filter "name = 'notepad.exe'").Term
inate()


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

All this might make you wonder where the hidden WMI methods are. The key to finding the hidden WMI methods is understanding if the method is an instance method or if it is a static WMI method. If the method is an instance method, it is available directly from within using the Get-WmiObject, in the same way the methods were available from VBScript using the ExecQuery method. The method operates on an instance of the WMI class. If the method is a static WMI method, you will need to use the [wmiclass] type accelerator. This is similar to the way you had to use a different syntax in VBScript to call the class methods. In Windows PowerShell, you use the [wmiclass] type accelerator to retrieve the WMI class—not an instance of the class. To retrieve the class, you use [wmiclass] and the WMI class name. This is seen here:

PS C:\> [wmiclass]"Win32_process"


   NameSpace: ROOT\cimv2

Name                                Methods              Properties
----                                -------              ----------
Win32_Process                       {Create, Terminat... {Caption, CommandLi...

You can think of it as casting a string into a WMI class, or you can think of it as a type accelerator because it makes it easy to get access to the WMI class. After you have the WMI class, you can send it to Get-Member as seen here:

PS C:\> [wmiclass]"Win32_process" | Get-Member


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

Name                MemberType    Definition
----                ----------    ----------
Name                AliasProperty Name = __Class
Create              Method        System.Management.ManagementBaseObject Cre...
__CLASS             Property      System.String __CLASS {get;set;}
__DERIVATION        Property      System.String[] __DERIVATION {get;set;}
__DYNASTY           Property      System.String __DYNASTY {get;set;}
__GENUS             Property      System.Int32 __GENUS {get;set;}
__NAMESPACE         Property      System.String __NAMESPACE {get;set;}
__PATH              Property      System.String __PATH {get;set;}
__PROPERTY_COUNT    Property      System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH           Property      System.String __RELPATH {get;set;}
__SERVER            Property      System.String __SERVER {get;set;}
__SUPERCLASS        Property      System.String __SUPERCLASS {get;set;}
ConvertFromDateTime ScriptMethod  System.Object ConvertFromDateTime();
ConvertToDateTime   ScriptMethod  System.Object ConvertToDateTime();


The only WMI method that is seen is the Create method. The remaining properties are all system properties. There are two scriptMethods that were added by Windows PowerShell team to the object. To use the create method, you can call the method directly from the class 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        : 3616
ReturnValue      : 0

You can also store the WMI class in a variable and call the method from there. This is seen here:

PS C:\> $wmi = [wmiclass]"Win32_process"
PS C:\> $wmi.Create("notepad")


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

You can put this into a script, as seen in the New-WmiProcess.ps1 script.

New-WmiProcess.ps1

Function New-WmiProcess($name)
{
  $wmi = [wmiclass]"Win32_process"
  $wmi.Create($name)
} #end function New-WmiProcess

# *** Entry point to script ***
$name = "calc"
$rtn = New-WmiProcess -name $name
"A new instance of $name with ProcessID $($rtn.ProcessID) was created."

DF, I hope you can now understand that there are no "hidden" WMI methods, but rather different ways of accessing the methods. This is in reality the same as it was in the VBScript days and is because of the difference between instance methods and class methods.

If you want to be the first to know what is happening on the Script Center, follow us on Twitter or on Facebook. If you need assistance with a script, you can post questions to the Official Scripting Guys Forum, or send e-mail to scripter@microsoft.com. Join us tomorrow as we dive deep into the virtual mail bag and retrieve some short-answer questions for Quick-Hits Friday. Until tomorrow, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys