最近涉及到读取WINDOWS 系统电脑设备的各种信息,在一些特殊的PE或者简化系统中是没有WMI查询工具的,所以就自己写了个查询大部分WMI属性值的工具,免去了查网站的功夫。涉及到的方法内容就汇总做个总结。
PS:因为工作中软件基本都是我一个人开发和维护,所以一般就写一堆一个方法,最近看了很多 GitHub上的代码顿时有种写了一堆屎的感觉,啧啧,向诸位大佬学习借鉴
Win32提供程序查询主要代码
- 初始化COM库
- 获取IWbemServices实例
- WQL查询
- 获取属性名称、类型的另一种写法
- CIMTYPE 数据类型转换
- gumbo-parse解析HTML
- 小工具可执行程序看文章附件资源
- 源码下载
初始化COM库
void _print_hresult(const char *file, int line, const char *msg, HRESULT hr);
#define PRINT_HRESULT(hr, msg) _print_hresult(__FILE__, __LINE__, msg, hr)
#define IFFAILED_PRINTERROR(hr, msg) { HRESULT __hr__ = (hr); if ((__hr__ != RPC_E_TOO_LATE) &&FAILED(__hr__)) { PRINT_HRESULT(__hr__, msg); } }
static class BLL_CUSTOM_LIBRARY_EXPORT COMThreading {
public:
COMThreading() {
// Regardless of return value, you must balance each call to
// CoInitialize[Ex] with a call to CoUninitialize.
HRESULT hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
IFFAILED_PRINTERROR(hres, "Error on CoInitializeEx");
hres =CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
IFFAILED_PRINTERROR(hres, "Error on CoInitializeSecurity");
}
~COMThreading() {
CoUninitialize();
}
};
这种初始化COM库的写法真的就很经典,完全不用担心调用后没释放的问题,第一次看到的时候真的觉得大佬真是牛逼。
获取IWbemServices实例
#define PRINT_HRESULT(hr, msg) WMIOBJ_PRINT_ERROR(QString("%1 : %2").arg(msg).arg(GetError(hr)))
#define IFFAILED_PRINTERROR(hr, msg) { HRESULT __hr__ = (hr); if ((__hr__ != RPC_E_TOO_LATE) &&FAILED(__hr__)) { PRINT_HRESULT(__hr__, msg); } }
HRESULT GetWMIProxy(const CString &objectPath, CComPtr<IWbemServices> &pSvc)
{
WMIOBJ_PRINT_INFO("Execution GetWMIProxy...");
HRESULT hres;
CComPtr<IWbemLocator> pLoc;
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
);
IFFAILED_PRINTERROR(hres, "Error on CoInitializeSecurity.");
// Obtain the initial locator to WMI
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc);
IFFAILED_RETURN_RES(hres, "Error on CoCreateInstance.");
hres = pLoc->ConnectServer(
_bstr_t(objectPath), // 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 (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
IFFAILED_RETURN_RES(hres, "Error on pLoc->ConnectServer.");
// 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
);
IFFAILED_RETURN_RES(hres, "Error on CoSetProxyBlanket.");
return hres;
}
硬件方面的命名空间大多是 L"ROOT\CIMV2",宏定义可以这么用我是真的没想到,我以前还是用的简单了,
WQL查询
通过传人WQL查询对应类的所有属性,只做示例,对应参数类型是封装好的,获取返回参数的所有属性,也是有很多种方法。
void Lib_WmiObject::SQL_WQL(QString WQL)
{
QueryTitle title;
QueryData Data;
WMIOBJ_PRINT_INFO("Execution Select WQL...<b>START</b>");
COMThreading();
HRESULT hres;
CComPtr<IWbemServices> pSvc;
IFFAILED_RETURN(GetWMIProxy(WMI_NAMESPACE, pSvc), " Lib_WmiQuery : Couldn't get WMI proxy");
WMIOBJ_PRINT_INFO("WQL : "+WQL);
CComPtr<IEnumWbemClassObject> pEnumerator;
hres = pSvc->ExecQuery(bstr_t(L"WQL"),
bstr_t(WQL.toStdWString().c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
IFFAILED_RETURN(hres, "WQL ["+WQL+"] Execution failure.");
if(!pEnumerator)
WMIOBJ_PRINT_ERROR("WQL 执行失败:"+QString(GetError()));
while (pEnumerator)
{
CComPtr<IWbemClassObject> pclsObj;
ULONG uReturn = 0;
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn || pclsObj == NULL)
{
if(hr!=ERROR_INVALID_FUNCTION)
WMIOBJ_PRINT_ERROR("WQL 执行失败:"+QString(GetError(hr)));
break;
}
if(title.count()==0)
{
SAFEARRAY* pNames;
hres=pclsObj->GetNames(L"",WBEM_FLAG_ALWAYS,NULL,&pNames);
if (FAILED(hres))
{
WMIOBJ_PRINT_ERROR("获取属性名称列表失败!");
break;
}
qDebug()<<"lLbound : "<<pNames->cDims;
qDebug()<<"cbElements : "<<pNames->cbElements;
BSTR HUGEP *pbstr;
// Get a pointer to the elements of the array.
hr = SafeArrayAccessData(pNames, (void HUGEP**)&pbstr);
if (FAILED(hr))
continue;
for (int i = 0; i < pNames->rgsabound->cElements; i++)
{
BSTR bstrTemp = pbstr[i];
title.append(QString::fromWCharArray(bstrTemp));
}
SafeArrayDestroy(pNames);
}
QStringList _Data;
foreach (QString name , title) {
VARIANT vtProp;
CIMTYPE pType= CIM_EMPTY;
VariantInit(&vtProp);
hr = pclsObj->Get(name.toStdWString().c_str(), 0, &vtProp, &pType, 0);
if(FAILED(hr))
{
_Data.append("[ERROR]");
}
else
_Data.append(check_vatiant_type(vtProp,pType));
VariantClear(&vtProp);
}
Data.append(_Data);
}
UnCOMThreading();
WMIOBJ_PRINT_INFO("Execution Select WQL...<b>END</b>");
emit Out_Data(title,Data);
}
获取属性名称、类型的另一种写法
#define IFFAILED_RETURN_RES2(hr, msg) { HRESULT __hr__ = (hr); if (FAILED(__hr__)) { PRINT_HRESULT(__hr__, msg); return __hr__; } }
HRESULT EnumerateProperties(IWbemClassObject * pObject)
{
BSTR name;
VARIANT val;
CIMTYPE type;
LONG flavor;
IFFAILED_RETURN_RES2(pObject->BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY), "BeginEnumeration");
qDebug("Properties:");
while (pObject->Next(0, &name, &val, &type, &flavor) != WBEM_S_NO_MORE_DATA) {
qDebug("%-20ls type=0x%04x val.vt=0x%04x val:%s", name, type, val.vt,check_vatiant_type(val,type).toStdString().c_str());
SysFreeString(name);
VariantClear(&val);
}
return S_OK;
}
CIMTYPE 数据类型转换
之前在GitHub上看到过一个VARIANT数据类型转换的,结果忘了保存下来,自己又不想写,GitHub又打不开,所以只有用CIMTYPE 类型转换成QString了
QString Lib_WmiObject::check_vatiant_type(VARIANT Value,CIMTYPE _enum_type)
{
if(Value.vt==VT_NULL)
return "[NULL]";
switch ( _enum_type ){
case CIM_ILLEGAL ://值: 0xfff非法值。
return QString::fromWCharArray(Value.bstrVal);
case CIM_EMPTY : //值: 0 空 (null) 值。
return "[NULL]";
case CIM_SINT8 : //值: 16//8 位有符号整数。
return QString::number(Value.iVal);
case CIM_SINT16 : //值: 2//16 位带符号整数。
return QString::number(Value.iVal);
case CIM_SINT32 : //值: 3//32 位带符号整数。
return QString::number(Value.intVal);
case CIM_SINT64 : //值: 20//64 位带符号整数。
return QString::number(Value.llVal,10);
case CIM_UINT8 : //值: 17//8 位无符号整数。
return QString::number(Value.uiVal);
case CIM_UINT16 : //值: 18//16 位无符号整数。
return QString::number(Value.uintVal);
case CIM_UINT32 : //值: 19//32 位无符号整数。
return QString::number(Value.uintVal);
case CIM_UINT64 : //值: 21//64 位无符号整数。
return QString::number(Value.ullVal,10);
case CIM_REAL32 : //值: 4//32 位实数。
return QString::number(Value.intVal);
case CIM_REAL64 : //值: 5//64 位实数。
return QString::number(Value.ullVal,10);
case CIM_BOOLEAN : //值: 11//一个布尔值。
return Value.boolVal?"TRUE":"FALSE";
case CIM_STRING : // 值: 8// 字符串值。
return QString::fromWCharArray(Value.bstrVal);
case CIM_DATETIME : //值: 101//一个日期时间值。
case CIM_REFERENCE : //值: 102//另一个对象的引用 (__Path) 。
case CIM_CHAR16 : //值: 103//16 位字符值。
case CIM_OBJECT : //值: 13//Object 值 。
case CIM_FLAG_ARRAY : // 值: 0x2000//数组值。
return QString::fromWCharArray(Value.bstrVal);
default:
return QString::fromWCharArray(Value.bstrVal);
}
}
gumbo-parse解析HTML
gumbo-parse用来解析html还行。不确定是不是我方法没写对,我之前尝试过用gumbo-parse解析XML,获取不到节点,最后还是用的QT自带的XML解析工具。
* https://blog.csdn.net/chijingjing/article/details/104255763
#pragma once
#include "stdafx.h"
#include "enumtest.cpp"
#include "gumbo-parser/Selector.h"
#include "gumbo-parser/Document.h"
#include "gumbo-parser/Selection.h"
#include "gumbo-parser/Node.h"
void test_parser() {
std::string page("<h1><a>wrong link</a><a class=\"special\"\\>some link</a></h1>");
CDocument doc;
doc.parse(page.c_str());
CSelection c = doc.find("h1 a.special");
CNode node = c.nodeAt(0);
printf("Node: %s\n", node.text().c_str());
std::string content = page.substr(node.startPos(), node.endPos() - node.startPos());
printf("Node: %s\n", content.c_str());
}
void test_html() {
std::string page = "<html><div><span>1\n</span>2\n</div></html>";
CDocument doc;
doc.parse(page.c_str());
CNode pNode = doc.find("div").nodeAt(0);
std::string content = page.substr(pNode.startPos(), pNode.endPos() - pNode.startPos());
printf("Node: #%s#\n", content.c_str());
}
void test_escape() {
std::string page = "<html><div><span id=\"that's\">1\n</span>2\n</div></html>";
CDocument doc;
doc.parse(page.c_str());
CNode pNode = doc.find("span[id=\"that's\"]").nodeAt(0);
std::string content = page.substr(pNode.startPos(), pNode.endPos() - pNode.startPos());
printf("Node: #%s#\n", content.c_str());
}
int main() {
test_parser();
test_html();
test_escape();
}
————————————————
版权声明:本文为CSDN博主「cejutue」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chijingjing/article/details/104255763
小工具可执行程序看文章附件资源
源码下载
Qt开发项目案例-以及部分示例的源码下载链接