ploc,Calling COM methods in C,Ploc.on Flickr – Photo Sharing!,www.ploc.co.uk,ploc on deviantART

09 January 2010

So Windows has this strange thing called COM. It allows you to share objects between unrelated processes and languages, a bit like CORBA in that sense, only very different. Anyway, if you'd like to get information out of this other Windows thing called WMI (which provides a lot of information you can't get hold of using native APIs, at least not if you don't digg into unpublished internals) then you've got to use COM.

But Microsoft seems to have decided some long time ago that C++ is such an amazing language (because if it's got ++ in the name it must be better then say just C, you know) that it solves all of the world's problems. So obviously that is the language you favour when designing APIs, meaning you end up with very convoluted APIs when using good old plain C. And obviously there is no need to document the way you do things in C because no one would use it. Anyway, my conclusion is that COM is crazy and I can only assume that .NET must somehow make this a bit easier (just like using WMI from Python is easy with Mark Hammond's pythoncom and Tim Golden's wmi module) which is probably the reason that C# is popular among developers who choose Windows as their platform.

Back to the point however: how do you call a method on a COM object using C? Microsoft will always show instance->Method() in their examples, but in C it's not that much different:
instance->lpVtbl->Method(instance)

There is also supposed to be a way to use some macro's using the class name of the object joined up with the method name. For this you need to define the COBJMACROS before your #includes and then you can call:
ClassName_Method(instance)

Only I can't get that way to work. No clue why.

For completeness here is the full example of the WMI local computer example in the MSDN library, ported to C. For extra fun the error handling is done by setting Python exceptions, but that shouldn't confuse things. It also prints the result string in both unicode and ascii, from the unicode string you could have easily made a PyUnicodeObject. There's probably many ugly things in there as I'm really not a win32 developer. It's also example code and not what I'd take into production (e.g. I think the looping over the results is broken but I'm sticking close the the MSDN example).

static int
example(void)
{
HRESULT hr;
IWbemLocator *pLoc;
IWbemServices *pSvc;
IEnumWbemClassObject *pEnumerator;
BSTR bstr, bstr2;

hr = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (hr == RPC_E_CHANGED_MODE)
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (hr != S_OK && hr != S_FALSE) {
PyErr_Format(PyExc_WindowsError,
"Failed to initialise COM (HRESULT=0x%x)", hr);
return -1;
}
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, EOAC_NONE, NULL);
if (FAILED(hr)) {
PyErr_Format(PyExc_WindowsError,
"Failed to initialise COM security (HRESULT=0x%x)", hr);
CoUninitialize();
return -1;
}
hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
&IID_IWbemLocator, (LPVOID *) &pLoc);

if (FAILED(hr)) {
PyErr_Format(PyExc_WindowsError,
"Failed to get IWBemLocator (HRESULT=0x%x)", hr);
CoUninitialize();
return -1;
}
bstr = SysAllocString(L"ROOT\\CIMV2");
if (bstr == NULL) {
PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
}
hr = pLoc->lpVtbl->ConnectServer(pLoc, bstr, NULL, NULL,
NULL, 0, NULL, NULL, &pSvc);
SysFreeString(bstr);
if (FAILED(hr)) {
PyErr_Format(PyExc_WindowsError,
"Localhost connection for WMI failed (HRESULT=0x%x)", hr);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return -1;
}
hr = CoSetProxyBlanket((IUnknown*)pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
NULL, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
if (FAILED(hr)) {
PyErr_Format(PyExc_WindowsError,
"Failed to set ProxyBlanket (HRESULT=0x%x)", hr);
pSvc->lpVtbl->Release(pSvc);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return -1;
}
bstr = SysAllocString(L"WQL");
if (bstr == NULL) {
PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
pSvc->lpVtbl->Release(pSvc);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return -1;
}
bstr2 = SysAllocString(L"SELECT * FROM Win32_OperatingSystem");
if (bstr2 == NULL) {
PyErr_SetString(PyExc_WindowsError, "BSTR allocation failed");
SysFreeString(bstr);
pSvc->lpVtbl->Release(pSvc);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return -1;
}
hr = pSvc->lpVtbl->ExecQuery(pSvc, bstr, bstr2,
WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY,
NULL, &pEnumerator);
SysFreeString(bstr);
SysFreeString(bstr2);
if (FAILED(hr)) {
PyErr_Format(PyExc_WindowsError, "WMI query failed (HRESULT=0x%x)", hr);
pSvc->lpVtbl->Release(pSvc);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return -1;
}
{
IWbemClassObject *pclsObj;
ULONG uReturn;
VARIANT vtProp;

while (pEnumerator) {
hr = pEnumerator->lpVtbl->Next(pEnumerator, WBEM_INFINITE,
1, &pclsObj, &uReturn);
if(uReturn == 0)
break;
hr = pclsObj->lpVtbl->Get(pclsObj, L"Name", 0, &vtProp, 0, 0);
wprintf(L"XXX OS Name: %s\n", vtProp.bstrVal);
{
/* XXX Need error checking in here */
/* Allocating the UTF-16 string size since that will be at
* least double the ASCII size, which is fine. */
char *prop;
int r;

prop = psi_malloc(SysStringByteLen(vtProp.bstrVal));
/* if (prop == NULL) */
r = WideCharToMultiByte(20127, 0, vtProp.bstrVal, -1, prop,
SysStringByteLen(vtProp.bstrVal),
NULL, NULL);
/* if (!r) */
printf("XXX OS Name: %s\n", prop);
}
VariantClear(&vtProp);
pclsObj->lpVtbl->Release(pclsObj);
}
}
pEnumerator->lpVtbl->Release(pEnumerator);
pSvc->lpVtbl->Release(pSvc);
pLoc->lpVtbl->Release(pLoc);
CoUninitialize();
return 0;
}

0 comments:

 
 
HTML Hit Counter