WMI技术介绍和应用——事件通知
? ? ? ? 在《WMI技術(shù)介紹和應(yīng)用——WMI概述》中,我們使用了下圖介紹WMI構(gòu)架(轉(zhuǎn)載請指明出于breaksoftware的csdn博客)
? ? ? ? 我們之前介紹的使用WMI查詢系統(tǒng)、硬件等信息的功能,是通過查詢WMI靜態(tài)數(shù)據(jù)的空間實現(xiàn)的。這個功能的核心是在上圖中2,即WMI Infrastructure層實現(xiàn)的。本文將讓我們對WMI的認(rèn)識深入到1,即WMI Providers and Managed Objects層。具體的功能是使用WMI檢測到WMI數(shù)據(jù)和服務(wù)的變化。再具體一點就是,我們可以使用WMI檢測進程創(chuàng)建、服務(wù)狀態(tài)變化、電腦狀態(tài)變化,磁盤可用空間變化等信息。這些都是非常讓人激動的技術(shù),我想做過安全的朋友應(yīng)該清楚,如果想全局監(jiān)控系統(tǒng)中進程的創(chuàng)建,除了下驅(qū)動就是使用Hook技術(shù)。如果你只是想知道哪些進程創(chuàng)建這樣輕量級的需求,也要使用驅(qū)動、Hook這種重量級的技術(shù),是否感覺有點得不償失?而WMI就給我們提供了這樣的一種輕量級的解決方案。
? ? ? ? 上圖的WMI Providers and Managed Objects(托管對象和WMI提供者)層中,我們將使用Provider幫我們檢測WMI變化。需要注意的一點是,并不是所有的Provider都可以為我們提供事件通知——只有WMI Event Class的托管對象才會在事件發(fā)生時給我們提供通知。我們收到的事件,可能來源于兩種事件,一種是intrinsic event,即內(nèi)在事件;還有一種是extrinsic event,即外來事件。內(nèi)在事件是在標(biāo)準(zhǔn)的WMI數(shù)據(jù)模型發(fā)生改變而產(chǎn)生的事件,這將是我們介紹的重點。外來事件,和內(nèi)在事件相對,即非標(biāo)準(zhǔn)WMI數(shù)據(jù)數(shù)據(jù)模型發(fā)生改變而產(chǎn)生的事件。
? ? ? ? 介紹了這么多基礎(chǔ)知識了,那如何查詢事件通知呢?在《WMI技術(shù)介紹和應(yīng)用——使用VC編寫一個半同步查詢WMI服務(wù)的類》中,我們講解WMI查詢靜態(tài)數(shù)據(jù)時,我們可以使用同步查詢和半同步查詢兩種查詢方式。像靜態(tài)數(shù)據(jù),正如其名,它是靜態(tài)的,即它存在就存在,不存在即不存在,所以我們可以使用同步方式查詢。半同步其實就是一個偽裝的異步操作,我們在那篇文章中已經(jīng)做了介紹,本文不再贅述。而本文主要講解的查詢事件通知,它是動態(tài)發(fā)生的。即可能我查詢的即刻,那個事件還未發(fā)生,我們需要等待一段時間,才會在事件發(fā)生后接收到通知。這就意味著查詢事件通知,是不可能使用同步查詢方式,我們可以選擇異步查詢或者半同步查詢方式。
? ? ? ? 作為查詢的載體——事件使用者(Event Consumers),也是分為兩種:臨時事件使用者和永久事件使用者。
? ? ? ? 臨時事件使用者是我們未來最早接觸到的一個使用者,顧名思義,它是指WMI接收事件通知的生命周期和發(fā)起查詢的應(yīng)用程序一致。WMI包含一個統(tǒng)一的接口用來向客戶端應(yīng)用程序提供WMI事件。
? ? ? ? 永久事件使用者是一種更復(fù)雜的使用者——它是一個COM對象,用于持續(xù)接收WMI事件通知。它使用一些現(xiàn)有的對象和過濾器去獲取WMI事件。我們可以設(shè)置一些WMI對象和過濾器去獲取WMI事件。當(dāng)一個事件發(fā)生,并命中過濾器,WMI將加載永久事件使用者并通知它某事件發(fā)生了?;蛟S你會有點好奇,永久事件使用者是保存在什么地方?WMI又是如何找到它的?永久事件使用者是保存在WMI倉庫中(上圖2層中WMI repository),并且是一個在WMI中注冊的可執(zhí)行文件,這樣WMI便可以方便的尋找和加載它了。
? ? ? ? 這些事件都是由事件提供者(An event provider)發(fā)送給WMI的。它也是個COM組件。我們可以使用C++或者C#編寫事件提供者程序。大部分事件提供者管理著一個WMI對象。對于如何編寫WMI事件提供者,我們會在之后介紹。
? ? ? ? 我們再回到查詢事件通知,首先我們要編寫一個異步事件查詢類。因為連接空間等操作和之前的都相同,所以我們的查詢類也是繼承于《WMI技術(shù)介紹和應(yīng)用——VC開發(fā)WMI應(yīng)用的基本步驟》介紹的CWMI類
template<typename T>
class CAsynNotifyQuery : public CWMI
{
public:CAsynNotifyQuery(const wstring& wszNamespace, const wstring& wszWQLQuery, HANDLE hExitEvent);~CAsynNotifyQuery(void);
private:HRESULT Excute(CComPtr<IWbemServices> pSvc);
private:wstring m_wszWQLQuery;HANDLE m_hExitHandle;
};? ? ? ? CAsynNotifyQuery是一個模板類,這是區(qū)別于我們之前的其他類。我們再來關(guān)注下主要方法——Excute方法的實現(xiàn)
template<typename T>
HRESULT CAsynNotifyQuery<T>::Excute( CComPtr<IWbemServices> pSvc )
{HRESULT hr = WBEM_S_FALSE;do {CComPtr<IUnsecuredApartment> pUnsecApp = NULL;hr = CoCreateInstance( CLSID_UnsecuredApartment, NULL,CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**) &pUnsecApp);CHECKWMIHR(hr);CComPtr<IWbemObjectSink> pSink = new T;CComPtr<IUnknown> pStubUnk = NULL;pUnsecApp->CreateObjectStub(pSink, &pStubUnk);CComPtr<IWbemObjectSink> pStubSink = NULL;pStubUnk->QueryInterface(IID_IWbemObjectSink, (void**)&pStubSink);hr = pSvc->ExecNotificationQueryAsync( CComBSTR("WQL"),CComBSTR(m_wszWQLQuery.c_str()),WBEM_FLAG_SEND_STATUS,NULL,pStubSink );CHECKWMIHR(hr);if ( NULL != m_hExitHandle ) {WaitForSingleObject(m_hExitHandle, INFINITE );}hr = pSvc->CancelAsyncCall(pStubSink);if ( NULL != pSink ) {delete pSink;pSink = NULL;}} while (0);return hr;
}? ? ? ? 首先我們需要創(chuàng)建一個IUnsecuredApartment接口實例。該接口是客戶進程發(fā)起異步調(diào)用的,它提供了一個CreateObjectStub方法創(chuàng)建一個樁,WMI將在異步執(zhí)行過程中對該樁進行操作。我們再看下傳入的模板類的定義
class CSink : public IWbemObjectSink
{
public:CSink();~CSink();virtual ULONG STDMETHODCALLTYPE AddRef();virtual ULONG STDMETHODCALLTYPE Release();virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv);virtual HRESULT STDMETHODCALLTYPE Indicate(LONG lObjectCount, IWbemClassObject __RPC_FAR* __RPC_FAR* apObjArray);virtual HRESULT STDMETHODCALLTYPE SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR* pObjParam);HRESULT DealIWbemClassObject(CComPtr<IWbemClassObject> pObj);
private:// 返回值為WBEM_S_NO_ERROR則繼續(xù)枚舉,否則中斷枚舉virtual HRESULT DealWithSingleItem(CComBSTR bstrName, CComVariant Value, CIMTYPE type, LONG lFlavor);
private:LONG m_lRef;bool m_bDone;
};? ? ? ??IWbemObjectSink接口用于接收WMI事件。我們主要需要實現(xiàn)Indicate方法,WMI框架將調(diào)用這個方法把消息實例傳遞給我們。從這個函數(shù)的最后一個參數(shù)可以看出,它傳遞過來的是一個事件數(shù)組
HRESULT STDMETHODCALLTYPE CSink::Indicate( LONG lObjectCount, IWbemClassObject __RPC_FAR* __RPC_FAR* apObjArray )
{for (long i = 0; i < lObjectCount; i++) { CComPtr<IWbemClassObject> pObj = apObjArray[i]; DealIWbemClassObject(pObj);} return WBEM_NO_ERROR;
}? ? ? ? DealWbemClassObject是我自己定義和實現(xiàn)的方法,其主要功能就是輸出事件的內(nèi)容。
我們可以使用下例去調(diào)用查詢操作
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);CAsynNotifyQuery<CInstanceEvent> recvnotify(L"root\\CIMV2", L"SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'", hEvent);recvnotify.ExcuteFun();
? ? ? ? 更多事件查詢的例子,可以查看《WMI技術(shù)介紹和應(yīng)用——接收事件》一文。
? ? ? ??工程源碼見《WMI技術(shù)介紹和應(yīng)用——WMI概述》結(jié)尾。
總結(jié)
以上是生活随笔為你收集整理的WMI技术介绍和应用——事件通知的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WMI技术介绍和应用——执行方法
- 下一篇: WMI技术介绍和应用——Instance