在学习本文前,你需要一点点的VBS基础,WMI了解常识,COM接口皮毛就行。
今天的问题是,如何获得系统中安装的杀毒软件?有人会说遍历注册表之类的。其实用不着这么麻烦。每个正式的AV(Anti-Virus)软件,都要向系统注册自己。通过查看WMI中的/root/SecurityCenter这个名称空间,我们就能知道其中装了那些反病毒产品。
我们来看一段Vbs代码:
strComputer = "." Set objComputer = CreateObject("Shell.LocalMachine") Set oWMI = GetObject("winmgmts://" & strComputer & "/root/SecurityCenter") Set colAV = oWMI.ExecQuery("Select * from AntiVirusProduct") For Each objAntiVirusProduct In colAV If IsNull(objAntiVirusProduct.instanceGuid) Then strSubject = "Anti-virus is not running on " & objComputer.MachineName strTextbody = "You will need to check on " & objComputer.MachineName Call SmtpServer Else strCompany = objAntiVirusProduct.companyName strAV = objAntiVirusProduct.displayName strScanning = objAntiVirusProduct.onAccessScanningEnabled strUptodate = objAntiVirusProduct.productUptoDate
如果以上代码你觉得很难懂的话,那么不推荐你继续看下去,请稍微补习下Vbs和Wmi相关的知识。
Select * from AntiVirusProduct是一条WQL语句,用来查询所有的反病毒软件,并返回一个集合。我们用WMITools里面的WMI CIM Studio进行查看下/root/SecurityCenter这个名称空间里面有哪些Win32 Class
![VC之获得系统安装的反病毒软件[Using WMI in C++]_第1张图片](http://img.e-com-net.com/image/info5/4bd6b0aa9f6141c58610a1dee0a78deb.jpg)
在左边,看到了么,AntiVirusProduct。还有FireWallProduct,防火墙产品,本机没有防火墙,就用查看反病毒软件来做说明吧。
右边的呢,都是AntiVirusProduct这个类的属性(Properties)。里面有产品名,产品公司名,版本号等等。那么我现在需要的,就是通过C++获取到和Vbs一样的信息。上面的Vbs脚本不知道你执行过了么?
我们接下来看C++代码
#define _WIN32_DCOM #include <iostream> using namespace std; #include <comdef.h> #include <Wbemidl.h> # pragma comment(lib, "wbemuuid.lib") int main(int argc, char **argv) { HRESULT hres; // Step 1: -------------------------------------------------- // Initialize COM. ------------------------------------------ hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl; return 1; // Program has failed. } // Step 2: -------------------------------------------------- // Set general COM security levels -------------------------- hres = CoInitializeSecurity( NULL, -1, // COM authentication NULL, // Authentication services NULL, // Reserved RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation NULL, // Authentication info EOAC_NONE, // Additional capabilities NULL // Reserved ); if (FAILED(hres)) { cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl; CoUninitialize(); return 1; // Program has failed. } // Step 3: --------------------------------------------------- // Obtain the initial locator to WMI ------------------------- IWbemLocator *pLoc = NULL; hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres)) { cout << "Failed to create IWbemLocator object." << " Err code = 0x" << hex << hres << endl; CoUninitialize(); return 1; // Program has failed. } // Step 4: ----------------------------------------------------- // Connect to WMI through the IWbemLocator::ConnectServer method IWbemServices *pSvc = NULL; // Connect to the root/SecurityCenter namespace with // the current user and obtain pointer pSvc // to make IWbemServices calls. hres = pLoc->ConnectServer( _bstr_t(L"ROOT//SecurityCenter"), // Object path of WMI namespace NULL, // User name. NULL = current user NULL, // User password. NULL = current 0, // Locale. NULL indicates current NULL, // Security flags. 0, // Authority (e.g. Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy ); if (FAILED(hres)) { cout << "Could not connect. Error code = 0x" << hex << hres << endl; pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } cout << "Connected to ROOT//SecurityCenter WMI namespace" << endl; // Step 5: -------------------------------------------------- // Set security levels on the proxy ------------------------- hres = CoSetProxyBlanket( pSvc, // Indicates the proxy to set RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } // Step 6: -------------------------------------------------- // Use the IWbemServices pointer to make requests of WMI ---- // For example, get the name of the operating system IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM AntiVirusProduct"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { cout << "Query for operating system name failed." << " Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } // Step 7: ------------------------------------------------- // Get the data from the query in step 6 ------------------- IWbemClassObject *pclsObj; ULONG uReturn = 0; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if(0 == uReturn) { break; } VARIANT vtProp; // Get the value of the Name property hr = pclsObj->Get(L"displayName", 0, &vtProp, 0, 0); wcout << "AV Product displayName : " << vtProp.bstrVal << endl; hr = pclsObj->Get(L"companyName", 0, &vtProp, 0, 0); wcout << "AV Product companyName : " << vtProp.bstrVal << endl; hr = pclsObj->Get(L"pathToSignedProductExe", 0, &vtProp, 0, 0); wcout << "AV Product pathToSignedProductExe : " << vtProp.bstrVal << endl; hr = pclsObj->Get(L"versionNumber", 0, &vtProp, 0, 0); wcout << "AV Product versionNumber : " << vtProp.bstrVal << endl; VariantClear(&vtProp); pclsObj->Release(); } // Cleanup // ======== pSvc->Release(); pLoc->Release(); pEnumerator->Release(); //pclsObj->Release(); CoUninitialize(); return 0; // Program successfully completed. }
这份代码是我从MSDN中的C++ WMI例子中抠出来然后针对具体情况进行修改,所以并不能算是真正意义上的原创。但是呢,这个教材文章确实是原创的 (Tr0j4n *_*)
运行效果:
![VC之获得系统安装的反病毒软件[Using WMI in C++]_第2张图片](http://img.e-com-net.com/image/info5/0cc06bfb7a3144ea98fdc49a0b6e6343.jpg)
注释大部分都是微软加的,所以非常详细。它本来是用来查询ROOT//CIMV2这个名称空间中的Win32_OperatingSystem类,然后获得操作系统的名称。说实话,用C++调用WMI就为了得到操作系统名称,这简直是一种浪费,简单的Win32 API调用就能实现了。所以我把它改成了查看本机的反病毒产品的代码。
C++操作WMI执行WQL返回,其实就是一个模板,所以我认为大家有掌握的必要。而且可以举一反三。
Step1:Initialize COM parameters with a call to CoInitializeEx.
For more information, see Initializing COM for a WMI Application.
Step2:Initialize COM process security by calling CoInitializeSecurity.
Windows 2000: Specify the default authentication credentials for a user by using a SOLE_AUTHENTICATION_LIST structure in the pAuthList parameter of CoInitializeSecurity.
For more information, see Setting the Default Process Security Level Using C++.
Step3:Obtain the initial locator to WMI by calling CoCreateInstance.
For more information, see Creating a Connection to a WMI Namespace.
Step4:Obtain a pointer to IWbemServices for the root/cimv2 namespace on the local computer by calling IWbemLocator::ConnectServer. To connect to a remote computer, see Example: Getting WMI Data from a Remote Computer.
For more information, see Creating a Connection to a WMI Namespace.
Step5:Set IWbemServices proxy security so the WMI service can impersonate the client by calling CoSetProxyBlanket.
For more information, see Setting the Security Levels on a WMI Connection.
Step6:Use the IWbemServices pointer to make requests of WMI. This example executes a query for the name of the operating system by calling IWbemServices::ExecQuery.
The following WQL query is one of the method arguments.
SELECT * FROM Win32_OperatingSystem 这就是WQL语句
The result of this query is stored in an IEnumWbemClassObject pointer. This allows the data objects from the query to be retrieved semisynchronously with the IEnumWbemClassObject interface. For more information, see Enumerating WMI. For getting the data asynchronously, see Example: Getting WMI Data from the Local Computer Asynchronously.
For more information about making requests to WMI, see Manipulating Class and Instance Information, Querying WMI, and Calling a Method.
Step7:Get and display the data from the WQL query. The IEnumWbemClassObject pointer is linked to the data objects that the query returned, and the data objects can be retrieved with the IEnumWbemClassObject::Next method. This method links the data objects to an IWbemClassObject pointer that is passed into the method. Use the IWbemClassObject::Get method to get the desired information from the data objects.
以上解释出自MSDN。MSDN用得好,对编程能力是一个很大的帮助。如果觉得英语不行,只能去恶补咯。
其实,关键在4,6,7步。第四步弄的是名称空间,第六步写的是WQL语句,第七步获得返回值。至于第七步。你对比那个While循环中的C++语句,和文章开头的Vbs语句中的For each对比,你看出了什么名堂了吗?
Vbs通过For each枚举类实例,而C++中通过IEnumWbemClassObject接口指针。
如何获得IEnumWbemClassObject接口指针呢?微软告诉我们有4种方法。我用的是第一种,执行一个WQL语句,获得一个AntiVirusProduct类实例的枚举接口指针。
IWbemServices::ExecQuery
IWbemServices::CreateInstanceEnum
IWbemServices::CreateClassEnum
IWbemServices::ExecNotificationQuery
我是怎么实现变量的呢?我用的是枚举接口指针的Next方法,其实它还有别的方法,比如Skip跳过,NextAsync异步下一个。
今后有什么WMI的代码想在VC中实现,只要按这个模板一套用,属于你自己的C++ WMI代码就出炉的。对C++ WMI编程感兴趣的同学,可以参考 这里