C++通过WMI获取硬件配置信息
WMI即Windows管理规范。通过它可以访问、配置、管理和监视几乎所有的Windows资源。
WMI提供程序在WMI和托管资源之间扮演着中间方的角色。提供程序代表使用者应用程序和脚本从WMI托管资源请求信息,并发送指令到WMI托管资源。
下面是我们利用WMI编程经常要用到的WMI内置提供程序清单,以供编程参考。
1. Active Directory提供程序
链接库文件:dsprov.dll
命名空间:root\directory\ldap
作用:将Active Directory 对象映射到 WMI。
2. 事件日志提供程序
链接库文件:ntevt.dll
命名空间:root\cimv2
作用:管理 Windows 事件日志,例如,读取、备份、清除、复制、删除、监视、重命名、压缩、解压缩和更改事件日志设置。
3. 注册表提供程序
链接库文件:stdprov.dll
命名空间:root\default
作用:读取、写入、枚举、监视、创建、删除注册表项和值。
4. Win32提供程序
链接库文件:cimwin32.dll
命名空间:root\cimv2
作用:提供关于计算机、磁盘、外围设备、文件、文件夹、文件系统、网络组件、操作系统、打印机、进程、安全性、服务、共享、SAM 用户及组,以及更多资源的信息。
5. Windows安装程序提供程序
链接库文件:msiprov.dll
命名空间:root\cimv2
作用:提供对已安装软件信息的访问。
以上可以看出WMI中的类被分组到不同的命名空间中,所以我们在调用相应的程序库时要注意引入对应的命名空间。我们今天用到的库就是cimwin32.dll库(第4个)。
通过WMI(windows管理规范)接口编程来实现系统硬件信息的获取方便多了,使用起来是相当的方便,但是速度有点慢。
// 头文件
#pragma once
#include <atlbase.h>
#include <afxpriv.h>
#include <WbemIdl.h>
#pragma comment(lib,"WbemUuid.lib")
class CWmiInfo
{
public:
CWmiInfo(void);
~CWmiInfo(void);
public:
HRESULT InitWmi(); //初始化WMI
HRESULT ReleaseWmi(); //释放
BOOL GetSingleItemInfo(CString,CString,CString&);
BOOL GetGroupItemInfo(CString,CString[],int,CString&);
private:
void VariantToString(const LPVARIANT,CString &) const;//将Variant类型的变量转换为CString
private:
IEnumWbemClassObject* m_pEnumClsObj;
IWbemClassObject* m_pWbemClsObj;
IWbemServices* m_pWbemSvc;
IWbemLocator* m_pWbemLoc;
};
#include "StdAfx.h"
#include "WmiInfo.h"
CWmiInfo::CWmiInfo(void)
{
m_pWbemSvc=NULL;
m_pWbemLoc=NULL;
m_pEnumClsObj = NULL;
}
// 源文件
CWmiInfo::~CWmiInfo(void)
{
m_pWbemSvc=NULL;
m_pWbemLoc=NULL;
m_pEnumClsObj = NULL;
}
HRESULT CWmiInfo::InitWmi()
{
HRESULT hr;
// 初始化COM组件
//初始化COM
hr=::CoInitializeEx(0,COINIT_MULTITHREADED);
if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)
{
//设置进程的安全级别,(调用COM组件时在初始化COM之后要调用CoInitializeSecurity设置进程安全级别,否则会被系统识别为病毒)
hr=CoInitializeSecurity(NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_PKT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL);
//VERIFY(SUCCEEDED(hr));
// 创建一个WMI命名空间连接
//创建一个CLSID_WbemLocator对象
hr=CoCreateInstance(CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
(LPVOID*)&m_pWbemLoc);
VERIFY(SUCCEEDED(hr));
//使用m_pWbemLoc连接到"root\cimv2"并设置m_pWbemSvc的指针
hr=m_pWbemLoc->ConnectServer(CComBSTR(L"ROOT\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&m_pWbemSvc);
VERIFY(SUCCEEDED(hr));
// 设置WMI连接的安全性
hr=CoSetProxyBlanket(m_pWbemSvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE);
VERIFY(SUCCEEDED(hr));
}
return(hr);
}
HRESULT CWmiInfo::ReleaseWmi()
{
HRESULT hr;
if (NULL != m_pWbemSvc)
{
hr=m_pWbemSvc->Release();
}
if (NULL != m_pWbemLoc)
{
hr=m_pWbemLoc->Release();
}
if (NULL != m_pEnumClsObj)
{
hr=m_pEnumClsObj->Release();
}
::CoUninitialize();
return(hr);
}
BOOL CWmiInfo::GetSingleItemInfo(CString ClassName,CString ClassMember,CString &chRetValue)
{
USES_CONVERSION;
CComBSTR query("SELECT * FROM ");
VARIANT vtProp;
ULONG uReturn;
HRESULT hr;
BOOL bRet = FALSE;
if (NULL != m_pWbemSvc)
{
//查询类ClassName中的所有字段,保存到m_pEnumClsObj中
query+=CComBSTR(ClassName);
hr=m_pWbemSvc->ExecQuery(CComBSTR("WQL"),query,WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,
0,&m_pEnumClsObj);
if (SUCCEEDED(hr))
{
//初始化vtProp值
VariantInit(&vtProp);
uReturn=0;
//返回从当前位置起的第一个对象到m_pWbemClsObj中
hr=m_pEnumClsObj->Next(WBEM_INFINITE,1,&m_pWbemClsObj,&uReturn);
if(SUCCEEDED(hr)&&uReturn>0)
{
//从m_pWbemClsObj中找出ClassMember标识的成员属性值,并保存到vtProp变量中
hr=m_pWbemClsObj->Get(CComBSTR(ClassMember),0,&vtProp,0,0);
if (SUCCEEDED(hr))
{
VariantToString(&vtProp,chRetValue);
VariantClear(&vtProp);//清空vtProp
bRet = TRUE;
}
}
}
}
if(NULL != m_pEnumClsObj)
{
hr=m_pEnumClsObj->Release();
m_pEnumClsObj = NULL;
}
if(NULL != m_pWbemClsObj)
{
hr=m_pWbemClsObj->Release();
m_pWbemClsObj = NULL;
}
return bRet;
}
BOOL CWmiInfo::GetGroupItemInfo(CString ClassName,CString ClassMember[],int n,CString &chRetValue)
{
USES_CONVERSION;
CComBSTR query("SELECT * FROM ");
CString result,info;
VARIANT vtProp;
ULONG uReturn;
HRESULT hr;
int i;
BOOL bRet = FALSE;
if (NULL != m_pWbemSvc)
{
query+=CComBSTR(ClassName);
hr=m_pWbemSvc->ExecQuery(CComBSTR("WQL"),query,WBEM_FLAG_FORWARD_ONLY|WBEM_FLAG_RETURN_IMMEDIATELY,0,&m_pEnumClsObj);
if (SUCCEEDED(hr))
{
VariantInit(&vtProp); //初始化vtProp变量
if(m_pEnumClsObj)
{
Sleep(10);
uReturn=0;
hr=m_pEnumClsObj->Next(WBEM_INFINITE,1,&m_pWbemClsObj,&uReturn);
if (SUCCEEDED(hr) &&uReturn>0)
{
for(i=0;i<n;++i)
{
hr=m_pWbemClsObj->Get(CComBSTR(ClassMember[i]),0,&vtProp,0,0);
if (SUCCEEDED(hr))
{
VariantToString(&vtProp,info);
chRetValue+=info+_T("\t");
VariantClear(&vtProp);
bRet = TRUE;
}
}
chRetValue+=_T("\r\n");
}
}
}
}
if(NULL != m_pEnumClsObj)
{
hr=m_pEnumClsObj->Release();
m_pEnumClsObj=NULL;
}
if(NULL != m_pWbemClsObj)
{
hr=m_pWbemClsObj->Release();
m_pWbemClsObj=NULL;
}
return bRet;
}
void CWmiInfo::VariantToString(const LPVARIANT pVar,CString &chRetValue) const
{
USES_CONVERSION;
CComBSTR HUGEP* pBstr;
BYTE HUGEP* pBuf;
LONG low,high,i;
HRESULT hr;
switch(pVar->vt)
{
case VT_BSTR:
{
chRetValue=W2T(pVar->bstrVal);
}
break;
case VT_BOOL:
{
if(VARIANT_TRUE==pVar->boolVal)
chRetValue="是";
else
chRetValue="否";
}
break;
case VT_I4:
{
chRetValue.Format(_T("%d"),pVar->lVal);
}
break;
case VT_UI1:
{
chRetValue.Format(_T("%d"),pVar->bVal);
}
break;
case VT_UI4:
{
chRetValue.Format(_T("%d"),pVar->ulVal);
}
break;
case VT_BSTR|VT_ARRAY:
{
hr=SafeArrayAccessData(pVar->parray,(void HUGEP**)&pBstr);
hr=SafeArrayUnaccessData(pVar->parray);
chRetValue=W2T(pBstr->m_str);
}
break;
case VT_I4|VT_ARRAY:
{
SafeArrayGetLBound(pVar->parray,1,&low);
SafeArrayGetUBound(pVar->parray,1,&high);
hr=SafeArrayAccessData(pVar->parray,(void HUGEP**)&pBuf);
hr=SafeArrayUnaccessData(pVar->parray);
CString strTmp;
high=min(high,MAX_PATH*2-1);
for(i=low;i<=high;++i)
{
strTmp.Format(_T("%02X"),pBuf[i]);
chRetValue+=strTmp;
}
}
break;
default:
break;
}
}
上面就是一个WMI的封装,返回参数均以CString类型返回调用顺序:
- 实例化一个CWmiInfo类对象;
- 调用InitWmi()函数初始化COM组件等。函数中有说明必须调用InitWmi初始化WMI组件,否则所有调用都无意义。
- 调用GetXXX方法获取你要获取的字段值。
- 最后使用完CWmiInfo类对象之后一定要记得调用ReleaseWmi()函数来释放资源。
调用说明:
GetSingleItemInfo()
- 第一个参数为要查询的类名如"Win32_Processor"表示CPU类;
- 第二个参数表示要查的类的成员,如"Caption"表示查询CPU的名称;
- 第三个参数表示返回值。
故可以这样调用:
CString strRetValue;
GetSingleItemInfo(_T("Win32_Processor"),_T("Caption"),strRetValue);
一样调用成功后即可从strRetValue参数中读取出CPU的名称。
GetGroupItemInfo 函数与GetSingleItemInfo类似,不同的是GetSingleItemInfo一次只能查一个类成员而GetGroupItemInfo一次可以查询一个类的多个成员。GetGroupItemInfo的第三个参数表示要查询的类成员的个数,即第二个参数CString数组的大小可以这样调用:
CString strRetValue;CString [] strClassMem = {_T("Caption"),_T("CurrentClockSpeed"),_T("DeviceID"), _T("Manufacturer"),_T("Manufacturer")};GetGroupItemInfo(_T("Win32_Processor"),strClassMem,5,strRetValue);
下面给大家列出一些常用的类名:
// 硬件信息类名
Win32_Processor, // CPU 处理器
Win32_PhysicalMemory, // 物理内存条
Win32_Keyboard, // 键盘
Win32_PointingDevice, // 点输入设备,包括鼠标。
Win32_FloppyDrive, // 软盘驱动器
Win32_DiskDrive, // 硬盘驱动器
Win32_CDROMDrive, // 光盘驱动器
Win32_BaseBoard, // 主板
Win32_BIOS, // BIOS 芯片
Win32_ParallelPort, // 并口
Win32_SerialPort, // 串口
Win32_SerialPortConfiguration, // 串口配置
Win32_SoundDevice, // 多媒体设置,一般指声卡。
Win32_SystemSlot, // 主板插槽 (ISA & PCI & AGP)
Win32_USBController, // USB 控制器
Win32_NetworkAdapter, // 网络适配器
Win32_NetworkAdapterConfiguration, // 网络适配器设置
Win32_Printer, // 打印机
Win32_PrinterConfiguration, // 打印机设置
Win32_PrintJob, // 打印机任务
Win32_TCPIPPrinterPort, // 打印机端口
Win32_POTSModem, // MODEM
Win32_POTSModemToSerialPort, // MODEM 端口
Win32_DesktopMonitor, // 显示器
Win32_DisplayConfiguration, // 显卡
Win32_DisplayControllerConfiguration, // 显卡设置
Win32_VideoController, // 显卡细节。
Win32_VideoSettings, // 显卡支持的显示模式。
// 操作系统
Win32_TimeZone, // 时区
Win32_SystemDriver, // 驱动程序
Win32_DiskPartition, // 磁盘分区
Win32_LogicalDisk, // 逻辑磁盘
Win32_LogicalDiskToPartition, // 逻辑磁盘所在分区及始末位置。
Win32_LogicalMemoryConfiguration, // 逻辑内存配置
Win32_PageFile, // 系统页文件信息
Win32_PageFileSetting, // 页文件设置
Win32_BootConfiguration, // 系统启动配置
Win32_ComputerSystem, // 计算机信息简要
Win32_OperatingSystem, // 操作系统信息
Win32_StartupCommand, // 系统自动启动程序
Win32_Service, // 系统安装的服务
Win32_Group, // 系统管理组
Win32_GroupUser, // 系统组帐号
Win32_UserAccount, // 用户帐号
Win32_Process, // 系统进程
Win32_Thread, // 系统线程
Win32_Share, // 共享
Win32_NetworkClient, // 已安装的网络客户端
Win32_NetworkProtocol, // 已安装的网络协议