使用 C++ 创建一个 WMI 应用程序绝非易事。首先必须初始化 COM(WMI 基于 COM 技术),访问并设置 WMI 协议(调用 CoInitializeEx 和 CoInitializeSecurity 函数以访问 WMI),最后手动完成清理工作。(保持整洁始终很重要!)
以下是连接 WMI 的方法:
通过调用 CoInitializeEx 来初始化 COM。下面的代码示例说明了如何调用 CoInitializeEx:
HRESULT hres;
hres = CoInitializeEx (0, COINIT_MULTITHREADED); // 初始化 COM。
if (FAILED (hres))
{
cout << "初始化 COM 库失败。错误代码 = 0x"
<< hex << hres << endl;
return 1;
}
通过调用 CoInitializeSecurity 接口设置一般 COM 安全级别。
与 CoInitializeEx 一样,CoInitializeSecurity 是设置 COM 接口时需要调用的一个标准函数。下面是调用 CoInitializeSecurity 的方法:
hres = CoInitializeSecurity (NULL, -1, NULL,
NULL, RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IDENTIFY, NULL,
EOAC_NONE, 0);
if (FAILED (hres))
{
cout << "初始化安全性失败。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
对 COM 进行标准调用后,需要通过调用 IWbemLocator::ConnectServer 方法连接到 WMI。ConnectServer 方法返回 IWbemServices 接口的一个代理。
要创建到 WMI 命名空间的连接,需要通过调用 CoCreateInstance 初始化 IWbemLocator 接口。下面的代码说明了如何初始化 IWbemLocator:
IWbemLocator *pLoc = 0;
hres = CoCreateInstance (CLSID_WbemLocator, 0,
CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED (hres))
{
cout <<
"创建 IWbemLocator 对象失败。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
通过调用 IWbemLocator::ConnectServer 方法连接到 WMI。
ConnectServer 方法返回 IWbemServices 接口的一个代理,您可以使用它访问调用 ConnectServer 时指定的本地或远程 WMI 命名空间。调用 ConnectServer 的方法为:
IWbemServices *pServices = NULL;
// 以当前用户身份连接到 root\cimv2 名称空间。
hres = pLoc->ConnectServer (
_bstr_t(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&pServices
);
if (FAILED(hres))
{
cout << "无法连接。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
检索到 IWbemServices 代理的指针后,需要设置该代理的安全级别以通过它访问 WMI。必须设置安全级别,因为 IWbemServices 代理负责为进程外对象授权。一般来说,如果您对安全性属性的设置不正确,COM 安全性不允许一个进程访问另一个进程。
要设置 WMI 连接的安全性级别,需要通过调用 CoSetProxyBlanket 来设置 IWbemServices 代理的安全性级别。下面是调用 CoSetProxyBlanket 的常用方法:
// 设置代理,以模拟客户。
hres = CoSetProxyBlanket(pServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if (FAILED(hres))
{
cout << "无法设置代理层。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
要枚举系统中的所有 Job 对象,GUI 医生通常使用如下代码:
// 枚举 Win32_NamedJobObject 类的实例
IEnumWbemClassObject *pEnumWbemClsObj;
BSTR bstrClsName = SysAllocString (L"Win32_NamedJobObject");
Hres = pServices->CreateInstanceEnum(bstrClsName,
WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
NULL, &pEnumWbemClsObj);
SysFreeString(bstrClsName);
if (FAILED(hres))
{
cout << "无法枚举 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
cout << "系统中存在的 Job 对象包括:" << endl;
while(pEnumWbemClsObj)
{
HRESULT hr = pEnumWbemClsObj->Next (WBEM_INFINITE,
1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
VariantInit(&vtProp);
hr = pclsObj->Get (L"CollectionID", 0, &vtProp, 0, 0);
CHString str(vtProp.bstrVal);
wcout << "Name: " << (LPCWSTR) str.Mid (1, str.GetLength ()) << endl;
VariantClear (&vtProp);
}
IWbemServices::CreateInstanceEnum 方法将创建一个枚举器,它可以根据用户指定的选择标准返回指定类的实例。将类名指定为 Win32_NamedJobObject 以枚举 Job 对象。Win32_NamedJobObject WMI 类表示用来分组进程以控制 Job 对象中进程的资源和寿命的核心对象。
您还需要获得与进程相关的 Job 对象(名称或句柄)。下面的代码可以帮助您从进程 ID 中查找 Job 对象名称或 Job 对象句柄:
// 枚举与 Job 对象
// Win32_NamedJobObjectProcess
// 相关的所有进程
SAFE_INTERFACE_RELEASE (pEnumWbemClsObj);
bstrClsName = SysAllocString (L"Win32_NamedJobObjectProcess");
hres = pServices->CreateInstanceEnum (bstrClsName,
WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
NULL, &pEnumWbemClsObj);
SysFreeString (bstrClsName);
if (FAILED(hres))
{
cout << "无法枚举 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
SAFE_INTERFACE_RELEASE (pclsObj);
uReturn = 0;
cout << endl;
while (pEnumWbemClsObj)
{
HRESULT hr = pEnumWbemClsObj->Next (WBEM_INFINITE,
1, &pclsObj, &uReturn);
if (0 == uReturn)
{
goto CleanUp;
}
VARIANT vtProp;
VariantInit (&vtProp);
hr = pclsObj->Get (L"Member", 0, &vtProp, 0, 0);
CHString str (vtProp.bstrVal);
int i = str.GetLength()-1;
str = str.Right (i - str.Find (L"\""));
str = str.Mid (0, (str.GetLength () - 1));
// argv[1] 是进程 ID,要寻找与其
// 相关的 Job 对象。
if (str.Compare (argv[1]) == 0) //str.Compare("Process ID")
{
VariantClear (&vtProp);
hr = pclsObj->Get (L"Collection", 0, &vtProp, 0, 0);
str = vtProp.bstrVal;
int i = str.GetLength ()-1;
str = str.Right (i - (str.Find (L"\"") + 2));
i = str.GetLength ()-1;
str = str.Left (i);
VariantClear (&vtProp);
wcout << "Process ID: " << argv[1] <<
" is attached with Job ID: " << (LPCWSTR)str << endl;
}
VariantClear (&vtProp);
}
以下是连接 WMI 的方法:
通过调用 CoInitializeEx 来初始化 COM。下面的代码示例说明了如何调用 CoInitializeEx:
HRESULT hres;
hres = CoInitializeEx (0, COINIT_MULTITHREADED); // 初始化 COM。
if (FAILED (hres))
{
cout << "初始化 COM 库失败。错误代码 = 0x"
<< hex << hres << endl;
return 1;
}
通过调用 CoInitializeSecurity 接口设置一般 COM 安全级别。
与 CoInitializeEx 一样,CoInitializeSecurity 是设置 COM 接口时需要调用的一个标准函数。下面是调用 CoInitializeSecurity 的方法:
hres = CoInitializeSecurity (NULL, -1, NULL,
NULL, RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IDENTIFY, NULL,
EOAC_NONE, 0);
if (FAILED (hres))
{
cout << "初始化安全性失败。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
对 COM 进行标准调用后,需要通过调用 IWbemLocator::ConnectServer 方法连接到 WMI。ConnectServer 方法返回 IWbemServices 接口的一个代理。
要创建到 WMI 命名空间的连接,需要通过调用 CoCreateInstance 初始化 IWbemLocator 接口。下面的代码说明了如何初始化 IWbemLocator:
IWbemLocator *pLoc = 0;
hres = CoCreateInstance (CLSID_WbemLocator, 0,
CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED (hres))
{
cout <<
"创建 IWbemLocator 对象失败。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
通过调用 IWbemLocator::ConnectServer 方法连接到 WMI。
ConnectServer 方法返回 IWbemServices 接口的一个代理,您可以使用它访问调用 ConnectServer 时指定的本地或远程 WMI 命名空间。调用 ConnectServer 的方法为:
IWbemServices *pServices = NULL;
// 以当前用户身份连接到 root\cimv2 名称空间。
hres = pLoc->ConnectServer (
_bstr_t(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&pServices
);
if (FAILED(hres))
{
cout << "无法连接。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
检索到 IWbemServices 代理的指针后,需要设置该代理的安全级别以通过它访问 WMI。必须设置安全级别,因为 IWbemServices 代理负责为进程外对象授权。一般来说,如果您对安全性属性的设置不正确,COM 安全性不允许一个进程访问另一个进程。
要设置 WMI 连接的安全性级别,需要通过调用 CoSetProxyBlanket 来设置 IWbemServices 代理的安全性级别。下面是调用 CoSetProxyBlanket 的常用方法:
// 设置代理,以模拟客户。
hres = CoSetProxyBlanket(pServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if (FAILED(hres))
{
cout << "无法设置代理层。错误代码 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
要枚举系统中的所有 Job 对象,GUI 医生通常使用如下代码:
// 枚举 Win32_NamedJobObject 类的实例
IEnumWbemClassObject *pEnumWbemClsObj;
BSTR bstrClsName = SysAllocString (L"Win32_NamedJobObject");
Hres = pServices->CreateInstanceEnum(bstrClsName,
WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
NULL, &pEnumWbemClsObj);
SysFreeString(bstrClsName);
if (FAILED(hres))
{
cout << "无法枚举 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
cout << "系统中存在的 Job 对象包括:" << endl;
while(pEnumWbemClsObj)
{
HRESULT hr = pEnumWbemClsObj->Next (WBEM_INFINITE,
1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
VariantInit(&vtProp);
hr = pclsObj->Get (L"CollectionID", 0, &vtProp, 0, 0);
CHString str(vtProp.bstrVal);
wcout << "Name: " << (LPCWSTR) str.Mid (1, str.GetLength ()) << endl;
VariantClear (&vtProp);
}
IWbemServices::CreateInstanceEnum 方法将创建一个枚举器,它可以根据用户指定的选择标准返回指定类的实例。将类名指定为 Win32_NamedJobObject 以枚举 Job 对象。Win32_NamedJobObject WMI 类表示用来分组进程以控制 Job 对象中进程的资源和寿命的核心对象。
您还需要获得与进程相关的 Job 对象(名称或句柄)。下面的代码可以帮助您从进程 ID 中查找 Job 对象名称或 Job 对象句柄:
// 枚举与 Job 对象
// Win32_NamedJobObjectProcess
// 相关的所有进程
SAFE_INTERFACE_RELEASE (pEnumWbemClsObj);
bstrClsName = SysAllocString (L"Win32_NamedJobObjectProcess");
hres = pServices->CreateInstanceEnum (bstrClsName,
WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY,
NULL, &pEnumWbemClsObj);
SysFreeString (bstrClsName);
if (FAILED(hres))
{
cout << "无法枚举 = 0x"
<< hex << hres << endl;
retVal = 1;
goto CleanUp;
}
SAFE_INTERFACE_RELEASE (pclsObj);
uReturn = 0;
cout << endl;
while (pEnumWbemClsObj)
{
HRESULT hr = pEnumWbemClsObj->Next (WBEM_INFINITE,
1, &pclsObj, &uReturn);
if (0 == uReturn)
{
goto CleanUp;
}
VARIANT vtProp;
VariantInit (&vtProp);
hr = pclsObj->Get (L"Member", 0, &vtProp, 0, 0);
CHString str (vtProp.bstrVal);
int i = str.GetLength()-1;
str = str.Right (i - str.Find (L"\""));
str = str.Mid (0, (str.GetLength () - 1));
// argv[1] 是进程 ID,要寻找与其
// 相关的 Job 对象。
if (str.Compare (argv[1]) == 0) //str.Compare("Process ID")
{
VariantClear (&vtProp);
hr = pclsObj->Get (L"Collection", 0, &vtProp, 0, 0);
str = vtProp.bstrVal;
int i = str.GetLength ()-1;
str = str.Right (i - (str.Find (L"\"") + 2));
i = str.GetLength ()-1;
str = str.Left (i);
VariantClear (&vtProp);
wcout << "Process ID: " << argv[1] <<
" is attached with Job ID: " << (LPCWSTR)str << endl;
}
VariantClear (&vtProp);
}