Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Tutorial - Adding WMI Query support to your engine (C++)

| Inside3d Forums Forum Index -> Programming Tutorials

View previous topic :: View next topic
Author | Message
--- | ---
mh Joined: 12 Jan 2008 Posts: 909 |

Posted: Thu Nov 27, 2008 9:31 pm Post subject: Tutorial - Adding WMI Query support to your engine (C++) |

Here we're going to add support for WMI queries to our engine. Advance warning - it's C++ (so you'll need to write a wrapper in C) and it's Windows only, but at the same time, if you're happy to live with that, you can do a lot of good useful stuff with WMI, such as getting the amount of video RAM you have (useful if you want to scale down those huge textures to something more manageable at load time!), finding your CPU speed, physical memory, disk space, basically just about any hardware or software related parameter you can think of.
This code is based off a DirectX SDK sample (getting video RAM), but was rewritten by myself rather than just being a copy and paste job. While doing that, I reworked it to be a little bit more generally useful.
It's not an all-purpose WMI query engine, but is eminently suitable for simple queries that return single values.
So let's dive in.
First thing I did was declare these useful macros in quakedef.h, as a lot of WMI storage info is returned in bytes:

Code:
#define BYTES_TO_MEGS(b) ((b) / 1048576) #define K_TO_MEGS(b) ((b) / 1024) #define BYTES_TO_K(b) ((b) / 1024)

Next thing, still in quakedef.h, is to add a #include for sys_wmi.h - just plonk it below the windows.h #include. We're going to get this file next, which is our class definition:

Code:
#include <wbemidl.h> class WMIQuery { public: WMIQuery (void);
VARIANT ExecQuery (const BSTR WMIClassName, const BSTR WMIPropName);
~WMIQuery (void);
private:
IWbemLocator *pIWbemLocator;
IWbemServices *pIWbemServices;
HRESULT hrCoInitialize;
BSTR pNameSpace;
};

Now we're going to get the sys_wmi.cpp file; I've sprinkled commen ts through this fairly liberally, so I'm not going to say anything on what it does or how it does it here:

Code:
#include "quakedef.h" #pragma comment (lib, "wbemuuid.lib") typedef BOOL (WINAPI *PfnCoSetProxyBlanket) ( IUnknown *pProxy,
DWORD dwAuthnSvc,
DWORD dwAuthzSvc,
OLECHAR *pServerPrincName,
DWORD dwAuthnLevel,
DWORD dwImpLevel,
RPC_AUTH_IDENTITY_HANDLE pAuthInfo,
DWORD dwCapabilities
);
WMIQuery::WMIQuery (void)
{
// safe defaults
this->pIWbemLocator = NULL;
this->pIWbemServices = NULL;
// result handle for setup
HRESULT hr;
// attempt to initialize COM - if this fails, we assume that COM has already been initialized
// elsewhere rather than that anything dramatic or bad happened - it's 2008 and we don't
// really expect something as basic as COM initialization to fail any more...
this->hrCoInitialize = CoInitialize (NULL);
// connect to default namespace on local computer
this->pNameSpace = SysAllocString (L"\\\\.\\root\\cimv2");
// create a locator instance
hr = CoCreateInstance (CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &this->pIWbemLocator);
// WMI not working or something else bad happened
if (FAILED (hr) || !this->pIWbemLocator) return;
// connect to the COM server
hr = pIWbemLocator->ConnectServer (this->pNameSpace, NULL, NULL, 0L, 0L, NULL, NULL, &this->pIWbemServices);
// WMI not working or something else bad happened
if (FAILED (hr) || !this->pIWbemServices) return;
// load ole32.dll - calling CoSetProxyBlanket is broken in the API, as is creating an IID_IClientSecurity thingie, so instead
// we load the dll, GetProcAddress on it, and do it this way - ugleeeeeee!
HINSTANCE hInstOle32 = LoadLibrary ("ole32.dll");
if (!hInstOle32) return;
// set up the client security proxy now
PfnCoSetProxyBlanket pfnCoSetProxyBlanket = (PfnCoSetProxyBlanket) GetProcAddress (hInstOle32, "CoSetProxyBlanket");
if (pfnCoSetProxyBlanket)
{
pfnCoSetProxyBlanket
(
this->pIWbemServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
0
);
}
FreeLibrary (hInstOle32);
}
VARIANT WMIQuery::ExecQuery (const BSTR WMIClassName, const BSTR WMIPropName)
{
VARIANT var;
var.intVal = -1;
// something went wrong...
if (!this->pNameSpace) return var;
if (!this->pIWbemLocator) return var;
if (!this->pIWbemServices) return var;
// initial setup
IEnumWbemClassObject *pIEnumWbemClassObject = NULL;
// create an enumerator for this class instance
HRESULT hr = this->pIWbemServices->CreateInstanceEnum (WMIClassName, 0, NULL, &pIEnumWbemClassObject);
// make sure it worked
if (SUCCEEDED (hr) && pIEnumWbemClassObject)
{
// alloc space for 10 object pointers
IWbemClassObject *pIWbemClassObject[10] = {NULL};
DWORD uReturned = 0;
// get the first 10 (or less if there are less) instances of this class object
pIEnumWbemClassObject->Reset ();
hr = pIEnumWbemClassObject->Next (5000, 10, pIWbemClassObject, &uReturned);
// did we get them?
if (SUCCEEDED (hr))
{
// yes
for (UINT uClassObj = 0; uClassObj < uReturned; uClassObj++)
{
// clear down the variant
VariantClear (&var);
// get the memory and release the object
hr = pIWbemClassObject[uClassObj]->Get (WMIPropName, 0L, &var, NULL, NULL);
pIWbemClassObject[uClassObj]->Release ();
}
}
// clean up
pIEnumWbemClassObject->Release ();
}
return var;
}
WMIQuery::~WMIQuery (void)
{
// release COM objects
if (this->pIWbemServices) this->pIWbemServices->Release ();
if (this->pIWbemLocator) this->pIWbemLocator->Release ();
// release the namespace string
if (this->pNameSpace) SysFreeString (pNameSpace);
// if COM initialized in here we must uninitialize it to maintain integrity
if (SUCCEEDED (this->hrCoInitialize)) CoUninitialize ();
}

I've written this so that you only need to create a single instance of WMIQuery, then you can run as many queries as you like. Here's an example:

Code:
void GetAdapterRAM (void) { WMIQuery *TheWMI = new WMIQuery (); int NumCPUs = TheWMI->ExecQuery (L"Win32_ComputerSystem", L"NumberOfProcessors").intVal; int AdapterRAM = BYTES_TO_MEGS ((TheWMI->ExecQuery (L"Win32_VideoController", L"AdapterRAM")).intVal);
int CPUSpeed = (TheWMI->ExecQuery (L"Win32_Processor", L"MaxClockSpeed")).intVal;
delete TheWMI;
}

Have fun!
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Fri Nov 28, 2008 12:09 am Post subject: Re: Tutorial - Adding WMI Query support to your engine (C++) |

Woohoo, he's back

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Programming Tutorials | All times are GMT
Page 1 of 1

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group