EternalWindows
WMI / プロパティの列挙

前節では、IWbemServices::CreateInstanceEnumを呼び出すことで、特定のクラスの全てのインスタンスを列挙しましたが、 WMIはこれよりも柔軟な可能とするWQLをサポートしています。 WQL(WMI Query Language)はSQLを基にして作られた言語であり、この言語を使用すれば、 プロパティが特定の値になっているインスタンスだけを列挙するようなことが可能になります。 WQLを使用してインスタンスを列挙したい場合は、IWbemServices::ExecQueryを呼び出します。

HRESULT IWbemServices::ExecQuery(
  const BSTR strQueryLanguage,
  const BSTR strQuery,
  LONG lFlags,
  IWbemContext *pCtx,
  IEnumWbemClassObject **ppEnum
);

strQueryLanguageは、WMIに使用される言語を指定します。これはWQLでなければなりません。 strQueryは、クエリを示す文字列を指定します。 lFlagsは、このメソッドの動作に影響するフラグを指定します。 WBEM_FLAG_RETURN_IMMEDIATELYとWBEM_FLAG_FORWARD_ONLYを指定するのが、 パフォーマンス的に一番よいとされています。 pCtxは、NULLで問題ありません。 ppEnumは、IEnumWbemClassObjectを受け取る変数のアドレスを指定します。

IWbemServices::ExecQueryを呼び出す例を次に示します。

bstrLanguage = SysAllocString(L"WQL");
bstrQuery = SysAllocString(L"SELECT * FROM Win32_Process");
pNamespace->ExecQuery(bstrLanguage, bstrQuery, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

このWQLは、SQLにおけるSELECT文と同じ要領です。 SQLではテーブルの行を取得する意味がありましたが、 WQLではクラスのインスタンスを取得する意味があります。 上記の使い方では、IWbemServices::CreateInstanceEnumを使用するのと変わりありませんが、 次のように記述するとWQLの利点を活かすことができます。

bstrQuery = SysAllocString(L"SELECT * FROM Win32_Process WHERE Caption = 'notepad.exe'");

WHERE句の後に条件式を記述することで、列挙するインスタンスを限定することができます。 上記の例では、Captionプロパティがnotepad.exeである場合のみということになりますから、 メモ帳のプロセスだけ列挙することになります。

特定のクラスのインスタンスがどのようなプロパティを持っているかは、 クラスの定義を予め確認しておくことで分かります。 動的に確認したい場合は、IWbemServices::GetNamesを呼び出します。

HRESULT IWbemServices::GetNames(
  LPCWSTR wszQualifierName,
  LONG lFlags,
  VARIANT *pQualifierValue,
  SAFEARRAY **pstrNames
);

wszQualifierNameは、フィルタとして使用する文字列を指定します。 NULLでも問題ありません。 lFlagsは、フィルタに関する定数を指定します。 WBEM_FLAG_ALWAYSを指定すれば、全てのプロパティを列挙できます。 pQualifierValueは、フィルタに関するVARIANT構造体を指定します。 pstrNamesは、プロパティを受け取る配列のアドレスを指定します。 配列の要素はBSTR型になります。

今回のプログラムは、特定のインスタンスの全てのプロパティをリストボックスに列挙します。

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

#pragma comment (lib, "wbemuuid.lib")

void EnumInstanceProperty(HWND hwndListBox, IEnumWbemClassObject *pEnumerator);
void GetValue(VARIANT var, LPWSTR lpszBuf);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND          hwndListBox = NULL;
	static IWbemServices *pNamespace = NULL;
	
	switch (uMsg) {

	case WM_CREATE: {
		HRESULT              hr;
		BSTR                 bstrNamespace;
		BSTR                 bstrQuery;
		BSTR                 bstrLanguage;
		IWbemLocator         *pLocator;
		IEnumWbemClassObject *pEnumerator;

		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		CoInitializeEx(0, COINIT_MULTITHREADED);
		CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

		CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pLocator));
	
		bstrNamespace = SysAllocString(L"root\\cimv2");
		hr = pLocator->ConnectServer(bstrNamespace, NULL, NULL, NULL, 0, NULL, NULL, &pNamespace);
		if (FAILED(hr)) {
			SysFreeString(bstrNamespace);
			pLocator->Release();
			return -1;
		}

		hr = CoSetProxyBlanket(pNamespace, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);

		bstrLanguage = SysAllocString(L"WQL");
		bstrQuery = SysAllocString(L"SELECT * FROM Win32_Process WHERE Caption = 'notepad.exe'");
		pNamespace->ExecQuery(bstrLanguage, bstrQuery, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);

		EnumInstanceProperty(hwndListBox, pEnumerator);

		SysFreeString(bstrQuery);
		SysFreeString(bstrLanguage);
		SysFreeString(bstrNamespace);
		pEnumerator->Release();
		pLocator->Release();

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		if (pNamespace != NULL)
			pNamespace->Release();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void EnumInstanceProperty(HWND hwndListBox, IEnumWbemClassObject *pEnumerator)
{
	HRESULT          hr;
	ULONG            uReturned;
	LONG             i, lIndexMin, lIndexMax;
	SAFEARRAY        *pNames;
	LPWSTR           *p;
	VARIANT          var;
	WCHAR            szBuf[512], szValue[256];
	IWbemClassObject *pObject;

	hr = pEnumerator->Next(WBEM_INFINITE, 1, &pObject, &uReturned);
	if (hr != S_OK)
		return;

	pObject->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pNames);
	SafeArrayGetLBound(pNames, 1, &lIndexMin);
	SafeArrayGetUBound(pNames, 1, &lIndexMax);
	SafeArrayAccessData(pNames, (void **)&p);

	for (i = lIndexMin; i <= lIndexMax; i++) {
		pObject->Get(p[i], 0, &var, NULL, NULL);
		GetValue(var, szValue);
		wsprintfW(szBuf, L"%s : %s", p[i], szValue);
		SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
		VariantClear(&var);
	}

	SafeArrayUnaccessData(pNames);
	SafeArrayDestroy(pNames);
	pObject->Release();
}

void GetValue(VARIANT var, LPWSTR lpszBuf)
{
	if (var.vt == VT_I4)
		wsprintfW(lpszBuf, L"%d", var.lVal);
	else if (var.vt == VT_BSTR)
		lstrcpyW(lpszBuf, var.bstrVal);
	else if (var.vt == VT_BOOL)
		wsprintfW(lpszBuf, L"%s", var.boolVal == VARIANT_TRUE ? L"true" : L"false");
	else if (var.vt == VT_UI1)
		wsprintfW(lpszBuf, L"%u", var.uiVal);
	else
		lstrcpyW(lpszBuf, L"?");
}

WM_CREATEでは、IWbemServices::ExecQueryを呼び出すことでIEnumWbemClassObjectを取得しています。 クエリの内容がメモ帳プロセスだけを列挙することになっているため、事前にメモ帳を起動しておいてください。 自作関数のEnumInstancePropertyの実装は次のようになっています。

void EnumInstanceProperty(HWND hwndListBox, IEnumWbemClassObject *pEnumerator)
{
	HRESULT          hr;
	ULONG            uReturned;
	LONG             i, lIndexMin, lIndexMax;
	SAFEARRAY        *pNames;
	LPWSTR           *p;
	VARIANT          var;
	WCHAR            szBuf[512], szValue[256];
	IWbemClassObject *pObject;

	hr = pEnumerator->Next(WBEM_INFINITE, 1, &pObject, &uReturned);
	if (hr != S_OK)
		return;

	pObject->GetNames(NULL, WBEM_FLAG_ALWAYS, NULL, &pNames);
	SafeArrayGetLBound(pNames, 1, &lIndexMin);
	SafeArrayGetUBound(pNames, 1, &lIndexMax);
	SafeArrayAccessData(pNames, (void **)&p);

	for (i = lIndexMin; i <= lIndexMax; i++) {
		pObject->Get(p[i], 0, &var, NULL, NULL);
		GetValue(var, szValue);
		wsprintfW(szBuf, L"%s : %s", p[i], szValue);
		SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
		VariantClear(&var);
	}

	SafeArrayUnaccessData(pNames);
	SafeArrayDestroy(pNames);
	pObject->Release();
}

本来、IEnumWbemClassObject::Nextは複数回呼び出すものですが、 今回は対象となるプロセスをメモ帳だけに限定しているため、一回だけ呼び出すようにしています。 IWbemClassObjectを取得したら、GetNamesを呼び出すことでプロパティ名を格納した配列を取得します。 この配列はSAFEARRAY型で識別できるため、SafeArrayGetLBoundを呼び出せば配列の最小の要素を、 SafeArrayGetUBoundを呼び出せば配列の最大の要素を取得できます。 SafeArrayAccessDataを呼び出せば実際に配列の各要素にアクセスでき、 p[i]からi番目のプロパティ名を参照できます。 Getを呼び出してプロパティの値を取得したら、GetValueで型に応じた適切な処理を行います。


戻る