使用wmi获取电脑信息(bios序列号、磁盘序列号、cpu序列号、主板序列号、网卡序列号等)
wmi_api.h
#ifndef WMI_API_H_ #define WMI_API_H_ #include <string> #include <wbemidl.h> #include <wrl/client.h> // Enumeration of errors that can arise when connecting to a WMI server and // running a query. // Do not change ordering. This enum is captured as `WmiQueryError` in // enums.xml. enum class WmiError { kNoError = 0, kFailedToCreateInstance, kFailedToConnectToWMI, kFailedToSetSecurityBlanket, kFailedToExecWMIQuery, kMaxValue = kFailedToExecWMIQuery }; // String used to connect to the CIMV2 WMI server. extern const wchar_t kCimV2ServerName[]; // String used to connect to the SecurityCenter2 WMI server. extern const wchar_t kSecurityCenter2ServerName[]; // Connects to a server named `server_name` on the local computer through COM // and run the given WQL `query`. Sets `enumerator` with the values returned by // that `query`. Will return a WmiError value if an error occurs, else returns // absl::nullopt. WmiError RunWmiQuery( const std::wstring& server_name, const std::wstring& query, Microsoft::WRL::ComPtr<IEnumWbemClassObject>* enumerator); // Creates an instance of the WMI service connected to the local computer and // returns its COM interface. If |set_blanket| is set to true, the basic COM // security blanket is applied to the returned interface. This is almost // always desirable unless you set the parameter to false and apply a custom // COM security blanket. // Returns true if succeeded and |wmi_services|: the pointer to the service. bool CreateLocalWmiConnection( bool set_blanket, Microsoft::WRL::ComPtr<IWbemServices>* wmi_services); // Creates a WMI method using from a WMI class named |class_name| that // contains a method named |method_name|. Only WMI classes that are CIM // classes can be created using this function. // Returns true if succeeded and |class_instance| returns a pointer to the // WMI method that you can fill with parameter values using SetParameter. bool CreateWmiClassMethodObject( IWbemServices* wmi_services, std::wstring class_name, std::wstring method_name, Microsoft::WRL::ComPtr<IWbemClassObject>* class_instance); // Creates a new process from |command_line|. The advantage over CreateProcess // is that it allows you to always break out from a Job object that the caller // is attached to even if the Job object flags prevent that. // Returns true and the process id in process_id if the process is launched // successful. False otherwise. // Note that a fully qualified path must be specified in most cases unless // the program is not in the search path of winmgmt.exe. // Processes created this way are children of wmiprvse.exe and run with the // caller credentials. // More info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx bool WmiLaunchProcess(const std::wstring& command_line, int* process_id); // An encapsulation of information retrieved from the 'Win32_ComputerSystem' and // 'Win32_Bios' WMI classes; see : // https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-computersystem // https://docs.microsoft.com/en-us/windows/desktop/CIMWin32Prov/win32-systembios // Note that while model and manufacturer can be obtained through WMI, it is // more efficient to obtain them via SysInfo::GetHardwareInfo() which uses the // registry. class WmiComputerSerialNumber { public: typedef struct { std::wstring id; std::wstring sql; std::wstring key; }INFO; INFO query_arr[7] = { {L"hd",L"SELECT * FROM Win32_DiskDrive WHERE (SerialNumber IS NOT NULL) AND (MediaType LIKE 'Fixed hard disk%')",L"SerialNumber"},// 硬盘序列号 {L"hdn",L"SELECT * FROM Win32_PhysicalMedia WHERE (SerialNumber IS NOT NULL)",L"SerialNumber"},// 硬盘序列号 {L"board_sn",L"SELECT * FROM Win32_BaseBoard WHERE (SerialNumber IS NOT NULL)",L"SerialNumber"},// 主板序列号 {L"board_type",L"SELECT * FROM Win32_BaseBoard WHERE (Product IS NOT NULL)",L"Product"},// 主板型号 {L"cpu",L"SELECT * FROM Win32_Processor WHERE (ProcessorId IS NOT NULL)",L"ProcessorId"},// 处理器ID {L"bios",L"SELECT * FROM Win32_BIOS WHERE (SerialNumber IS NOT NULL)",L"SerialNumber"},// BIOS序列号 {L"mac",L"SELECT * FROM Win32_NetworkAdapter WHERE (MACAddress IS NOT NULL) AND (NOT (PNPDeviceID LIKE 'ROOT%'))",L"MACAddress"}// 网卡当前MAC地址 }; std::wstring Bios(); std::wstring DiskDrive(); std::wstring Get(const std::wstring& id); private: void PopulateValue( const Microsoft::WRL::ComPtr<IEnumWbemClassObject>& enumerator_bios, const std::wstring& key, std::wstring* output); }; #endif // WMI_API_H_
wmi_api.cpp
// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "wmi_api.h" #include <windows.h> #include <objbase.h> #include <stdint.h> #include <utility> #pragma comment(lib, "wbemuuid.lib") using Microsoft::WRL::ComPtr; const wchar_t kCimV2ServerName[] = L"ROOT\\CIMV2"; const wchar_t kSecurityCenter2ServerName[] = L"ROOT\\SecurityCenter2"; namespace { void TerminateBecauseOutOfMemory(size_t size) { const DWORD kOomExceptionCode = 0xe0000008; ULONG_PTR exception_args[] = { size }; ::RaiseException(kOomExceptionCode, EXCEPTION_NONCONTINUABLE, std::size(exception_args), exception_args); // Safety check, make sure process exits here. _exit(kOomExceptionCode); } BSTR AllocBstrOrDie(std::wstring non_bstr) { BSTR result = ::SysAllocStringLen(non_bstr.data(), static_cast<UINT>(non_bstr.length())); if (!result) TerminateBecauseOutOfMemory((non_bstr.length() + 1) * sizeof(wchar_t)); return result; } BSTR AllocBstrBytesOrDie(size_t bytes) { BSTR result = ::SysAllocStringByteLen(nullptr, static_cast<UINT>(bytes)); if (!result) TerminateBecauseOutOfMemory(bytes + sizeof(wchar_t)); return result; } // Manages a BSTR string pointer. // The class interface is based on unique_ptr. class ScopedBstr { public: ScopedBstr(std::wstring non_bstr) : bstr_(AllocBstrOrDie(non_bstr)) { } ScopedBstr(const ScopedBstr&) = delete; ScopedBstr& operator=(const ScopedBstr&) = delete; ~ScopedBstr() { static_assert(sizeof(ScopedBstr) == sizeof(BSTR), "ScopedBstrSize"); ::SysFreeString(bstr_); } BSTR Get() const { return bstr_; } // Give ScopedBstr ownership over an already allocated BSTR or null. // If you need to allocate a new BSTR instance, use |allocate| instead. void Reset(BSTR bstr) { if (bstr != bstr_) { // SysFreeString handles null properly. ::SysFreeString(bstr_); bstr_ = bstr; } } // Releases ownership of the BSTR to the caller. BSTR Release() { BSTR bstr = bstr_; bstr_ = nullptr; return bstr; } // Swap values of two ScopedBstr's. void Swap(ScopedBstr& bstr2) { BSTR tmp = bstr_; bstr_ = bstr2.bstr_; bstr2.bstr_ = tmp; } // Retrieves the pointer address. // Used to receive BSTRs as out arguments (and take ownership). // The function DCHECKs on the current value being null. // Usage: GetBstr(bstr.Receive()); BSTR* Receive() { return &bstr_; } // Creates a new BSTR from a 16-bit C-style string. // // If you already have a BSTR and want to transfer ownership to the // ScopedBstr instance, call |reset| instead. // // Returns a pointer to the new BSTR. BSTR Allocate(std::wstring str) { Reset(AllocBstrOrDie(str)); return bstr_; } // Allocates a new BSTR with the specified number of bytes. // Returns a pointer to the new BSTR. BSTR AllocateBytes(size_t bytes) { Reset(AllocBstrBytesOrDie(bytes)); return bstr_; } // Sets the allocated length field of the already-allocated BSTR to be // |bytes|. This is useful when the BSTR was preallocated with e.g. // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and then // not all the bytes are being used. // // Note that if you want to set the length to a specific number of // characters, you need to multiply by sizeof(wchar_t). Oddly, there's no // public API to set the length, so we do this ourselves by hand. // // NOTE: The actual allocated size of the BSTR MUST be >= bytes. That // responsibility is with the caller. void SetByteLen(size_t bytes) { uint32_t* data = reinterpret_cast<uint32_t*>(bstr_); data[-1] = static_cast<uint32_t>(bytes); } // Returns number of chars in the BSTR. size_t Length() const { return ::SysStringLen(bstr_); } // Returns the number of bytes allocated for the BSTR. size_t ByteLength() const { return ::SysStringByteLen(bstr_); } // Forbid comparison of ScopedBstr types. You should never have the same // BSTR owned by two different scoped_ptrs. bool operator==(const ScopedBstr& bstr2) const = delete; bool operator!=(const ScopedBstr& bstr2) const = delete; protected: BSTR bstr_ = nullptr; }; // Instantiates `wmi_services` with a connection to `server_name` in WMI. Will // set a security blanket if `set_blanket` is true. WmiError CreateLocalWmiConnection( const std::wstring& server_name, bool set_blanket, ComPtr<IWbemServices>* wmi_services) { ComPtr<IWbemLocator> wmi_locator; HRESULT hr = ::CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&wmi_locator)); if (FAILED(hr)) return WmiError::kFailedToCreateInstance; ComPtr<IWbemServices> wmi_services_r; hr = wmi_locator->ConnectServer(ScopedBstr(server_name).Get(), nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wmi_services_r); if (FAILED(hr)) return WmiError::kFailedToConnectToWMI; if (set_blanket) { hr = ::CoSetProxyBlanket(wmi_services_r.Get(), RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); if (FAILED(hr)) return WmiError::kFailedToSetSecurityBlanket; } *wmi_services = std::move(wmi_services_r); return WmiError::kNoError; } // Runs `query` through `wmi_services` and sets the results' `enumerator`. bool TryRunQuery(const std::wstring& query, const ComPtr<IWbemServices>& wmi_services, ComPtr<IEnumWbemClassObject>* enumerator) { ScopedBstr query_language(L"WQL"); ScopedBstr query_bstr(query); ComPtr<IEnumWbemClassObject> enumerator_r; HRESULT hr = wmi_services->ExecQuery( query_language.Get(), query_bstr.Get(), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &enumerator_r); if (FAILED(hr)) return false; *enumerator = std::move(enumerator_r); return true; } class ScopedVariant { public: // Default constructor. ScopedVariant() { // This is equivalent to what VariantInit does, but less code. var_.vt = VT_EMPTY; } // Constructor to create a new VT_BSTR VARIANT. // NOTE: Do not pass a BSTR to this constructor expecting ownership to // be transferred ScopedVariant(const wchar_t* str) { var_.vt = VT_EMPTY; Set(str); } void Set(const wchar_t* str) { var_.vt = VT_BSTR; var_.bstrVal = ::SysAllocString(str); } inline VARTYPE type() const { return var_.vt; } // A hack to pass a pointer to the variant where the accepting // function treats the variant as an input-only, read-only value // but the function prototype requires a non const variant pointer. // There's no DCHECK or anything here. Callers must know what they're doing. VARIANT* AsInput() const { // The nature of this function is const, so we declare // it as such and cast away the constness here. return const_cast<VARIANT*>(&var_); } // Allows const access to the contained variant without DCHECKs etc. // This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to // work properly but still doesn't allow modifications since we want control // over that. const VARIANT* ptr() const { return &var_; } // Retrieves the pointer address. // Used to receive a VARIANT as an out argument (and take ownership). // The function DCHECKs on the current value being empty/null. // Usage: GetVariant(var.receive()); VARIANT* Receive() { return &var_; } protected: VARIANT var_; }; } // namespace WmiError RunWmiQuery(const std::wstring& server_name, const std::wstring& query, ComPtr<IEnumWbemClassObject>* enumerator) { ComPtr<IWbemServices> wmi_services; WmiError error = CreateLocalWmiConnection(server_name, /*set_blanket=*/true, &wmi_services); if (error != WmiError::kNoError) return error; if (!TryRunQuery(query, wmi_services, enumerator)) return WmiError::kFailedToExecWMIQuery; return WmiError::kNoError; } bool CreateLocalWmiConnection(bool set_blanket, ComPtr<IWbemServices>* wmi_services) { WmiError error = CreateLocalWmiConnection(kCimV2ServerName, set_blanket, wmi_services); return error != WmiError::kNoError; } bool CreateWmiClassMethodObject(IWbemServices* wmi_services, std::wstring class_name, std::wstring method_name, ComPtr<IWbemClassObject>* class_instance) { // We attempt to instantiate a COM object that represents a WMI object plus // a method rolled into one entity. ScopedBstr b_class_name(class_name); ScopedBstr b_method_name(method_name); ComPtr<IWbemClassObject> class_object; HRESULT hr; hr = wmi_services->GetObject(b_class_name.Get(), 0, nullptr, &class_object, nullptr); if (FAILED(hr)) return false; ComPtr<IWbemClassObject> params_def; hr = class_object->GetMethod(b_method_name.Get(), 0, ¶ms_def, nullptr); if (FAILED(hr)) return false; if (!params_def.Get()) { // You hit this special case if the WMI class is not a CIM class. MSDN // sometimes tells you this. Welcome to WMI hell. return false; } hr = params_def->SpawnInstance(0, &(*class_instance)); return SUCCEEDED(hr); } // The code in Launch() basically calls the Create Method of the Win32_Process // CIM class is documented here: // http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx // NOTE: The documentation for the Create method suggests that the ProcessId // parameter and return value are of type uint32_t, but when we call the method // the values in the returned out_params, are VT_I4, which is int32_t. bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) { ComPtr<IWbemServices> wmi_local; if (!CreateLocalWmiConnection(true, &wmi_local)) return false; static constexpr wchar_t class_name[] = L"Win32_Process"; static constexpr wchar_t method_name[] = L"Create"; ComPtr<IWbemClassObject> process_create; if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name, &process_create)) { return false; } ScopedVariant b_command_line(command_line.c_str()); if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(), 0))) { return false; } ComPtr<IWbemClassObject> out_params; HRESULT hr = wmi_local->ExecMethod( ScopedBstr(class_name).Get(), ScopedBstr(method_name).Get(), 0, nullptr, process_create.Get(), &out_params, nullptr); if (FAILED(hr)) return false; // We're only expecting int32_t or uint32_t values, so no need for // ScopedVariant. VARIANT ret_value = { {{VT_EMPTY}} }; hr = out_params->Get(L"ReturnValue", 0, &ret_value, nullptr, nullptr); if (FAILED(hr) || V_I4(&ret_value) != 0) return false; VARIANT pid = { {{VT_EMPTY}} }; hr = out_params->Get(L"ProcessId", 0, &pid, nullptr, nullptr); if (FAILED(hr) || V_I4(&pid) == 0) return false; if (process_id) *process_id = V_I4(&pid); return true; } std::wstring WmiComputerSerialNumber::Bios() { return Get(L"bios"); } std::wstring WmiComputerSerialNumber::DiskDrive() { return Get(L"hd"); } std::wstring WmiComputerSerialNumber::Get(const std::wstring& id) { std::wstring sql, key; for (const auto& iter : query_arr) { if (iter.id == id) { sql = iter.sql; key = iter.key; } } ComPtr<IEnumWbemClassObject> enumerator_bios; WmiError error = RunWmiQuery(kCimV2ServerName, sql.c_str(), &enumerator_bios); if (error != WmiError::kNoError) return std::wstring(); std::wstring value; PopulateValue(enumerator_bios, key.c_str(), &value); return value; } void WmiComputerSerialNumber::PopulateValue( const ComPtr<IEnumWbemClassObject>& enumerator_bios, const std::wstring& key, std::wstring* output) { ComPtr<IWbemClassObject> class_obj; ULONG items_returned = 0; HRESULT hr = enumerator_bios->Next(WBEM_INFINITE, 1, &class_obj, &items_returned); if (FAILED(hr) || !items_returned) return; ScopedVariant serial_number; hr = class_obj->Get(key.c_str(), 0, serial_number.Receive(), nullptr, nullptr); if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) { output->assign(V_BSTR(serial_number.ptr()), ::SysStringLen(V_BSTR(serial_number.ptr()))); } }
demo.cpp
#include <iostream> #include "wmi_api.h" int main() { HRESULT hres; //初始化COM环境 hres = ::CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) return 0;//初始化COM环境失败 //设置COM安全等级 hres = ::CoInitializeSecurity( NULL, -1, // COM 认证 NULL, // 认证服务 NULL, // 保留 RPC_C_AUTHN_LEVEL_DEFAULT, // 默认认证 RPC_C_IMP_LEVEL_IMPERSONATE, // 默认模拟 NULL, // 认证信息 EOAC_NONE, // 附加能力标志 NULL // 保留 ); WmiComputerSerialNumber wcsn; std::wcout << wcsn.Bios().c_str() << std::endl; std::wcout << wcsn.DiskDrive().c_str() << std::endl; ::CoUninitialize(); system("pause"); }