How to obtain data from WMI using a C Application?
Asked Answered
N

2

9

I have a pure C application that issues IOCTL calls to my adapter driver and displays info ,this is however compiled using Visual Developer Studio 5(non-managed code) ... I need to get some info however from my adapter using WMI .... My googling efforts show that i would need to write a C++ Application using COM to achieve any form of communication with wMI or a C# with .NET app a) Is that really the case? NO work around for my C application? b) If above is true,what are the minimum level changes that i would need to do my project/wp /workspace settings?

Thanks Som

Neologize answered 16/9, 2009 at 5:24 Comment(0)
S
15

You can invoke COM from C. The syntax is somewhat less friendly than that of C++, but it works. COM was initially designed to work from either C or C++, and native C language support is included in COM and WMI header files. It will be long though... your program will be responsible for allocating all the necessary objects, checking for error conditions from each and every COM call, and for releasing the objects it instantiated.

When using documentation written with C++ in mind, convert COM calls of the form:

pSomething->Method(arg1, ...); // C++

to:

pSomething->lpVtbl->Method(pSomething, arg1, ...); // C

Below is the shortest piece of C code I could get to actually pull some information from WMI. If successful, it should list the processors on your computer, along with their clock frequency in MHz. The program takes care of disposing resources it allocates, but it does no error checking whatsoever (you should look at those hr values before continuing each step).

This is a visual studio 2008 "Win32 Console Application" with the main file renamed to a .c extension, and the extra stdafx files removed. To get the program to link, make sure to include wbemuuid.lib in the project properties, under Configuration Properties/Linker/Input/Additional Dependencies. It ran successfully on my Vista box.

#define _WIN32_WINNT 0x0400
#define _WIN32_DCOM

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <wbemidl.h>

void _tmain(int argc, _TCHAR* argv[])
{
    // result code from COM calls
    HRESULT hr = 0;

    // COM interface pointers
    IWbemLocator         *locator  = NULL;
    IWbemServices        *services = NULL;
    IEnumWbemClassObject *results  = NULL;

    // BSTR strings we'll use (http://msdn.microsoft.com/en-us/library/ms221069.aspx)
    BSTR resource = SysAllocString(L"ROOT\\CIMV2");
    BSTR language = SysAllocString(L"WQL");
    BSTR query    = SysAllocString(L"SELECT * FROM Win32_Processor");

    // initialize COM
    hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

    // connect to WMI
    hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *) &locator);
    hr = locator->lpVtbl->ConnectServer(locator, resource, NULL, NULL, NULL, 0, NULL, NULL, &services);

    // issue a WMI query
    hr = services->lpVtbl->ExecQuery(services, language, query, WBEM_FLAG_BIDIRECTIONAL, NULL, &results);

    // list the query results
    if (results != NULL) {
        IWbemClassObject *result = NULL;
        ULONG returnedCount = 0;

        // enumerate the retrieved objects
        while((hr = results->lpVtbl->Next(results, WBEM_INFINITE, 1, &result, &returnedCount)) == S_OK) {
            VARIANT name;
            VARIANT speed;

            // obtain the desired properties of the next result and print them out
            hr = result->lpVtbl->Get(result, L"Name", 0, &name, 0, 0);
            hr = result->lpVtbl->Get(result, L"MaxClockSpeed", 0, &speed, 0, 0);
            wprintf(L"%s, %dMHz\r\n", name.bstrVal, speed.intVal);

            // release the current result object
            result->lpVtbl->Release(result);
        }
    }

    // release WMI COM interfaces
    results->lpVtbl->Release(results);
    services->lpVtbl->Release(services);
    locator->lpVtbl->Release(locator);

    // unwind everything else we've allocated
    CoUninitialize();

    SysFreeString(query);
    SysFreeString(language);
    SysFreeString(resource);
}
Steady answered 16/9, 2009 at 7:36 Comment(4)
Hi Oren, Thanks a ton, that worked like a champ on Win2008 .However , i ran into compilation issues when i first tried it on Visual Studio 2005 , putting them out below: error C2065: 'COINIT_MULTITHREADED' : undeclared identifier error C2065: 'EOAC_NONE' : undeclared identifier Any idea on how to resolve them for 2005? Thanks SomNeologize
Try adding #define _WIN32_WINNT 0x0400 and #define _WIN32_DCOM before the include files. If that doesn't solve the problem, just replace both missing constants with 0.Steady
Hi Oren, Yes it worked with the first pound define itself, once again thanks a lot SomNeologize
Good to know all the details, but maybe even better to know that there are options to forget about them :-)Seigneur
A
3

Another option, if you want to keep the impact to your existing C application low, is to write a DLL that internally can use C++ and COM wrapper classes to query the desired WMI information.

This DLL can provide a plain C interface to adapt to your application. Thats the way I would go for.

Afterword answered 16/9, 2009 at 8:13 Comment(1)
Absolutely, handling all the COM stuff in C not only a pain, but also on a completely different abstraction level compared to the actual application.Seigneur

© 2022 - 2024 — McMap. All rights reserved.