EternalWindows
WMI / プロバイダ サンプル(exe)

今回はプロバイダをexeとして実装する例を示します。 exeの場合はdllのときのようにwmiprvse.exeが起動されるのではなく、 本当にプロバイダのexeが起動されることになります。 exeはdllのときのようにNETWORK SERVICEではなく、SYSTEMとして動作する特徴があります。 できることならセキュリティコンテキストの低いNETWORK SERVICEとして動作したいところですが、 MOFファイルの__Win32Providerのインスタンスの定義で、HostingModel = "NetworkServiceHost"を記述しても、 変わることはありませんでした。

class CWbemProvider : public IWbemProviderInit, public IWbemServices
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();

	STDMETHODIMP Initialize(LPWSTR wszUser, LONG lFlags, LPWSTR wszNamespace, LPWSTR wszLocale, IWbemServices *pNamespace, IWbemContext *pCtx, IWbemProviderInitSink *pInitSink);

	STDMETHODIMP OpenNamespace(const BSTR strNamespace, long lFlags, IWbemContext *pCtx, IWbemServices **ppWorkingNamespace, IWbemCallResult **ppResult);
	STDMETHODIMP CancelAsyncCall(IWbemObjectSink *pSink);
	STDMETHODIMP QueryObjectSink(long lFlags, IWbemObjectSink **ppResponseHandler);
	STDMETHODIMP GetObject(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemClassObject **ppObject, IWbemCallResult **ppCallResult);
	STDMETHODIMP GetObjectAsync(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP PutClass(IWbemClassObject *pObject, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult);
	STDMETHODIMP PutClassAsync(IWbemClassObject *pObject, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP DeleteClass(const BSTR strClass, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult);
	STDMETHODIMP DeleteClassAsync(const BSTR strClass, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP CreateClassEnum(const BSTR strSuperclass, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum);
	STDMETHODIMP CreateClassEnumAsync(const BSTR strSuperclass, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP PutInstance(IWbemClassObject *pInst, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult);
	STDMETHODIMP PutInstanceAsync(IWbemClassObject *pInst, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP DeleteInstance(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult);
	STDMETHODIMP DeleteInstanceAsync(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP CreateInstanceEnum(const BSTR strFilter, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum);
	STDMETHODIMP CreateInstanceEnumAsync(const BSTR strFilter, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP ExecQuery(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum);
	STDMETHODIMP ExecQueryAsync(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP ExecNotificationQuery(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum);
	STDMETHODIMP ExecNotificationQueryAsync(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler);
	STDMETHODIMP ExecMethod(const BSTR strObjectPath, const BSTR strMethodName, long lFlags, IWbemContext *pCtx, IWbemClassObject *pInParams, IWbemClassObject **ppOutParams, IWbemCallResult **ppCallResult);
	STDMETHODIMP ExecMethodAsync(const BSTR strObjectPath, const BSTR strMethodName, long lFlags, IWbemContext *pCtx, IWbemClassObject *pInParams, IWbemObjectSink *pResponseHandler);

	CWbemProvider();
	~CWbemProvider();
	void SampleMethod(IWbemClassObject *pInParams, IWbemClassObject *pOutParams);
	
private:
	LONG          m_cRef;
	IWbemServices *m_pNamespace;
};

class CClassFactory : public IClassFactory
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
	STDMETHODIMP LockServer(BOOL fLock);
};

extern HANDLE g_hEvent;

次に、ソースファイル(非WMI関連)を示します。

#include <windows.h>
#include <wbemidl.h>
#include <shlwapi.h>
#include "sample.h"

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

const CLSID CLSID_WbemProviderSample = {0xbb466b74, 0x24a8, 0x4611, {0x89, 0x5f, 0x88, 0xa4, 0x67, 0x66, 0xe8, 0xa0}};
const TCHAR g_szClsid[] = TEXT("{BB466B74-24A8-4611-895F-88A46766E8A0}");

HANDLE g_hEvent;

BOOL RegisterServer();
BOOL UnregisterServer();
BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	HRESULT       hr;
	DWORD         dwRegister;
	CClassFactory classFactory;
	int           nResult = 0;

	if ((lstrcmpA(lpszCmdLine, "-RegServer") == 0) || (lstrcmpA(lpszCmdLine, "/RegServer") == 0)) {
		if (RegisterServer())
			MessageBox(NULL, TEXT("登録に成功しました。"), TEXT("OK"), MB_OK);
		else
			MessageBox(NULL, TEXT("登録に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	else if ((lstrcmpA(lpszCmdLine, "-UnregServer") == 0) || (lstrcmpA(lpszCmdLine, "/UnregServer") == 0)) {
		UnregisterServer();
		MessageBox(NULL, TEXT("登録を解除しました。"), TEXT("OK"), MB_OK);
		return 0;
	}
	else
		;

	CoInitializeEx(NULL, COINIT_MULTITHREADED);
	CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_SECURE_REFS, NULL);

	hr = CoRegisterClassObject(CLSID_WbemProviderSample, static_cast<IClassFactory *>(&classFactory), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister);
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}
	
	g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	WaitForSingleObject(g_hEvent, INFINITE);
	CloseHandle(g_hEvent);

	CoRevokeClassObject(dwRegister);
	CoUninitialize();

	return nResult;
}


// CClassFactory


STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
		*ppvObject = static_cast<IClassFactory *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CClassFactory::AddRef()
{
	return 2;
}

STDMETHODIMP_(ULONG) CClassFactory::Release()
{
	return 1;
}

STDMETHODIMP CClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
	CWbemProvider *p;
	HRESULT       hr;
	
	*ppvObject = NULL;

	if (pUnkOuter != NULL)
		return CLASS_E_NOAGGREGATION;

	p = new CWbemProvider();
	if (p == NULL)
		return E_OUTOFMEMORY;

	hr = p->QueryInterface(riid, ppvObject);
	p->Release();

	return hr;
}

STDMETHODIMP CClassFactory::LockServer(BOOL fLock)
{
	return S_OK;
}


// Function


BOOL RegisterServer()
{
	TCHAR szModulePath[MAX_PATH];
	TCHAR szKey[256];

	wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, TEXT("WMI Provider Sample")))
		return FALSE;

	GetModuleFileName(NULL, szModulePath, sizeof(szModulePath) / sizeof(TCHAR));
	wsprintf(szKey, TEXT("CLSID\\%s\\LocalServer32"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, szModulePath))
		return FALSE;
	
	wsprintf(szKey, TEXT("AppID\\%s"), g_szClsid);
	if (!CreateRegistryKey(HKEY_CLASSES_ROOT, szKey, NULL, NULL))
		return FALSE;

	return TRUE;
}

BOOL UnregisterServer()
{
	TCHAR szKey[256];

	wsprintf(szKey, TEXT("CLSID\\%s"), g_szClsid);
	SHDeleteKey(HKEY_CLASSES_ROOT, szKey);
	
	wsprintf(szKey, TEXT("AppID\\%s"), g_szClsid);
	SHDeleteKey(HKEY_CLASSES_ROOT, szKey);

	return TRUE;
}

BOOL CreateRegistryKey(HKEY hKeyRoot, LPTSTR lpszKey, LPTSTR lpszValue, LPTSTR lpszData)
{
	HKEY  hKey;
	LONG  lResult;
	DWORD dwSize;

	lResult = RegCreateKeyEx(hKeyRoot, lpszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	if (lpszData != NULL)
		dwSize = (lstrlen(lpszData) + 1) * sizeof(TCHAR);
	else
		dwSize = 0;

	RegSetValueEx(hKey, lpszValue, 0, REG_SZ, (LPBYTE)lpszData, dwSize);
	RegCloseKey(hKey);
	
	return TRUE;
}

最後に、ソースファイル(WMI関連)を示します。

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

struct DATA {
	int   nId;
	WCHAR szData[256];
} g_data[] = {
	{1, L"apple"},
	{2, L"orange"}
};
const int g_nCount = sizeof(g_data) / sizeof(g_data[0]);
const WCHAR g_szClassName[] = L"MySampProv";


// CWbemProvider


CWbemProvider::CWbemProvider()
{
	m_cRef = 1;
	m_pNamespace = NULL;
}

CWbemProvider::~CWbemProvider()
{
	if (m_pNamespace != NULL)
		m_pNamespace->Release();
}

STDMETHODIMP CWbemProvider::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IWbemProviderInit))
		*ppvObject = static_cast<IWbemProviderInit *>(this);
	else if (IsEqualIID(riid, IID_IWbemServices))
		*ppvObject = static_cast<IWbemServices *>(this);
	else
		return E_NOINTERFACE;
	
	AddRef();

	return S_OK;
}

STDMETHODIMP_(ULONG) CWbemProvider::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CWbemProvider::Release()
{
	if (InterlockedDecrement(&m_cRef) == 0) {
		SetEvent(g_hEvent);
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CWbemProvider::Initialize(LPWSTR wszUser, LONG lFlags, LPWSTR wszNamespace, LPWSTR wszLocale, IWbemServices *pNamespace, IWbemContext *pCtx, IWbemProviderInitSink *pInitSink)
{
	if (pNamespace != NULL) {
		m_pNamespace = pNamespace;
		m_pNamespace->AddRef();
		pInitSink->SetStatus(WBEM_S_INITIALIZED, 0);
		
	}
	else
		pInitSink->SetStatus(WBEM_E_FAILED, 0);

	return WBEM_S_NO_ERROR;
}

STDMETHODIMP CWbemProvider::OpenNamespace(const BSTR strNamespace, long lFlags, IWbemContext *pCtx, IWbemServices **ppWorkingNamespace, IWbemCallResult **ppResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::CancelAsyncCall(IWbemObjectSink *pSink)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::QueryObjectSink(long lFlags, IWbemObjectSink **ppResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::GetObject(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemClassObject **ppObject, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::GetObjectAsync(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::PutClass(IWbemClassObject *pObject, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::PutClassAsync(IWbemClassObject *pObject, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::DeleteClass(const BSTR strClass, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::DeleteClassAsync(const BSTR strClass, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::CreateClassEnum(const BSTR strSuperclass, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::CreateClassEnumAsync(const BSTR strSuperclass, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::PutInstance(IWbemClassObject *pInst, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::PutInstanceAsync(IWbemClassObject *pInst, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::DeleteInstance(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::DeleteInstanceAsync(const BSTR strObjectPath, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::CreateInstanceEnum(const BSTR strFilter, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::CreateInstanceEnumAsync(const BSTR strFilter, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	int              i;
	IWbemClassObject *pClass = NULL;
	IWbemClassObject *pNewInst[100];
	VARIANT          var;

	for (i = 0; i < g_nCount; i++) {
		m_pNamespace->GetObject(strFilter, 0, pCtx, &pClass, NULL);
		pClass->SpawnInstance(0, &pNewInst[i]);
		pClass->Release();
		
		var.vt = VT_I4;
		var.lVal = g_data[i].nId;
		pNewInst[i]->Put(L"id", 0, &var, 0);

		var.vt = VT_BSTR;
		var.bstrVal = SysAllocString(g_data[i].szData);
		pNewInst[i]->Put(L"data", 0, &var, 0);
		VariantClear(&var);
	}	

	pResponseHandler->Indicate(g_nCount, pNewInst);

	pResponseHandler->SetStatus(0, WBEM_S_NO_ERROR, NULL, NULL);

	return WBEM_S_NO_ERROR;
}

STDMETHODIMP CWbemProvider::ExecQuery(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::ExecQueryAsync(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::ExecNotificationQuery(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::ExecNotificationQueryAsync(const BSTR strQueryLanguage, const BSTR strQuery, long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::ExecMethod(const BSTR strObjectPath, const BSTR strMethodName, long lFlags, IWbemContext *pCtx, IWbemClassObject *pInParams, IWbemClassObject **ppOutParams, IWbemCallResult **ppCallResult)
{
	return WBEM_E_NOT_SUPPORTED;
}

STDMETHODIMP CWbemProvider::ExecMethodAsync(const BSTR strObjectPath, const BSTR strMethodName, long lFlags, IWbemContext *pCtx, IWbemClassObject *pInParams, IWbemObjectSink *pResponseHandler)
{
	HRESULT          hr;
	IWbemClassObject *pClass = NULL;
	IWbemClassObject *pOutClass = NULL;
	IWbemClassObject *pOutParams = NULL;
	BSTR             bstrClassName;
	
	bstrClassName = SysAllocString(g_szClassName);
	m_pNamespace->GetObjectW(bstrClassName, 0, pCtx, &pClass, NULL);
	if (pClass == NULL) {
		SysFreeString(bstrClassName);
		pResponseHandler->SetStatus(0, WBEM_E_NOT_SUPPORTED, NULL, NULL);
		return WBEM_E_NOT_SUPPORTED;
	}

	pClass->GetMethod(strMethodName, 0, NULL, &pOutClass);
	pOutClass->SpawnInstance(0, &pOutParams);

	if (lstrcmpW(L"SampleMethod", strMethodName) == 0) {	
		SampleMethod(pInParams, pOutParams);
		pResponseHandler->Indicate(1, &pOutParams);
		hr = WBEM_S_NO_ERROR;
	}
	else
		hr = WBEM_E_NOT_SUPPORTED;

	pResponseHandler->SetStatus(0, hr, NULL, NULL);

	pOutParams->Release();
	pOutClass->Release();
	pClass->Release();
	SysFreeString(bstrClassName);

	return hr;
}

void CWbemProvider::SampleMethod(IWbemClassObject *pInParams, IWbemClassObject *pOutParams)
{
	BSTR    bstrInputArgName = SysAllocString(L"strIn");
	BSTR    bstrOutputArgName = SysAllocString(L"strOut");
	BSTR    bstrRetValName = SysAllocString(L"ReturnValue");
	VARIANT varIn, varOut, varRet;
	BSTR    bstr;
	LONG    lRetValue;

	VariantInit(&varIn);
	pInParams->Get(bstrInputArgName, 0, &varIn, NULL, NULL);
	if (varIn.vt == VT_BSTR) {
		bstr = varIn.bstrVal;
		CharUpperBuffW(bstr, lstrlenW(bstr));
		
		varOut.vt = VT_BSTR;
		varOut.bstrVal = bstr;
		pOutParams->Put(bstrOutputArgName, 0, &varOut, 0);

		lRetValue = lstrlenW(bstr);

		VariantClear(&varIn);
		VariantClear(&varOut);
	}
	else
		lRetValue = -1;

	varRet.vt = VT_I4;
	varRet.lVal = lRetValue;
	pOutParams->Put(bstrRetValName, 0, &varRet, 0);
	
	SysFreeString(bstrInputArgName);
	SysFreeString(bstrOutputArgName);
	SysFreeString(bstrRetValName);
}

プロバイダがexeとして実装されているということは、当然ながらWinMainを持つことになります。 プロバイダはクライアントからの要求に応えるために、CoRegisterClassObjectを呼び出すことになりますが、 コマンドライン文字列として-RegServerなどが渡された場合は、レジストリの登録処理だけを行って終了します。 CoRegisterClassObjectを呼び出しても、プロバイダが直ちに終了してしまっては、クライアントからの要求に応えられなくなるため、 クライアントが終了するまでイベントオブジェクトを使用して待機しています。 このオブジェクトがシグナル状態になるのは、CWbemProvider::Releaseで参照カウントが0になった場合です。 プロバイダの登録処理は次のようにして行います。

C:\sample.exe -RegServer (登録の解除時は C:\sample.exe -UnregServer のようにする)

これにより、プロバイダは登録されることになります。 実際にプロバイダを使用するためには、 プロバイダのCLSIDを記述したMOFファイルがコンパイルされている必要があります。

スクリプトからの使用

WMIはスクリプト言語からも使用できるようになっており、 独自のプロバイダが登録されている場合は、 独自のクラスを指定することもできます。 次に、VBScriptの例を示します。

Set objWMIService = GetObject("winmgmts:")
Set provs = objWMIService.ExecQuery("Select * from MySampProv")

For Each p in provs
  WScript.Echo p.data
Next

このコードをメモ帳などで.vbsとして保存し、ファイルをクリックすれば、 プロバイダ内で定義されたインスタンスが列挙されるはずです。



戻る