Calling Instance Methods in WMI

It’s pretty rare that I wind up using WMI from a C++ application, but when I’ve done so in the past, it’s been a straightforward process. The documentation in MSDN is generally adequate, and their example projects tend to lead me in the right direction. However, I was helping a friend out with a side project and I needed to call an instance method of a WMI object. It turns out that MSDN’s documentation on the subject is sorely lacking, and their example product is misleading. They focus only on the easiest possible case — how to call a static method on an object.

It turns out that calling an instance method isn’t overly difficult. You just need to know the proper magic incantation to get it to work.

Assuming that you’ve gotten an IWbemClassObject from some form of query, you can ask for the __PATH variable from it. This is the key ingredient the documentation fails to discuss: the __PATH variable is WMI’s version of a “this” pointer. It uniquely identifies a computer, session, object class and object instance. Once you’ve gotten the __PATH variable, you can use that in a call to ExecMethod to execute a method on a specific instance. For example:

// Get a reference to the Win32_Printer class so we can find 
// the RenamePrinter method.  This lets us create an object 
// representing the input parameter block to be passed to the 
// method when we call it.
IWbemClassObject *printer = NULL;
IWbemClassObject *params = NULL;
IWbemClassObject *paramsInst = NULL;

services->GetObject( _bstr_t( L"Win32_Printer" ), 0, NULL, 
                   &printer, NULL );
printer->GetMethod( _bstr_t( "RenamePrinter" ), 0, &params, 
                   NULL );
params->SpawnInstance( 0, &paramsInst );

// Now that we've got an instance representing the input 
// parameters, we can fill in the parameter values
_bstr_t paramValue( L"New Printer Name Goes Here" );
VARIANT paramVt;
paramVt.vt = VT_BSTR;
paramVt.bstrVal = paramValue;
paramsInst->Put( L"NewPrinterName", 0, &paramVt, NULL );

// Get the "this" pointer to our object instance so that we 
// can call the RenamePrinter method on it
LONG flavor;
objInstance->Get( L"__PATH", 0, &var, &type, &flavor );

// Execute the RenamePrinter method on our object instance
IWbemClassObject *results = NULL;
services->ExecMethod( var.bstrVal, _bstr_t( L"RenamePrinter" ), 0, 
                  NULL, paramsInst, &results, NULL );

(Obviously, I’ve elided the error checking and cleanup code — you’ll want to add that in to any production code you write.)

Once you’ve seen the solution, it makes sense. However, there was no indication on MSDN that the __PATH variable was what should be passed in as the first parameter to ExecMethod to represent the object. Hopefully this blog posting saves you some time!

tl;dr: the “this” pointer for an object in WMI is represented by the __PATH variable.

This entry was posted in Win32 and tagged . Bookmark the permalink.

3 Responses to Calling Instance Methods in WMI

  1. Jean-Pierre RIBEAUVILLE says:

    I’m trying to invoke Activate() method for a PowerPlan instance .
    Code runs without crashing ; but the PowerPlan scheme we want to activate never went active .
    Could you help me ?
    Thanks in advance.

    Here is an extract of my C code :

    BSTR MethodName = SysAllocString(L”Activate”);
    BSTR RelPath = SysAllocString(L”Win32_PowerPlan”);
    IWbemClassObject *pOut = NULL;
    VARIANT var;
    IWbemClassObject *wmi_class = NULL;

    /* initialize WMI ROOT\\CIMV2\Power collection */
    languageCIMV2Power = SysAllocString(QUERY_Language); resourceCIMV2Power = SysAllocString(L”\\\\.\\ROOT\\CIMV2\\Power”);
    hres = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) locatorCIMV2Power);
    hres = locatorCIMV2->lpVtbl->ConnectServer(locatorCIMV2Power, resourceCIMV2Power, NULL, NULL, NULL, 0, NULL, NULL, &servicesCIMV2Power);

    queryCIMV2Power = SysAllocString(L”SELECT * FROM Win32_PowerPlan”);

    hres = servicesCIMV2Power->lpVtbl->ExecQuery(servicesCIMV2Power, languageCIMV2Power, queryCIMV2Power, WBEM_FLAG_FORWARD_ONLY, NULL, &ResultsCIMV2Power);
    if (hres == S_OK)
    /* enumerate the retrieved objects */
    while((hres = ResultsCIMV2Power->lpVtbl->Next(ResultsCIMV2Power, WBEM_INFINITE, 1, &IWbemResultCIMV2Power, &returnedCountCIMV2Power)) == S_OK)
    hres = servicesCIMV2Power->lpVtbl->GetObjectA(servicesCIMV2Power,L”Win32_PowerPlan”,0,NULL,&wmi_class,NULL);
    hres= wmi_class->lpVtbl->Get(wmi_class,L”__PATH”,0,&var,NULL,NULL);

    /* Call the method to activate this PowerPlan Instance */
    hres =servicesCIMV2Power->lpVtbl->ExecMethod(servicesCIMV2Power, var.bstrVal, MethodName, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, IWbemResultCIMV2Power,&pOut, NULL);

  2. Aaron Ballman says:

    What HRESULT are you getting from calling Activate?

  3. lewis deng says:

    user __RELPATH instead __PATH

Leave a Reply

Your email address will not be published. Required fields are marked *