深入Windows开发--PC硬件信息获取

引言 


因为项目需要对单机版进行加密,进而需要要获取客户机一些硬件信息。现在把当时用到的一些方法列出来。

这里主要获取的是都是比较唯一的信息,不会随着系统重装而变化的,比如CPU ID、主板SN码、MAC地址(虽然MAC地址并不是绝对唯一,可以通过软件修改。不能选MAC的guid,它会随系统重装而变。)、显卡名称。

将这些信息按规则拼成字符串后再进行加密。

方法一:WMI


本文只关注怎么使用WMI技术获取硬件信息,具体它是个什么并未深究。摘一段百度的“硬解释”如下:

WMI(Windows Management Instrumentation,Windows 管理规范)是一项核心的 Windows 管理技术;用户可以使用 WMI 管理本地和远程计算机。

下面是获取主板SN码和显卡名称的代码:

HRESULT GetBaseBoardSNAndVideoInfos(std::string& o_strBaseBoard, std::list<std::string>& o_lstVideos)
{
	HRESULT hRes = S_OK;

	// Initialize COM.
	hRes = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
	if (FAILED(hRes))
	{
		return hRes;
	}

	// Initialize 
	hRes = CoInitializeSecurity(
		NULL,
		-1,      // COM negotiates service                  
		NULL,    // Authentication services
		NULL,    // Reserved
		RPC_C_AUTHN_LEVEL_DEFAULT,    // authentication
		RPC_C_IMP_LEVEL_IMPERSONATE,  // Impersonation
		NULL,             // Authentication info 
		EOAC_NONE,        // Additional capabilities
		NULL              // Reserved
	);


	if (FAILED(hRes))
	{
		if (hRes != RPC_E_TOO_LATE)
		{
			CoUninitialize();
			return hRes;
		}
		
	}

	// Obtain the initial locator to Windows Management
	// on a particular host computer.
	IWbemLocator *pLoc = 0;

	hRes = CoCreateInstance(
		CLSID_WbemLocator,
		0,
		CLSCTX_INPROC_SERVER,
		IID_IWbemLocator, (LPVOID *)&pLoc);

	if (FAILED(hRes))
	{
		CoUninitialize();
		return hRes;
	}

	IWbemServices *pSvc = 0;

	// Connect to the root\cimv2 namespace with the
	// current user and obtain pointer pSvc
	// to make IWbemServices calls.

	hRes = pLoc->ConnectServer(
		_bstr_t(L"ROOT\\CIMV2"), // WMI namespace
		NULL,                    // User name
		NULL,                    // User password
		0,                       // Locale
		NULL,                    // Security flags                 
		0,                       // Authority       
		0,                       // Context object
		&pSvc                    // IWbemServices proxy
	);

	if (FAILED(hRes))
	{
		CoUninitialize();
		return 1;                // Program has failed.
	}

	// Set the IWbemServices proxy so that impersonation
	// of the user (client) occurs.
	hRes = CoSetProxyBlanket(
		pSvc,                         // the proxy to set
		RPC_C_AUTHN_WINNT,            // authentication service
		RPC_C_AUTHZ_NONE,             // authorization service
		NULL,                         // Server principal name
		RPC_C_AUTHN_LEVEL_CALL,       // authentication level
		RPC_C_IMP_LEVEL_IMPERSONATE,  // impersonation level
		NULL,                         // client identity 
		EOAC_NONE                     // proxy capabilities     
	);

	if (FAILED(hRes))
	{
		pSvc->Release();
		pLoc->Release();
		CoUninitialize();
		return hRes;
	}


	// Use the IWbemServices pointer to make requests of WMI. 
	// Make requests here:

	// For example, query for all the running processes
	IEnumWbemClassObject* pVideoEnumerator = NULL;
	hRes = pSvc->ExecQuery(
		bstr_t("WQL"),
		bstr_t("SELECT * FROM Win32_VideoController"),
		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
		NULL,
		&pVideoEnumerator);

	ULONG uReturn = 0;
	if (SUCCEEDED(hRes))
	{
		IWbemClassObject *pclsVideoObj;

		while (pVideoEnumerator)
		{
			hRes = pVideoEnumerator->Next(WBEM_INFINITE, 1,
				&pclsVideoObj, &uReturn);
			if (0 == uReturn)
			{
				break;
			}

			VARIANT vtProp;

			// Get the value of the Name property
			hRes = pclsVideoObj->Get(L"Description", 0, &vtProp, 0, 0);
			std::string strDes = (_bstr_t)vtProp.bstrVal;
			o_lstVideos.push_back(strDes);
			/*hRes = pclsVideoObj->Get(L"DeviceID", 0, &vtProp3, 0, 0);
			wcout << "DeviceID: " << vtProp3.uintVal << endl;*/

			VariantClear(&vtProp);
		}
	}

	uReturn = 0;
	IEnumWbemClassObject* pBordEnumerator = NULL;
	hRes = pSvc->ExecQuery(
		bstr_t("WQL"),
		bstr_t("SELECT * FROM Win32_BaseBoard"),
		WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
		NULL,
		&pBordEnumerator);

	if (SUCCEEDED(hRes))
	{
		IWbemClassObject *pclsBordObj;

		while (pBordEnumerator)
		{
			hRes = pBordEnumerator->Next(WBEM_INFINITE, 1,
				&pclsBordObj, &uReturn);
			if (0 == uReturn)
			{
				break;
			}

			VARIANT vtProp;

			// Get the value of the Name property
			hRes = pclsBordObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
			std::string strBordSN = (_bstr_t)vtProp.bstrVal;
			o_strBaseBoard = strBordSN;
			VariantClear(&vtProp);
		}
	}

	// Cleanup

	pSvc->Release();
	pLoc->Release();
	CoUninitialize();

	return hRes;
}

代码中注释的已经很明确了,无需再多说什么,但是有几个点需要注意一下:

  • 需要引入头文件、添加lib
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
  • CoInitializeSecurity在同一线程内不可连续多次调用,所以这里加了hRes != RPC_E_TOO_LATE的判断,以实现多次调用GetBaseBoardSNAndVideoInfos。我这个函数其实写的并不是很合理,对于这个获取硬件信息的小exe而言,CoInitializeEx、CoInitializeSecurity、CoUninitialize都应该只调用一次,不应多次调用。也就是说这三个函数应该在你程序的初始化、退出时调用,而非每一个获取硬件信息的子函数中调用。
  • 核心代码在于wmi语句SELECT * FROM Win32_VideoController、SELECT * FROM Win32_BaseBoardExecQuery完语句后获取到IWbemClassObject,再去Get详细子信息。
  • Win32_VideoController    显卡信息

       Win32_BaseBoard    主板信息

       Win32_NetworkAdapter    网卡信息    其下MACAddress即为MAC地址

       Win32_Processor   网卡信息    其下SerialNumber即为CPU ID

       具体想要哪一个,百度一下就行,网上挺全的。

再想详细了解的可以参考下这2个链接:

https://blog.csdn.net/wangshubo1989/article/details/51855895

https://www.cnblogs.com/yunsicai/articles/2986741.html

方法二:其他方法


以下这些其他方法是博主最初做项目时并不知道WMI,用的其他野路子,感觉相对WMI没有那么标准化、正规化。

获取CPU ID

//to get CPUID
#include "intrin.h"

int GetCpuId (std::string& o_sCpuId)
{
	int nCPUInfo[4];
	__cpuid(nCPUInfo, 1);
	char chCPU[255];
	sprintf_s(chCPU, 255, "%08X%08X%08X", nCPUInfo[0], nCPUInfo[2], nCPUInfo[3]);	//do not use nCPUInfo[1], it might be different in different process, why?

	o_sCpuId = chCPU;

	return 1;
}

获取MAC地址

//to get the MAC
#include <iphlpapi.h>
#pragma comment (lib, "Ws2_32.lib")
#pragma comment(lib, "IPHLPAPI.lib")

HRESULT GetAllMacAddresses(std::list<std::string>& o_lstMacs)
{
	// Allocate a 15 KB buffer to start with.
#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))


	DWORD dwRetVal = 0;

	//unsigned int i = 0;

	// Set the flags to pass to GetAdaptersAddresses
	ULONG flags = GAA_FLAG_SKIP_UNICAST|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME;

	// default to unspecified address family (both)
	ULONG family = AF_UNSPEC;

	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
	ULONG outBufLen = WORKING_BUFFER_SIZE;
	ULONG Iterations = 0;

	//step1. get all NIC info
	do {

		pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
		if (pAddresses == NULL) 
		{
			return E_OUTOFMEMORY;
		}

		dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

		if (dwRetVal == ERROR_BUFFER_OVERFLOW) 
		{
			FREE(pAddresses);
			pAddresses = NULL;
		} 
		else 
		{
			break;
		}
		Iterations++;

	} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	if (dwRetVal != NO_ERROR) 
	{
		return E_UNEXPECTED;
	}

	//step2. get all the names(which is guid)
	//Mr.W 2019/06/26
	//when the operating system is reinstalled,the guid of MAC changes 
	//but the address of MAC does not
	//so we use address instead
	PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses;
	while (pCurrAddresses) 
	{
		if((MIB_IF_TYPE_ETHERNET != pCurrAddresses->IfType) && (IF_TYPE_IEEE80211 != pCurrAddresses->IfType))
		{
			pCurrAddresses = pCurrAddresses->Next;
			continue;
		}

		pCurrAddresses->PhysicalAddress;
		char chMAC[50] = "";
		sprintf_s(chMAC, 50, "%02X-%02X-%02X-%02X-%02X-%02X",
			pCurrAddresses->PhysicalAddress[0],
			pCurrAddresses->PhysicalAddress[1],
			pCurrAddresses->PhysicalAddress[2],
			pCurrAddresses->PhysicalAddress[3],
			pCurrAddresses->PhysicalAddress[4],
			pCurrAddresses->PhysicalAddress[5]);
		std::string t_strMAC = chMAC;

		o_lstMacs.push_back(t_strMAC);

		pCurrAddresses = pCurrAddresses->Next;
	}

	o_lstMacs.sort(std::less<std::string> ());
	//sort(o_lstMacs.begin(), o_lstMacs.end(), less<string> ());

	if (pAddresses) 
	{
		FREE(pAddresses);
	}

	return S_OK;
}


上面2种方法是博主项目中实际所用,总体上更推荐方法一。    

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值