Descriptive monitor name from D3D display adapter ID - c++

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";
}

Related

C++: How do I create a Shortcut in the Start Menu on Windows

I would like to know how to obtain the path to the start menu folder on Windows and then create a shortcut to a path that might contain non-ASCII characters.
Here is the solution. It uses Qt but it's also possible without. Then just use std::wstring instead of QString. For concatenating the paths and filenames you will then have to use string operations instead of using QDir.
#include <shlobj.h>
bool createStartMenuEntry(QString targetPath) {
targetPath = QDir::toNativeSeparators(targetPath);
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
if (SUCCEEDED(result)) {
QString linkPath = QDir(QString::fromWCharArray(startMenuPath)).absoluteFilePath("Shortcut Name.lnk");
CoInitialize(NULL);
IShellLinkW* shellLink = NULL;
result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
if (SUCCEEDED(result)) {
shellLink->SetPath(targetPath.toStdWString().c_str());
shellLink->SetDescription(L"Shortcut Description");
shellLink->SetIconLocation(targetPath.toStdWString().c_str(), 0);
IPersistFile* persistFile;
result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile);
if (SUCCEEDED(result)) {
result = persistFile->Save(linkPath.toStdWString().c_str(), TRUE);
persistFile->Release();
} else {
return false;
}
shellLink->Release();
} else {
return false;
}
} else {
return false;
}
return true;
}
Thats the part that obtains the location of the start-menu folder:
WCHAR startMenuPath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);
The rest is then creation of the shortcut. Exchange shortcut name and description for your desired values.
Same idea as accepted answer but Visual Studio method.
Usage:
CString sProgramsPath = getenv("PROGRAMDATA");
CString sShortcutPath = sProgramsPath += "\\Microsoft\\Windows\\Start Menu\\Programs\\SHORTCUT_NAME.lnk";
// (that's .LNK)
CreateLink("C:\\target_file_path\\target_file_name.exe",
"sShortcutPath",
"C:\\target_file_path\\,
"Shortcut Description");
Function:
/*============================================================================*/
// CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces
// to create and store a shortcut to the specified object.
//
// Returns the result of calling the member functions of the interfaces.
//
// Parameters:
// lpszPathObj - Address of a buffer that contains the path of the object,
// including the file name.
// lpszPathLink - Address of a buffer that contains the path where the
// Shell link is to be stored, including the file name.
// lpszPath - Working directory of target Obj file
// lpszDesc - Address of a buffer that contains a description of the
// Shell link, stored in the Comment field of the link
// properties.
HRESULT CreateLink(
LPCSTR lpszPathObj,
LPCSTR lpszPathLink,
LPCSTR lpszPath,
LPCSTR lpszDesc )
/*============================================================================*/
{
IShellLink* psl = NULL;
HRESULT hres = CoInitialize(NULL);
if (!SUCCEEDED(hres))
LOGASSERT(FALSE);
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(lpszPathObj);
psl->SetDescription(lpszDesc);
psl->SetWorkingDirectory(lpszPath);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
if (!SUCCEEDED(hres))
LOGASSERT(FALSE);
ppf->Release();
}
psl->Release();
}
CoUninitialize();
return hres;
}

How do I get the disk drive serial number in 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;
}

IPersistFile::Save method keeps failing to save the shortcut

I am trying to save a shortcut to my application in the startup folder. It all compiles, but it fails to actually save the game. The error seems to occur at hres = ppf->Save(wsz, TRUE);, where hres is set to -2147024891. If that means something specific, I haven't discovered what yet. My code is copied almost verbatim from MSDN, so I'm pretty confused why it isn't working. Perhaps I don't have permission to save a shortcut to the startup folder? Then again, I am also fairly new to all this, so it might be some basic error I am making. I am copying in all my #includes as well in case that is the problem.
Edit:
First, to avoid confusion, this is CLI based C++.
Checking hres for errors is just part of the MDSN code. This is really almost the exact same code from the website example. I have put in breakpoints, which is how I know that hres becomes -2147024891 right after the line hres = ppf->Save(wsz, TRUE); is run.
In case these are wrong, mediaMaestroLocation is set to "C:\Users\Keith\Documents\Visual Studio 2012\Projects\MediaMaestro\Debug\MediaMaestro.exe" and startupDestination is "C:\Users\Keith\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup". While the exe location looks great, I wonder if it matters that there isn't a \ after the destination folder path. I would have checked it already, but I need to spend a couple minutes figure out how to do it first.
#include <windows.h>
#include <string>
#include <stdio.h>
#include <shobjidl.h>
#include <shlobj.h>
#include "objbase.h"
#include <objidl.h>
#include <shlguid.h>
#include <winnls.h>
#using <System.dll>
#using <System.Windows.Forms.dll>
using namespace System;
using namespace System::Windows::Forms;
char startupDestination[MAX_PATH];
char mediaMaestroLocation[MAX_PATH];
DWORD nChars = 0;
BOOL yChars = 0;
HRESULT CreateLink()
{
CoInitializeEx( NULL, 0 );
HRESULT hres = 0;
IShellLink* psl;
if (SUCCEEDED(hres))
{
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(mediaMaestroLocation);
psl->SetDescription("Media Maestro");
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, startupDestination, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return hres;
}
Here is the click event in the UI that calls the function:
void settingsLaunchOnStart_Click( Object^ Sender, EventArgs^ e )
{
if (settingsLaunchOnStart->Checked == false)
{
HRESULT r;
nChars = GetModuleFileName( NULL, mediaMaestroLocation, sizeof(mediaMaestroLocation) );
yChars = SHGetFolderPath( NULL, CSIDL_STARTUP, NULL, SHGFP_TYPE_CURRENT, startupDestination);
r = CreateLink();
}
else if (settingsLaunchOnStart->Checked == true)
{
//code to remove the shortcut
}
}
Is there something I am missing?
It turns out that it wasn't enough to name the output folder path, I had to name the file and extension as well. It seems strange to me, considering I don't think I have seen a single other example doing this. Anyway, here is my updated working code:
HRESULT CreateLink()
{
CoInitializeEx( NULL, 0 );
HRESULT hres = 0;
IShellLink* psl;
if (SUCCEEDED(hres))
{
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
// has already been called.
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLink, (LPVOID*)&psl); //CLSCTX_ALL CLSCTX_INPROC_SERVER (void**)&psl (LPVOID*)&psl
if (SUCCEEDED(hres))
{
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(mediaMaestroLocation);
psl->SetDescription(L"Media Maestro");
psl->SetIconLocation(mediaMaestroLocation, 0);
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); //(void**)&psl (LPVOID*)&ppf
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Save the link by calling IPersistFile::Save.
hres = _wmakepath_s( wsz, _MAX_PATH, NULL, startupDestination,
L"MediaMaestro", L"lnk" );
hres = ppf->Save(wsz, TRUE);
ppf->Release();
}
psl->Release();
}
}
CoUninitialize();
return hres;
}
The addition of _wmakepath_s lets me append the name of my program and its extension onto the filepath I got from SHGetFolderPath. Once I feed that into the IPersistFile interface it saves as it should.
you have only initialized hres to 0 and then ur checking if it succeded ? u are never realling declaring it somewhere, and -2147024891 probably means that the variable is not initialized yet.
a wild guess is that it never even reahes the: hres = ppf->Save(wsz, TRUE); line and therefore it is not initialized :P try putting out some breakpoints when debugging and maybe use some watches to peek into variables :)
Best regards.

Incorrect Function Call "IOCTL_DISK_GET_DRIVE_LAYOUT_EX"

I am currently trying to write a C++ program to automate retrieving information about the partitions of a sample hard-drive image, the information in question being the number of partitions on the disk and for each partition its start sector, size and and file system type.
I'm pretty sure at this point the best way to achieve this is through MSDN functions, microsofts inbuilt commands. I am trying to use the "IOCTL_DISK_GET_DRIVE_LAYOUT_EX" function, but according to my get error call my function is incorrect. When I debug the program is appears that the bool value is also unchanged after the "IOCTL_DISK_GET_DRIVE_LAYOUT_EX" call, meaning it is not returning the bResult value.
I am using Microsoft Visual C++ Express Edition. If people could take a look at my code and tell me what they think I did wrong it would be much appreciated.
#define UNICODE 1
#define _UNICODE 1
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#define wszDrive L"\\\\.\\PhysicalDrive6"
BOOL GetDriveParition(LPWSTR wszPath, DRIVE_LAYOUT_INFORMATION_EX *pdg)
{
HANDLE hDevice = INVALID_HANDLE_VALUE; // handle to the drive to be examined
BOOL bResult = FALSE; // results flag
DWORD junk = 0; // discard results
hDevice = CreateFileW(wszPath, // drive to open
0, // no access to the drive
FILE_SHARE_READ | // share mode
FILE_SHARE_WRITE,
NULL, // default security attributes
OPEN_EXISTING, // disposition
0, // file attributes
NULL); // do not copy file attributes
if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
{
return (FALSE);
}
bResult = DeviceIoControl(
hDevice, // handle to device
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
pdg, // lpOutBuffer
sizeof(*pdg), // nOutBufferSize
&junk, // lpBytesReturned
NULL); // lpOverlapped
CloseHandle(hDevice);
return (bResult);
}
int wmain(int argc, wchar_t *argv[])
{
DRIVE_LAYOUT_INFORMATION_EX pdg; // disk drive partition structure
BOOL bResult = FALSE; // generic results flag
bResult = GetDriveParition (wszDrive, &pdg);
if (bResult)
{
wprintf(L"Drive path = %ws\n", wszDrive);
wprintf(L"Partition Style = %I64d\n", pdg.PartitionStyle);
wprintf(L"Partition Count = %ld\n", pdg.PartitionCount);
system("Pause");
}
else
{
wprintf (L"GetDrivePartition failed. Error %ld.\n", GetLastError ());
system("Pause");
}
return ((int)bResult);
}
DRIVE_LAYOUT_INFORMATION_EX is a weird structure. It's defined as
struct {
DWORD PartitionStyle;
DWORD PartitionCount;
union {
DRIVE_LAYOUT_INFORMATION_MBR Mbr;
DRIVE_LAYOUT_INFORMATION_GPT Gpt;
};
PARTITION_INFORMATION_EX PartitionEntry[ 1 ];
}
but usually PartitionEntry is treated as a much larger array, with PartitionCount entries. This is similar to the C99 VLA mechanism. Since you'va allocated just sizeof(*pdg) bytes, there's no room for even a second PartitionEntry.
C++ hack:
struct ExtraEntries : DRIVE_LAYOUT_INFORMATION_EX
{
PARTITION_INFORMATION_EX PartitionEntry[ 9 ]; // Or some other reasonable value
};
Even if this post is a bit old, I found another way to get a fully populated PartitionEntry without creating a tricky struct. This is how I did it:
Inspired of an answer from this post: How-to-call-deviceiocontrol-to-retrieve-the-amount-of-memory-it-needs
DRIVE_LAYOUT_INFORMATION_EX dli;
DWORD bytesReturned = 0;
if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, (void*)&dli, sizeof(dli), &bytesReturned, NULL))
{
// Check last error if not ERROR_INSUFFICIENT_BUFFER then return
int nError = GetLastError();
if (nError != ERROR_INSUFFICIENT_BUFFER)
{
// std::cout << "DeviceIoControl() Failed: " << nError << std::endl;
CloseHandle(hDevice);
return false;
}
// Allocate enough buffer space based of the value of Partition Count:
size_t size = offsetof(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[dli.PartitionCount]);
std::vector<BYTE> buffer(size);
if (!DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, (void*)buffer.data(), size, &bytesReturned, NULL))
{
nError = GetLastError();
// std::cout << "DeviceIoControl() Failed: " << nError << std::endl;
CloseHandle(hDevice);
return false;
}
const DRIVE_LAYOUT_INFORMATION_EX& result = *reinterpret_cast<const DRIVE_LAYOUT_INFORMATION_EX*>(buffer.data());
// Here all parition entry are populated ...
// TO DO... Do your stuff with result
}
else
{
// Call succeeded; dli is populated with a signle partition entry
// TO DO... Do your stuff with dli
}

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 };
}