WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类
? ? ? ? 在《WMI技術介紹和應用——VC開發WMI應用的基本步驟》文中,我們介紹了VC使用WMI技術的基本框架。本節我將講解封裝和實現一個用于半同步查詢WMI的類。(轉載請指明出于breaksoftware的csdn博客)
? ? ? ? 我曾思考過如何編寫一個比較有用的類,因為不同平臺上WMI的查詢結果集是不同的,很難做個通用的類。于是,我使用了最簡單的方法——遍歷返回結果。我們先看下類的聲明
class CSynQuery : public CWMI
{
public:CSynQuery(const wstring& wszNamespace, const wstring& wszWQLQuery);~CSynQuery(void);
private:HRESULT Excute(CComPtr<IWbemServices> pSvc);HRESULT DealWithIWbemClassObject(CComPtr<IWbemClassObject> pclsObj);virtual HRESULT DealWithSingleItem( CComBSTR bstrName, CComVariant Value, CIMTYPE type, LONG lFlavor );
private:wstring m_wszWQLQuery;
};
? ? ? ? Excute是繼承于CWMI基類中的純虛函數。在CSynQuery實現的Excute中,我將執行一次半同步查詢,并枚舉返回的結果。
?
HRESULT CSynQuery::Excute( CComPtr<IWbemServices> pSvc )
{HRESULT hr = WBEM_S_FALSE;do {CComPtr<IEnumWbemClassObject> pEnumerator = NULL;hr = pSvc->ExecQuery( CComBSTR("WQL"),CComBSTR(m_wszWQLQuery.c_str()),WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,NULL,&pEnumerator );CHECKWMIHR(hr);ULONG uReturn = 0;while (pEnumerator) {CComPtr<IWbemClassObject> pclsObj = NULL;HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);if ( 0 == uReturn) {break;}DealWithIWbemClassObject(pclsObj);}} while (0);return hr;
}
? ? ? ? 首先我們先看一下用于(半)同步查詢的函數ExecQuery的聲明
HRESULT ExecQuery([in] const BSTR strQueryLanguage,[in] const BSTR strQuery,[in] LONG lFlags,[in] IWbemContext *pCtx,[out] IEnumWbemClassObject **ppEnum
);
? ? ? ??strQueryLanguage是用于標記查詢的語言種類。由此參數可以發現,當初微軟設計這個接口時是希望未來它支持更多的查詢語言。由于目前只支持WQL語言,所以該參數只能傳“WQL”。第二個參數strQuery是實際用于查詢的命令,比如“Select * From XXX",是不是看著很像SQL?第三個參數lFlags是一個非常重要的參數,如果仔細看該文標題,可以發現,我用的是”半同步“而不是“同步”,該參數就控制著該函數到底是“同步”還是“半同步”。如果該參數包含WBEM_FLAG_RETURN_IMMEDIATELY,則說明該調用是個半同步調用,否則是同步調用。
? ? ? ? 現在我說一下WMI中同步和半同步兩者的區別。同步這個過程不難理解,如果我們同步調用一個函數,該函數會經過計算后將返回結果準備好,然后返回到調用處。如果該過程非常消耗時間,且返回的結果非常占用空間,比如返回10240個對象,是不是覺得這個調用過程非常笨重?是的!那么解決這個問題的很好的方法便誕生了:半同步。半同步的調用方式非常類似于異步(以后介紹)調用。當我們調用一個半同步操作后,函數內部會啟動線程去執行查詢工作,之后會立即返回到我們的調用處。當半同步內部線程查詢到并封裝完一個對象后,便會通知我們外面枚舉結果的函數,告訴我們:一個結果準備好了,你可以使用了。這個相當于將合并結果集的過程去掉??紤]到調用半同步的邏輯處理一個返回對象可能需要一定的時間,在半同步啟動的線程中可以利用這段時間完成下一個對象的查找和封裝。所以總體來說半同步對時間的消耗是比同步好的。而從占用資源的角度看,半同步不用一次返回那么多個結果,所以占用的資源會比同步方式好很多。所以大部分情況下,只在同步和半同步中做出選擇的情況下,優先考慮使用半同步。
? ? ? ? 回到ExecQuery這個函數,lFlags還有個非常重要的可選值是WBEM_FLAG_FORWARD_ONLY。該參數讓ExecQuery函數返回的枚舉是個Forward-Only的。這樣做的好處是可以讓我們程序更快且占用更少的資源。所以lFlags一般是WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY。
? ? ? ??pCtx一般設置為NULL。ppEnum是個返回結果的枚舉器。
ULONG uReturn = 0;while (pEnumerator) {CComPtr<IWbemClassObject> pclsObj = NULL;HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);if ( 0 == uReturn) {break;}DealWithIWbemClassObject(pclsObj);}
? ? ? ? 我們可以如此枚舉返回的結果集。關注一下Next第一參數。在之前對半同步的介紹中,我們說道:半同步函數啟動的線程需要查詢和封裝下一個結果,其實這個可以看成是個異步操作,于是枚舉結果這邊需要等待那個實際查詢的線程完成一個結果。我給此參數設置為“一直等待”。一般來說,這個結果是立即返回的。pclsObj保存的是一個結果集中的一個結果。我們使用DealWithIWbemClassObject函數處理每個結果。
HRESULT CSynQuery::DealWithIWbemClassObject( CComPtr<IWbemClassObject> pclsObj )
{HRESULT hr = WBEM_S_NO_ERROR; do {CComVariant vtClass; hr = pclsObj->Get(L"__CLASS", 0, &vtClass, NULL, NULL); CHECKWMIHR(hr);if ( VT_BSTR == vtClass.vt ) {wprintf(L"\n%s\n", vtClass.bstrVal);}hr = pclsObj->BeginEnumeration(WBEM_FLAG_LOCAL_ONLY);do {CComBSTR bstrName;CComVariant Value;CIMTYPE type;LONG lFlavor = 0;hr = pclsObj->Next(0, &bstrName, &Value, &type, &lFlavor);CHECKWMIHR(hr);hr = DealWithSingleItem(bstrName, Value, type, lFlavor); }while ( WBEM_S_NO_ERROR == hr );hr = pclsObj->EndEnumeration();} while (0);return hr;
}
? ? ? ? 該函數首先會打印出該結果屬于哪個WMI類,然后會枚舉結果中的每個成員。其實在實際使用中大可不必如此,我如此設計只是為了該例子可以在不同平臺上,針對不同需求都可以正確執行。需要注意的一點是在枚舉前,我們需要制定要枚舉什么。于是我們要調用BeginEnumeration,并傳WBEM_FLAG_LOCAL_ONLY,即枚舉該類自己的成員。在枚舉完之后,一般要調用EndEnumeration。其實這個不是必須的,只有在要提前退出枚舉的場景下才必須調用EndEnumeration。針對每個成員,我們又要使用虛函數DealWithSingleItem來處理。于是處理邏輯又被我封裝到一個繼承于CSynQuery的類CSynQueryData中。該類邏輯非常簡單,如果成員是非對象,則直接打印出來;如果結果是一個對象,則再解析這個對象,并將其中非對象打印出來。
HRESULT CSynQueryData::DealWithSingleItem( CComBSTR bstrName, CComVariant Value, CIMTYPE type, LONG lFlavor )
{HRESULT hr = WBEM_S_NO_ERROR; switch ( Value.vt ) {case VT_UNKNOWN : {DealWithUnknownTypeItem(bstrName, Value, type, lFlavor);}break;default: {PrintfItem(bstrName, Value, type, lFlavor);};}return hr;
}HRESULT CSynQueryData::DealWithUnknownTypeItem( CComBSTR bstrName, CComVariant Value, CIMTYPE type, LONG lFlavor )
{HRESULT hr = WBEM_S_NO_ERROR;if ( NULL == Value.punkVal ) {return hr;}// object類型轉換成IWbemClassObject接口指針,通過該指針枚舉其他屬性CComPtr<IWbemClassObject> pObjInstance = (IWbemClassObject*)Value.punkVal;hr = pObjInstance->BeginEnumeration(WBEM_FLAG_LOCAL_ONLY);do {CHECKHR(hr);CComBSTR bstrNewName;CComVariant NewValue;CIMTYPE newtype;LONG lnewFlavor = 0;hr = pObjInstance->Next(0, &bstrNewName, &NewValue, &newtype, &lnewFlavor);CHECKHR(hr);PrintfItem(bstrNewName, NewValue, newtype, lnewFlavor);}while ( WBEM_S_NO_ERROR == hr );hr = pObjInstance->EndEnumeration();return WBEM_S_NO_ERROR;
}VOID CSynQueryData::PrintfItem( CComBSTR bstrName, CComVariant Value, CIMTYPE type, LONG lFlavor )
{wprintf(L"%s\t",bstrName.m_str);switch ( Value.vt ){case VT_BSTR: {wprintf(L"%s",Value.bstrVal); }break;case VT_I1:case VT_I2:case VT_I4:case VT_I8: case VT_INT: {wprintf(L"%d",Value.intVal); }break;case VT_UI8:case VT_UI1: case VT_UI2:case VT_UI4:case VT_UINT:{wprintf(L"0x%u",Value.intVal); }break;case VT_BOOL:{wprintf(L"%s", Value.boolVal ? L"TRUE" : L"FASLE" );}break;default:{ATLASSERT(FALSE);};}wprintf(L"\n");
}
? ? ? ? 至此,一個半同步查詢的框架就搭建完成。我會之后若干節,結合實際應用場景,利用這個框架,講解WMI的實際應用。
? ? ? ? 工程源碼見《WMI技術介紹和應用——WMI概述》結尾。
總結
以上是生活随笔為你收集整理的WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WMI技术介绍和应用——VC开发WMI应
- 下一篇: WMI技术介绍和应用——查询正在运行的进