How do I get the disk drive serial number in C/C++ - c++

This has been already answered but it's a C# solution. How do I do this in C or C++?

There are a few ways to do this. You could make calls using system to get the information.
For Linux:
system("hdparm -i /dev/hda | grep -i serial");
Without using system:
static struct hd_driveid hd;
int fd;
if ((fd = open("/dev/hda", O_RDONLY | O_NONBLOCK)) < 0) {
printf("ERROR opening /dev/hda\n");
exit(1);
}
if (!ioctl(fd, HDIO_GET_IDENTITY, &hd)) {
printf("%.20s\n", hd.serial_no);
} else if (errno == -ENOMSG) {
printf("No serial number available\n");
} else {
perror("ERROR: HDIO_GET_IDENTITY");
exit(1);
}
For Windows:
system("wmic path win32_physicalmedia get SerialNumber");
Without using system (Based on Getting WMI Data ):
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT SerialNumber FROM Win32_PhysicalMedia"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);

Here is a complete solution for Windows which wraps WMI calls without using cmd or system .
So you can easily get anything from WMI including hard drive serial number.
All you have to do is to call :
getWmiQueryResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
PS. It's based on official documentation and additionally does some better error handling and cleanup. Also you might want to use this or this for creating WQL queries.
#include "stdafx.h"
#define _WIN32_DCOM
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
#include <vector>
#include <string>
#pragma comment(lib, "wbemuuid.lib")
enum class WmiQueryError {
None,
BadQueryFailure,
PropertyExtractionFailure,
ComInitializationFailure,
SecurityInitializationFailure,
IWbemLocatorFailure,
IWbemServiceConnectionFailure,
BlanketProxySetFailure,
};
struct WmiQueryResult
{
std::vector<std::wstring> ResultList;
WmiQueryError Error = WmiQueryError::None;
std::wstring ErrorDescription;
};
WmiQueryResult getWmiQueryResult(std::wstring wmiQuery, std::wstring propNameOfResultObject, bool allowEmptyItems = false) {
WmiQueryResult retVal;
retVal.Error = WmiQueryError::None;
retVal.ErrorDescription = L"";
HRESULT hres;
IWbemLocator *pLoc = NULL;
IWbemServices *pSvc = NULL;
IEnumWbemClassObject* pEnumerator = NULL;
IWbemClassObject *pclsObj = NULL;
VARIANT vtProp;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
retVal.Error = WmiQueryError::ComInitializationFailure;
retVal.ErrorDescription = L"Failed to initialize COM library. Error code : " + std::to_wstring(hres);
}
else
{
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// note: JUCE Framework users should comment this call out,
// as this does not need to be initialized to run the query.
// see https://social.msdn.microsoft.com/Forums/en-US/48b5626a-0f0f-4321-aecd-17871c7fa283/unable-to-call-coinitializesecurity?forum=windowscompatibility
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
);
if (FAILED(hres))
{
retVal.Error = WmiQueryError::SecurityInitializationFailure;
retVal.ErrorDescription = L"Failed to initialize security. Error code : " + std::to_wstring(hres);
}
else
{
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *)&pLoc);
if (FAILED(hres))
{
retVal.Error = WmiQueryError::IWbemLocatorFailure;
retVal.ErrorDescription = L"Failed to create IWbemLocator object. Error code : " + std::to_wstring(hres);
}
else
{
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
pSvc = NULL;
// 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"), // 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
);
// Connected to ROOT\\CIMV2 WMI namespace
if (FAILED(hres))
{
retVal.Error = WmiQueryError::IWbemServiceConnectionFailure;
retVal.ErrorDescription = L"Could not connect to Wbem service.. Error code : " + std::to_wstring(hres);
}
else
{
// Step 5: --------------------------------------------------
// 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
);
if (FAILED(hres))
{
retVal.Error = WmiQueryError::BlanketProxySetFailure;
retVal.ErrorDescription = L"Could not set proxy blanket. Error code : " + std::to_wstring(hres);
}
else
{
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t(wmiQuery.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
retVal.Error = WmiQueryError::BadQueryFailure;
retVal.ErrorDescription = L"Bad query. Error code : " + std::to_wstring(hres);
}
else
{
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
// VARIANT vtProp;
// Get the value of desired property
hr = pclsObj->Get(propNameOfResultObject.c_str(), 0, &vtProp, 0, 0);
if (S_OK != hr) {
retVal.Error = WmiQueryError::PropertyExtractionFailure;
retVal.ErrorDescription = L"Couldn't extract property: " + propNameOfResultObject + L" from result of query. Error code : " + std::to_wstring(hr);
}
else {
BSTR val = vtProp.bstrVal;
// Sometimes val might be NULL even when result is S_OK
// Convert NULL to empty string (otherwise "std::wstring(val)" would throw exception)
if (NULL == val) {
if (allowEmptyItems) {
retVal.ResultList.push_back(std::wstring(L""));
}
}
else {
retVal.ResultList.push_back(std::wstring(val));
}
}
}
}
}
}
}
}
}
// Cleanup
// ========
VariantClear(&vtProp);
if (pclsObj)
pclsObj->Release();
if (pSvc)
pSvc->Release();
if (pLoc)
pLoc->Release();
if (pEnumerator)
pEnumerator->Release();
CoUninitialize();
return retVal;
}
void queryAndPrintResult(std::wstring query, std::wstring propNameOfResultObject)
{
WmiQueryResult res;
res = getWmiQueryResult(query, propNameOfResultObject);
if (res.Error != WmiQueryError::None) {
std::wcout << "Got this error while executing query: " << std::endl;
std::wcout << res.ErrorDescription << std::endl;
return; // Exitting function
}
for (const auto& item : res.ResultList) {
std::wcout << item << std::endl;
}
}
int main(int argc, char **argv)
{
// Get OS and partition
// queryAndPrintResult(L"SELECT * FROM Win32_OperatingSystem", L"Name");
// Get list of running processes
// queryAndPrintResult(L"Select * From Win32_Process", L"Name");
// Get serial number of Hard Drive
queryAndPrintResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
// Get id of CPU
queryAndPrintResult(L"SELECT ProcessorId FROM Win32_Processor", L"ProcessorId");
// Get desktops
queryAndPrintResult(L"SELECT * FROM Win32_DesktopMonitor ", L"DeviceId");
system("pause");
}
Note:
#matkatmusic discovered that using this function on a background thread in the JUCE framework will fail if you try to CoInitializeSecurity. And suggests that by removing the CoInitializeSecurity() call, this function will correctly run the WMI query.

For Windows:
wmic path win32_physicalmedia get SerialNumber
Here is example how to get back the data as a string running any command. we're using _popen instead of system suggested above
#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>
exec("wmic path win32_physicalmedia get SerialNumber");
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
std::shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
if (!pipe) throw std::runtime_error("_popen() failed!");
while (!feof(pipe.get())) {
if (fgets(buffer.data(), 128, pipe.get()) != NULL)
result += buffer.data();
}
return result;
}

For Linux without requiring root/sudo access:
Install libudev-dev.
Use code like that below:
#include <stdio.h>
#include <string.h>
#include <libudev.h>
#include <sys/stat.h>
int main()
{
struct udev *ud = NULL;
struct stat statbuf;
struct udev_device *device = NULL;
struct udev_list_entry *entry = NULL;
ud = udev_new();
if (NULL == ud) {
fprintf(stderr, "Failed to create udev.\n");
} else {
if (0 != stat("/dev/sda", &statbuf)) {
fprintf(stderr, "Failed to stat /dev/sda.\n");
} else {
device = udev_device_new_from_devnum(ud, 'b', statbuf.st_rdev);
if (NULL == device) {
fprintf(stderr, "Failed to open /dev/sda.\n");
} else {
entry = udev_device_get_properties_list_entry(device);
while (NULL != entry) {
if (0 == strcmp(udev_list_entry_get_name(entry),
"ID_SERIAL")) {
break;
}
entry = udev_list_entry_get_next(entry);
}
printf("Serial ID: %s\n", udev_list_entry_get_value(entry));
udev_device_unref(device);
}
}
(void)udev_unref(ud);
}
return 0;
}
The code is partly based on libudev documentation and partly on the source code for udevadm.

Linux: see /sys/class/ata_device/dev*/id
On my system there are four user-readable files containing hex dumps of device info; two of them are all zeros, two other contain info about disk and DVD; DVD has no serial number and disk serial starts at offset 0x20.

For Windows, if you don't want WMI, use DeviceIOControl(). Here is a working implementation that I have used for years.
GetDiskSerialNumber("c:");
Implementation:
std::string GetDiskSerialNumber(const std::string& pDevicePath)
{
// open the device
HANDLE hDevice = ::CreateFileA(devicePath.c_str(), 0, 0, NULL, OPEN_EXISTING, NULL, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
{
// unable to open disk
M_LogT("GDSN - CF - FAILED - " << devicePath);
return "";
}
// set the input data structure
::STORAGE_PROPERTY_QUERY storagePropertyQuery;
::ZeroMemory(&storagePropertyQuery, sizeof(STORAGE_PROPERTY_QUERY));
storagePropertyQuery.PropertyId = StorageDeviceProperty;
storagePropertyQuery.QueryType = PropertyStandardQuery;
// get the necessary output buffer size
STORAGE_DESCRIPTOR_HEADER storageDescriptorHeader = {0};
DWORD dwBytesReturned = 0;
if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery,
sizeof(STORAGE_PROPERTY_QUERY), &storageDescriptorHeader, sizeof(STORAGE_DESCRIPTOR_HEADER),
&dwBytesReturned, NULL))
{
DWORD error = ::GetLastError();
::CloseHandle(hDevice);
M_LogWarnT("MGDSNV - FAILED - " << M_WinError(error));
return "";
}
// has serial number?
if (!storageDescriptorHeader.Size)
return "";
// alloc the output buffer
const DWORD dwOutBufferSize = storageDescriptorHeader.Size;
std::unique_ptr<BYTE[]> pOutBuffer(new BYTE[dwOutBufferSize]);
::ZeroMemory(pOutBuffer.get(), dwOutBufferSize);
// het the storage device descriptor
if (!::DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &storagePropertyQuery,
sizeof(STORAGE_PROPERTY_QUERY), pOutBuffer.get(), dwOutBufferSize, &dwBytesReturned, NULL))
{
DWORD error = ::GetLastError();
::CloseHandle(hDevice);
LogWarnT("MGDSNV - FAILED - " << M_WinError(error));
return "";
}
// cleanup
::CloseHandle(hDevice);
std::string serial;
// output buffer points to a STORAGE_DEVICE_DESCRIPTOR structure followed by additional
// info like vendor ID, product ID, serial number, and so on.
const STORAGE_DEVICE_DESCRIPTOR* pDeviceDescriptor = (STORAGE_DEVICE_DESCRIPTOR*)pOutBuffer.get();
if (pDeviceDescriptor->SerialNumberOffset && *(pOutBuffer.get() + pDeviceDescriptor->SerialNumberOffset))
// get the serial number
serial = std::string(reinterpret_cast<char*>(pOutBuffer.get() + pDeviceDescriptor->SerialNumberOffset));
return serial;
}

Related

Change Brightness using WMI

I have tried this Windows example Getting WMI Data from a Remote Computer and this other Calling a Provider Method, and both work correctly in my computer. However I have tried to use WMI to change the brightness and I am getting an error on step 6, in this part:
//Get the Next Object from the collection
hres = pEnum->Next(WBEM_INFINITE, //Timeout
1, //No of objects requested
&pObj, //Returned Object
&ulReturned /*No of object returned*/);
pEnum is a negative value.
IMPORTANT: While I was writing this question, I tried with different computers and I get errors in all of them, except when I use a laptop. Therefore, how can I change the brightness of a monitor?
I also realize that if I go to wbemtest.exe, in all my computers the class WmiMonitorBrightnessMethods and the method WmiSetBrightness exist, but only in the laptop there is an instance of it. In fact, in the computers when I click on view objects of the class I get this message (see image)
This is my code:
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
int main(int iArgCnt, char ** argv)
{
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM negotiates service
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
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
cout << "Failed to create IWbemLocator object. "
<< "Err code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: ---------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the local root\wminamespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\WMI"),
NULL,
NULL,
0,
NULL,
0,
0,
&pSvc
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
cout << "Connected to ROOT\\WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels for 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
);
if (FAILED(hres))
{
cout << "Could not set proxy blanket. Error code = 0x"
<< hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Call WmiSetBrightness method -----------------------------
// set up to call the Win32_Process::Create method
BSTR ClassName = SysAllocString(L"WmiMonitorBrightnessMethods");
BSTR MethodName = SysAllocString(L"WmiSetBrightness");
BSTR bstrQuery = SysAllocString(L"Select * from WmiMonitorBrightnessMethods");
IEnumWbemClassObject *pEnum = NULL;
hres = pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
bstrQuery, //Query to Execute
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, //Make a semi-synchronous call
NULL, //Context
&pEnum /*Enumeration Interface*/);
hres = WBEM_S_NO_ERROR;
ULONG ulReturned;
IWbemClassObject *pObj;
DWORD retVal = 0;
//Get the Next Object from the collection
hres = pEnum->Next(WBEM_INFINITE, //Timeout
1, //No of objects requested
&pObj, //Returned Object
&ulReturned /*No of object returned*/);
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(MethodName, 0, &pInParamsDefinition, NULL);
IWbemClassObject* pClassInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
VARIANT var1;
VariantInit(&var1);
BSTR ArgName0 = SysAllocString(L"Timeout");
V_VT(&var1) = VT_BSTR;
V_BSTR(&var1) = SysAllocString(L"0");
hres = pClassInstance->Put(ArgName0,
0,
&var1,
CIM_UINT32); //CIM_UINT64
VariantClear(&var1);
VARIANT var;
VariantInit(&var);
BSTR ArgName1 = SysAllocString(L"Brightness");
V_VT(&var2) = VT_BSTR;
V_BSTR(&var2) = SysAllocString(L"80"); //Brightness value
hres = pClassInstance->Put(ArgName1,
0,
&var2,
CIM_UINT8);
VariantClear(&var2);
// Call the method
VARIANT pathVariable;
VariantInit(&pathVariable);
hres = pSvc->ExecMethod(pathVariable.bstrVal,
MethodName,
0,
NULL,
pClassInstance,
NULL,
NULL);
VariantClear(&pathVariable);
return 0;
}
As Amit Shakya has stated. changing the brightness throught WMI is only possible in systems that can dynamically set the brightness (laptops and some some all-in-one devices).
However, there is a Microsoft function that allows you to change the brightness of an external monitor, SetMonitorBrightness.
See Microsoft library
I attach a simple example of how to do it:
// Includes
#include "PhysicalMonitorEnumerationAPI.h"
#include "HighLevelMonitorConfigurationAPI.h"
(...)
// Prepare variables
HMONITOR hMonitor = NULL;
HMONITOR hMonitorTest = NULL;
DWORD cPhysicalMonitors;
LPPHYSICAL_MONITOR pPhysicalMonitors = NULL;
// Get the screen
HWND hWnd = GetDesktopWindow();
hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY);
_BOOL success = GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, &cPhysicalMonitors);
if(success)
{
pPhysicalMonitors = (LPPHYSICAL_MONITOR)malloc(cPhysicalMonitors* sizeof(PHYSICAL_MONITOR));
if(pPhysicalMonitors != NULL)
{
success = GetPhysicalMonitorsFromHMONITOR(hMonitor,cPhysicalMonitors, pPhysicalMonitors);
HANDLE hPhysicalMonitor = pPhysicalMonitors[0].hPhysicalMonitor;
// Set brightness to 50%
DWORD dwNewBrightness = 50;
success = SetMonitorBrightness(hPhysicalMonitor, dwNewBrightness);
// Free resources
free(pPhysicalMonitors);
}
}
It is mostly because your PC does not support brightness controls via a software. For example in laptops there are hotkeys that can be used to control the brightness, if that exists that you will get an instance and make use of it via WMI.
To be sure, you can run below mentioned command in powershell. If it returns Not supported then you can not use WMI for these controls.
Get-CimInstance -Namespace root/WMI -ClassName WmiMonitorBrightnessMethods
Note : Hotkeys work via another softwares like Mobility Center etc.

How to get computer manufacturer and model from windows registry in C++?

I am writing my own C++ code to read the computer model and manufacturer on a Windows computer by reading and parsing the registry key
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services/mssmbios/Data/SMBiosData
Is there any library function in Windows / C++ / Visual Studio that allows me to get this information directly?
The steps you need are explained on Creating a WMI Application Using C++. MSDN even includes a sample program. You just need to change two strings.
Change SELECT * FROM Win32_Process to SELECT * FROM Win32_ComputerSystem
Change Name to Manufacturer and then again for Model.
With the help of the Microsoft example code, I was able to create this method.
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
std::pair<CString,CString> getComputerManufacturerAndModel() {
// Obtain the initial locator to Windows Management on a particular host computer.
IWbemLocator *locator = nullptr;
IWbemServices *services = nullptr;
auto hResult = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&locator);
auto hasFailed = [&hResult]() {
if (FAILED(hResult)) {
auto error = _com_error(hResult);
TRACE(error.ErrorMessage());
TRACE(error.Description().Detach());
return true;
}
return false;
};
auto getValue = [&hResult, &hasFailed](IWbemClassObject *classObject, LPCWSTR property) {
CString propertyValueText = "Not set";
VARIANT propertyValue;
hResult = classObject->Get(property, 0, &propertyValue, 0, 0);
if (!hasFailed()) {
if ((propertyValue.vt == VT_NULL) || (propertyValue.vt == VT_EMPTY)) {
} else if (propertyValue.vt & VT_ARRAY) {
propertyValueText = "Unknown"; //Array types not supported
} else {
propertyValueText = propertyValue.bstrVal;
}
}
VariantClear(&propertyValue);
return propertyValueText;
};
CString manufacturer = "Not set";
CString model = "Not set";
if (!hasFailed()) {
// Connect to the root\cimv2 namespace with the current user and obtain pointer pSvc to make IWbemServices calls.
hResult = locator->ConnectServer(L"ROOT\\CIMV2", nullptr, nullptr, 0, NULL, 0, 0, &services);
if (!hasFailed()) {
// Set the IWbemServices proxy so that impersonation of the user (client) occurs.
hResult = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (!hasFailed()) {
IEnumWbemClassObject* classObjectEnumerator = nullptr;
hResult = services->ExecQuery(L"WQL", L"SELECT * FROM Win32_ComputerSystem", WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &classObjectEnumerator);
if (!hasFailed()) {
IWbemClassObject *classObject;
ULONG uReturn = 0;
hResult = classObjectEnumerator->Next(WBEM_INFINITE, 1, &classObject, &uReturn);
if (uReturn != 0) {
manufacturer = getValue(classObject, (LPCWSTR)L"Manufacturer");
model = getValue(classObject, (LPCWSTR)L"Model");
}
classObject->Release();
}
classObjectEnumerator->Release();
}
}
}
if (locator) {
locator->Release();
}
if (services) {
services->Release();
}
CoUninitialize();
return { manufacturer, model };
}

WMI call from C++ results in HRESULT=0x80041003

I'm not sure why am I getting this strange result. Can someone please shed some light on this?
I'm using WMI calls from a C++ program to export the 'Application' part of the Windows Event Log.
This is done from a local service and the code works fine under Windows 7. The problem happens when I run it on Windows XP. For some weird reason the ExecMethod() of the WMI interface returns HRESULT=0x80041003, which is Access denied.
But if I put the exact same code into a simple user process and run it from there, everything works great. How could that be, the code fails to run from a more privileged local service but works from a simple user process?
PS. I'd appreciate any ideas because I've been working on this for several days to no avail....
PS2. I enabled the following privileges (like if I need to do this for the local service) and that still didn't help:
SE_SECURITY_NAME
SE_BACKUP_NAME
EDIT: I guess adding a sample code wouldn't hurt. (Sorry for the long chunk, but this darn WMI/COM isn't a beauty either....) I marked the spot where I get an error below:
// Initialize COM. ------------------------------------------
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
{
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hr = 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
);
if(SUCCEEDED(hr))
{
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hr = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if(SUCCEEDED(hr))
{
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hr = pLoc->ConnectServer(
_bstr_t(L"\\\\.\\ROOT\\CIMV2"), // 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 (e.g. Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if(SUCCEEDED(hr))
{
// Set security levels on the proxy -------------------------
hr = 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
);
if(SUCCEEDED(hr))
{
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hr = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("Select * from Win32_NTEventLogFile Where LogFileName='Application'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if(SUCCEEDED(hr))
{
IWbemClassObject *pclsObj = NULL;
int nCnt = -1;
//Go through all results
while (pEnumerator)
{
ULONG uReturn = 0;
hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
//Go to next iteration
nCnt++;
// Get a reference to the Win32_Printer class so we can find
// the RenamePrinter method. This lets us create an object
// representing the input parameter block to be passed to the
// method when we call it.
IWbemClassObject *pNTEventLogFile = NULL;
IWbemClassObject *params = NULL;
IWbemClassObject *paramsInst = NULL;
hr = pSvc->GetObject( _bstr_t( L"Win32_NTEventLogFile" ), 0, NULL,
&pNTEventLogFile, NULL );
if(SUCCEEDED(hr))
{
hr = pNTEventLogFile->GetMethod( _bstr_t( "BackupEventLog" ), 0, &params,
NULL );
if(SUCCEEDED(hr))
{
hr = params->SpawnInstance( 0, &paramsInst );
if(SUCCEEDED(hr))
{
// Now that we've got an instance representing the input
// parameters, we can fill in the parameter values
_bstr_t paramValue( L"C:\\Users\\UserName\\Documents\\application.evt" );
VARIANT paramVt;
paramVt.vt = VT_BSTR;
paramVt.bstrVal = paramValue;
hr = paramsInst->Put( L"ArchiveFileName", 0, &paramVt, NULL );
if(SUCCEEDED(hr))
{
// Get the "this" pointer to our object instance so that we
// can call the RenamePrinter method on it
CIMTYPE type;
LONG flavor;
VARIANT var;
hr = pclsObj->Get( L"__PATH", 0, &var, &type, &flavor );
if(SUCCEEDED(hr))
{
// Execute the RenamePrinter method on our object instance
IWbemClassObject *results = NULL;
hr = pSvc->ExecMethod( var.bstrVal, _bstr_t( L"BackupEventLog" ), 0,
NULL, paramsInst, &results, NULL );
**///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////// THIS IS WHERE hr = 0x80041003 or wbemErrAccessDenied
//////////////////////////////////////////////// only when this code is run from a local service
//////////////////////////////////////////////// on a Windows XP machine, but if I run the exact same
//////////////////////////////////////////////// code from a user process on XP machine, it works!
//////////////////////////////////////////////// Note that this works fine on Windows Vista/7 in any configuration.
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////**
if(SUCCEEDED(hr))
{
//Get result code from the BackupEventLog method
VARIANT vtProp;
hr = results->Get(L"ReturnValue", 0, &vtProp, 0, 0);
if(SUCCEEDED(hr))
{
if(vtProp.vt == VT_I4)
{
//Check
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384808(v=vs.85).aspx
//0 = Success
//8 = Privilege missing
//21 = Invalid parameter
//80 = Archive file name already exists. This value is returned starting with Windows Vista.
int nResV = vtProp.intVal;
//_tprintf(_T("result2 : %d\n"), nResV);
}
else
{
//Error
}
//Free
VariantClear(&vtProp);
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(results)
{
results->Release();
results = NULL;
}
//Clear
VariantClear(&var);
}
else
{
//Error
}
//Clear
VariantClear(&paramVt);
}
else
{
//Error
}
}
else
{
//Error
}
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(pNTEventLogFile)
{
pNTEventLogFile->Release();
pNTEventLogFile = NULL;
}
if(params)
{
params->Release();
params = NULL;
}
if(paramsInst)
{
paramsInst->Release();
paramsInst = NULL;
}
if(pclsObj)
{
pclsObj->Release();
pclsObj = NULL;
}
}
//Free
if(pclsObj)
{
pclsObj->Release();
pclsObj = NULL;
}
}
else
{
//Error
}
//Free
if(pEnumerator)
{
pEnumerator->Release();
pEnumerator = NULL;
}
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(pSvc)
{
pSvc->Release();
pSvc = NULL;
}
}
else
{
//Error
}
//Free
if(pLoc)
{
pLoc->Release();
pLoc = NULL;
}
}
else
{
//Error
}
//Uninit
CoUninitialize();
}
else
{
//Error
}
I think I got it... For anyone else who doesn't want to waste 3 days looking for an answer here it is: on Windows XP replace RPC_C_IMP_LEVEL_IMPERSONATE with RPC_C_IMP_LEVEL_DELEGATE in the calls to CoInitializeSecurity() and CoSetProxyBlanket(). I don't know what exactly it does, but it makes the code above work! Yay!!!

Descriptive monitor name from D3D display adapter ID

As the question suggests, I'm trying to pull a descriptive monitor name to match with a display adapter name. The code below gives me a device ID like \.\DISPLAY1 which is understandable but not what I'm looking for.
// Get name.
D3DADAPTER_IDENTIFIER9 d3dID;
d3d9.Get().GetAdapterIdentifier(iAdapter, 0, &d3dID);
dispAd.name = d3dID.Description;
// Add monitor ID to display adapter name.
FIX_ME // Not happy with this yet!
HMONITOR hMonitor = d3d9.Get().GetAdapterMonitor(iAdapter);
MONITORINFOEXA monInfoEx;
monInfoEx.cbSize = sizeof(MONITORINFOEXA);
if (GetMonitorInfoA(hMonitor, &monInfoEx))
{
dispAd.name = dispAd.name + " on: " + monInfoEx.szDevice;
}
else TPB_ASSERT(0); // Mute?
I've looked around the documentation for where to pull that actual name from but until now I haven't been able to find it. Sometimes I am a little stupid (or blind if you will), so I'll give it another go during my lunch break -- but perhaps someone can point me in the right direction? Thanks a lot.
(and by actual name I mean the one presented in the graphics configuration panel)
UINT iOutput = 0;
IDXGIOutput *pOutput = nullptr;
while (DXGI_ERROR_NOT_FOUND != pAdapter->EnumOutputs(iOutput++, &pOutput))
{
DXGI_OUTPUT_DESC desc;
VERIFY(S_OK == pOutput->GetDesc(&desc));
MONITORINFOEXW monInfoEx;
monInfoEx.cbSize = sizeof(MONITORINFOEXW);
GetMonitorInfoW(desc.Monitor, &monInfoEx);
DISPLAY_DEVICEW dispDev;
dispDev.cb = sizeof(DISPLAY_DEVICEW);
EnumDisplayDevicesW(monInfoEx.szDevice, 0, &dispDev, 0);
// FIXME: far from perfect, but should do the job if a vendor driver is installed.
// Otherwise it just displays something along the lines of "Plug & Play monitor".
SendDlgItemMessageW(hDialog, IDC_COMBO_OUTPUT, CB_ADDSTRING, 0, (LPARAM) dispDev.DeviceString);
pOutput->Release();
}
This works. It is supposed to need only Windows+stl to compile and eats HMONITOR. Some things I'm not happy with:
The WMI stuff assuming the monitor order is the same as EnumDisplayDevices(). I wanted to compare to the ID string but could not find it in the WMI data. Still needs another look.
The WMI code probably doesn't use the optimal name field but on the Netbook I have around right now all of them say "Plug & play" blabla so I'll have to test it on another system a soon as I get the chance. Just a matter of tuning this line in the WMI function, though:
pClassObj->Get(L"Description", 0, &varProp, NULL, NULL);
Code:
// tpbds -- Windows monitor description
// Disable warnings about non-unwindable objects in case C++ exceptions are disabled.
#pragma warning(disable:4530)
// Force Unicode.
#ifndef _UNICODE
#define _UNICODE
#endif
#define _WIN32_DCOM
#pragma comment(lib, "wbemuuid.lib")
#include <windows.h>
#include <comdef.h>
#include <wbemidl.h>
#include <string>
#include <sstream>
#include "monitordescription.h"
#define FIX_ME
#define SAFE_RELEASE(pX) if (pX) pX->Release(); pX = NULL;
// serialize constant value T to std::wstring
template<typename T> inline std::wstring ToWideString(const T &X)
{
std::wstringstream stream;
stream << X;
return stream.str();
}
static const std::wstring GetMonitorDescriptonFromWMI(DWORD iMonitor)
{
// If anything fails down the line I just return an empty string and apply a fallback mechanism.
// This type of function should never fail unless you're probing a non-existent piece of harwdare.
// Initialize COM.
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
return L"";
}
// Set COM security levels.
// Note: if you are using Windows 200, you need to specify the default authentication
// credentials for a user by using a SOLE_AUTHENTICATION_LIST structure in the pAuthList parameter.
if (FAILED(CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, // pAuthList
EOAC_NONE,
NULL)))
{
CoUninitialize();
return L"";
}
// Obtain initial locator to WMI.
IWbemLocator *pLocator = NULL;
if (FAILED(CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID *>(&pLocator))))
{
CoUninitialize();
return L"";
}
// Connect to WMI.
IWbemServices *pServices = NULL;
if (FAILED(pLocator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, NULL, NULL, NULL, NULL, &pServices)))
{
pLocator->Release();
CoUninitialize();
return NULL;
}
// Set security levels on the proxy.
if (FAILED(CoSetProxyBlanket(
pServices,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE)))
{
pServices->Release();
pLocator->Release();
CoUninitialize();
return L"";
}
// Request WMI data.
IEnumWbemClassObject* pEnumerator = NULL;
if (FAILED(pServices->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_DesktopMonitor"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator)))
{
pServices->Release();
pLocator->Release();
CoUninitialize();
return L"";
}
// Try to compile a correct description.
std::wstring description;
DWORD iLoop = 1; // Monitor index is 1-based.
IWbemClassObject *pClassObj = NULL;
while (pEnumerator != NULL)
{
ULONG uReturn = 0;
const HRESULT hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObj, &uReturn);
if (uReturn == 0)
{
// Done (pClassObj remains NULL).
break;
}
// Is this the one we're looking for?
FIX_ME // Untested shortcut (assumes order is identical to that of EnumDisplayDevices).
if (iMonitor == iLoop)
{
FIX_ME // This needs to be tested, I only had a Netbook without proper driver!
VARIANT varProp;
pClassObj->Get(L"Description", 0, &varProp, NULL, NULL); // Check the MSDN for Win32_DesktopMonitor to see what your options are!
description = varProp.bstrVal;
description += L" #" + ToWideString(iMonitor);
VariantClear(&varProp);
SAFE_RELEASE(pClassObj);
// Done.
break;
}
else
SAFE_RELEASE(pClassObj);
}
pServices->Release();
pLocator->Release();
CoUninitialize();
// With a bit of luck this string was just built.
return description;
}
const std::wstring GetMonitorDescription(HMONITOR hMonitor)
{
MONITORINFOEX monInfoEx;
monInfoEx.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(hMonitor, &monInfoEx))
{
// Get monitor index by matching ID.
DWORD iDevNum = 0;
DISPLAY_DEVICE dispDev;
do
{
dispDev.cb = sizeof(DISPLAY_DEVICE);
EnumDisplayDevices(NULL, iDevNum, &dispDev, 0);
++iDevNum; // Incrementing here is right since we want a 1-based display.
}
while (0 != wcscmp(dispDev.DeviceName, monInfoEx.szDevice));
// Attempt to get the description from WMI.
// If it's empty, carry on.
const std::wstring descriptionFromWMI = GetMonitorDescriptonFromWMI(iDevNum);
if (!descriptionFromWMI.empty())
return descriptionFromWMI;
// Enumerate again, since doing it by string instead of index yields a different (more usable) DeviceString.
dispDev.cb = sizeof(DISPLAY_DEVICE);
EnumDisplayDevices(monInfoEx.szDevice, 0, &dispDev, 0);
// WMI approach failed so we rely on EnumDisplayDevices() for an acceptable result.
std::wstring description(dispDev.DeviceString);
return description + L" #" + ToWideString(iDevNum);
}
else return L"Unknown monitor";
}

C++: Get MAC address of network adapters on Vista?

We are currently using the NetBios method, and it works ok under XP. Preliminary tests under Vista show that it also works, but there are caveats - NetBIOS has to be present, for instance, and from what I've been reading, the order of the adapters is bound to change. Our alternative method - with SNMPExtensionQuery - seems to be broken under Vista.
The question is: do you know of a reliable way to get a list of the local MAC addresses on a Vista machine? Backwards compatibility with XP is a plus (I'd rather have one single method than lots of ugly #ifdef's). Thanks!
This will give you a list of all MAC addresses on your computer. It will work with all versions of Windows as well:
void getdMacAddresses(std::vector<std::string> &vMacAddresses;)
{
vMacAddresses.clear();
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//No network card? Other error?
if(dwStatus != ERROR_SUCCESS)
return;
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while(pAdapterInfo)
{
if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
vMacAddresses.push_back(szBuffer);
}
pAdapterInfo = pAdapterInfo->Next;
}
}
Could you use the WMIService? I used it to get the mac-address of a machine in pre-Vista days though.
Old question, already answered, but this is safer code - in case WMI can't be fully initialized.
For getting access to information about your system, here is a minimalist class that tries to stay safe:
NOTE: ToString, convertToUtf8 and convertFromUtf8 are left as an exercise for the reader. :)
NOTE: I've just shown the safe initialization and tear-down of the WMI system, and the basics of getting values from WMI, and getting the MAC Addresses (the question in the OP).
This came from working code, but was modified as I pasted it in here. So it is possible other things got left out that ought to have been included. Oops.
class WmiAccessor
{
public:
WmiAccessor()
: _pWbemLocator(NULL)
, _pWbemServices(NULL)
, _com_initialized(false)
, _com_need_uninitialize(false)
, _svc_initialized(false)
, _loc_initialized(false)
, _all_initialized(false)
, _errors("")
, m_mutex()
{
HRESULT hr;
hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch (hr)
{
case S_OK:
// The COM library was initialized successfully on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case S_FALSE:
// The COM library is already initialized on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case RPC_E_CHANGED_MODE:
// A previous call to CoInitializeEx specified the concurrency model
// for this thread as multithread apartment (MTA).
// This could also indicate that a change from neutral-threaded apartment to
// single-threaded apartment has occurred.
_com_initialized = true;
_com_need_uninitialize = false;
break;
default:
_com_initialized = false;
_com_need_uninitialize = false;
_errors += "Failed to initialize COM.\r\n";
return;
}
hr = ::CoInitializeSecurity(NULL, -1, NULL, NULL,
0 /*RPC_C_AUTHN_LEVEL_DEFAULT*/,
3 /*RPC_C_IMP_LEVEL_IMPERSONATE*/,
NULL, EOAC_NONE, NULL);
// RPC_E_TOO_LATE == Security must be initialized before!
// It cannot be changed once initialized. I don't care!
if (FAILED(hr) && (hr != RPC_E_TOO_LATE))
{
_errors += "Failed to initialize COM Security.\r\n";
if (_com_need_uninitialize)
{
::CoUninitialize();
_com_need_uninitialize = false;
}
return;
}
hr = _pWbemLocator.CoCreateInstance(CLSID_WbemLocator);
if (FAILED(hr) || (_pWbemLocator == nullptr))
{
_errors += "Failed to initialize WBEM Locator.\r\n";
return;
}
_loc_initialized = true;
hr = _pWbemLocator->ConnectServer(
CComBSTR(L"root\\cimv2"), NULL, NULL, 0, NULL, 0, NULL, &_pWbemServices);
if (FAILED(hr) || (_pWbemServices == nullptr))
{
_errors += "Failed to connect WBEM Locator.\r\n";
_pWbemLocator.Release();
_loc_initialized = false;
return;
}
else
{
_svc_initialized = true;
// Set security Levels on the proxy
hr = CoSetProxyBlanket(_pWbemServices,
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
);
if (FAILED(hr))
{
_errors += "Failed to set proxy blanket.\r\n";
return;
}
}
_all_initialized = true;
}
~WmiAccessor()
{
std::unique_lock<std::mutex> slock(m_mutex);
if (_svc_initialized)
{
if (_pWbemServices)
_pWbemServices.Release();
_svc_initialized = false;
}
if (_loc_initialized)
{
if (_pWbemLocator)
_pWbemLocator.Release();
_loc_initialized = false;
}
if (_com_initialized)
{
if (_com_need_uninitialize)
{
::CoUninitialize();
}
_com_initialized = false;
_com_need_uninitialize = false;
}
_all_initialized = false;
}
// public: must lock
std::string get_and_clear_errors()
{
std::string result = "";
std::unique_lock<std::mutex> slock(m_mutex);
std::swap(result, _errors);
return result;
}
// public: must lock
std::string get_string(const std::string& name, const std::string& dflt /*= ""*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _string(name) : dflt;
}
// public: must lock
uint32_t get_uint32(const std::string& name, uint32_t dflt /*= 0*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _uint32(name) : dflt;
}
// similarly for other public accessors of basic types.
private:
CComPtr<IWbemLocator> _pWbemLocator;
CComPtr<IWbemServices> _pWbemServices;
volatile bool _com_initialized;
volatile bool _com_need_uninitialize;
volatile bool _svc_initialized;
volatile bool _loc_initialized;
volatile bool _all_initialized;
std::string _errors;
CComVariant _variant(const std::wstring& name);
std::string _string(const std::string& name);
uint32_t _uint32(const std::string& name);
uint16_t _uint16(const std::string& name);
uint8_t _uint8(const std::string& name);
std::vector<std::string> _macAddresses(bool forceReCalculate = false);
// to protect internal objects, public methods need to protect the internals.
//
mutable std::mutex m_mutex;
std::vector<std::string> _macs; // unlikely to change, so save them once found.
// internal: assumes inside a lock
CComVariant _variant(const std::wstring& name)
{
if (!_all_initialized)
return CComVariant();
CComPtr<IEnumWbemClassObject> pEnum;
CComBSTR cbsQuery = std::wstring(L"Select " + name + L" from Win32_OperatingSystem").c_str();
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
CComVariant cvtValue;
if (FAILED(hr) || !pEnum)
{
std::wstring wquery(cbsQuery, SysStringLen(cbsQuery));
_errors += "Failed to exec WMI query: '" + convertToUtf8(wquery) + "'\r\n";
return cvtValue;
}
ULONG uObjectCount = 0;
CComPtr<IWbemClassObject> pWmiObject;
hr = pEnum->Next(WBEM_INFINITE, 1, &pWmiObject, &uObjectCount);
if (FAILED(hr) || !pWmiObject)
{
_errors
+= "Failed to get WMI Next result for: '" + convertToUtf8(name) + "'\r\n";
return cvtValue;
}
hr = pWmiObject->Get(name.c_str(), 0, &cvtValue, 0, 0);
if (FAILED(hr))
{
_errors
+= "Failed to get WMI result value for: '" + convertToUtf8(name) + "'\r\n";
}
return cvtValue;
}
// internal: assumes inside a lock
std::string _string(const std::string& name)
{
if (!_all_initialized)
return "";
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
std::wstring wValue(cvtValue.bstrVal, SysStringLen(cvtValue.bstrVal));
std::string sValue = convertToUtf8(wValue);
return sValue;
}
// internal: assumes inside a lock
uint32_t _uint32(const std::string& name)
{
if (!_all_initialized)
return 0;
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
uint32_t uValue = static_cast<uint32_t>(cvtValue.lVal);
return uValue;
}
// similarly for other internal access of basic types.
// internal: assumes inside a lock
std::vector<std::string> _macAddresses(bool forceReCalculate /*= false*/)
{
if (!_all_initialized)
{
return _macs; // it will still be empty at this point.
}
if (forceReCalculate)
{
_macs.clear();
}
if (_macs.empty())
{
// hr == 0x80041010 == WBEM_E_INVALID_CLASS
// hr == 0x80041017 == WBEM_E_INVALID_QUERY
// hr == 0x80041018 == WBEM_E_INVALID_QUERY_TYPE
CComBSTR cbsQuery = std::wstring(L"Select * from Win32_NetworkAdapter").c_str();
CComPtr<IEnumWbemClassObject> pEnum;
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_RETURN_IMMEDIATELY, NULL, &pEnum);
if (FAILED(hr))
{
_errors += "error: MacAddresses: ExecQuery('"
+ convertToUtf8((LPWSTR)cbsQuery) + "') returned "
+ ToString(hr) + "\r\n";
}
if (SUCCEEDED(hr))
{
ULONG fetched;
VARIANT var;
IWbemClassObject* pclsObj = NULL;
while (pEnum)
{
hr = pEnum->Next(WBEM_INFINITE, 1, &pclsObj, &fetched);
if (0 == fetched)
break;
std::string theMac = "";
VariantInit(&var);
hr = pclsObj->Get(L"MACAddress", 0, &var, 0, 0);
if (SUCCEEDED(hr))
{
switch (var.vt)
{
case VT_NULL: break;
case VT_BSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8(var.bstrVal);
break;
case VT_LPSTR:
theMac = (var.bstrVal == NULL)
? ""
: (const char*)var.bstrVal;
break;
case VT_LPWSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8((LPWSTR)var.bstrVal);
break;
// _could_ be array of BSTR, LPSTR, LPWSTR; unlikely, but ....
case VT_ARRAY | VT_BSTR:
case VT_ARRAY | VT_LPSTR:
case VT_ARRAY | VT_LPWSTR:
_errors += "warning: MacAddresses: unexpected array of addresses";
_errors += "\r\n";
// yet another exercise for the reader :)
break;
default:
_errors += "error: MacAddresses: unexpected VARIANT.vt = "
+ ToString(var.vt) + "\r\n";
break;
}
// local loopback has an empty address?
if (!theMac.empty())
{
_macs.push_back(theMac);
}
}
VariantClear(&var);
pclsObj->Release();
}
}
}
return _macs;
}
...
}
GetAdaptersInfo() is the official method, it enumerates all adapters even ones that are disconnected.
See this post for example code codeguru
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <Windows.h>
#include <Iphlpapi.h>
#include <Assert.h>
#include <string>
#pragma comment(lib, "iphlpapi.lib")
char* getdMacAddresses()
{
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//Exit When Error
if (dwStatus != ERROR_SUCCESS)
return "ERROR";
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while (pAdapterInfo)
{
if (pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
return szBuffer;
}
pAdapterInfo = pAdapterInfo->Next;
}
return "ERROR";
}
You can use WMI on both XP and Vista, there are a number of examples online. e.g:
Use Windows Management Instrumentation (WMI) to get a MAC Address