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
}
Related
I'm adding functionality to my (Qt-based) application to monitor an arbitrary folder on my Windows system for any activity recursively (something the Qt variant QFileSystemWatcher lacks). After opening the folder with CreatFileW(), I create a completion port to receive the overlapped I/O, and then I queue a read using ReadDirectoryChangesW().
I have placed all of this in the following "simple" Win32 console application to demonstrate (note that the "stdafx.h" header has been modified to include "windows.h", but is otherwise as the Visual Studio 2013 IDE generated it):
#include "stdafx.h"
#define MAX_BUFFER 4096
struct ThreadData
{;
DWORD winerr;
HANDLE handle;
unsigned int flags;
int recursive;
HANDLE completion_port;
CHAR buffer[MAX_BUFFER];
DWORD buffer_len;
OVERLAPPED overlapped;
};
int _tmain(int argc, _TCHAR* argv[])
{
DWORD winerr;
ThreadData td;
td.flags = FILE_NOTIFY_CHANGE_FILE_NAME|
FILE_NOTIFY_CHANGE_DIR_NAME|
FILE_NOTIFY_CHANGE_ATTRIBUTES|
FILE_NOTIFY_CHANGE_SIZE|
FILE_NOTIFY_CHANGE_LAST_WRITE|
FILE_NOTIFY_CHANGE_LAST_ACCESS|
FILE_NOTIFY_CHANGE_CREATION|
FILE_NOTIFY_CHANGE_SECURITY;
td.recursive = 1;
td.completion_port = INVALID_HANDLE_VALUE;
td.handle = INVALID_HANDLE_VALUE;
td.handle = CreateFileW(L"J:\\Font", // arbitrary folder
FILE_LIST_DIRECTORY, // required
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
// Use FILE_FLAG_OVERLAPPED for asynchronous operation with ReadDirectoryChangesW.
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if(td.handle == INVALID_HANDLE_VALUE)
{
winerr = GetLastError();
CloseHandle(td->handle);
return 0;
}
td.completion_port = CreateIoCompletionPort(td.handle,
td.completion_port,
(ULONG_PTR)td,
0); // max num processors
if(td.completion_port == INVALID_HANDLE_VALUE)
{
winerr = GetLastError();
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
BOOL rdc = ReadDirectoryChangesW(td.handle,
td.buffer, // read results
MAX_BUFFER,
td.recursive, // watch subdirectories
// NOTE: At least one flag is required!
td.flags, // see Notify Filters below
&td.buffer_len,
&td.overlapped,
NULL); // completion routine
if(rdc == 0)
{
winerr = GetLastError(); // "The handle is invalid. (0x6)"
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
// Launch thread here to handle completions and trigger new ones
...
// Clean up when the thread is done
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
The thing to note about this code is that it is modeled after a Python module ("watcher"), written in C, that provides similar functionality to a Python environment. I've used it in Python, and it works as expected with all of the same settings in this C++ fragment.
In the above code, CreateIoCompletionPort() accepts the HANDLE generated by CreateFileW(), but ReadDirectoryChangesW() does not. It returns 0, and GetLastError() is returning "The handle is invalid. (0x6)". I've tried this under both 32- and 64-bit compiles, just in case that made any difference (I was using the 64-bit version of Python). Also, the directory specified doesn't appear to matter: All directories I specify produce the same result, which suggests it's a problem with the settings somewhere.
Is there something in the CreateFileW() call that might cause the HANDLE to be valid for generating a completion port, but would give the ReadDirectoryChangesW() function heartburn?
You are not initializing the I/O Completion Port correctly, and you are not initializing the OVERLAPPED structure at all. ReadDirectoryChangesW() is failing because the OVERLAPPED::hEvent field contains an invalid event object handle. That is the invalid handle that the error code is referring to, not the directory handle.
Try this instead:
#include "stdafx.h"
#define MAX_BUFFER 4096
struct ThreadData
{
DWORD winerr;
HANDLE handle;
DWORD flags;
BOOL recursive;
HANDLE completion_port;
CHAR buffer[MAX_BUFFER];
DWORD buffer_len;
OVERLAPPED overlapped;
};
int _tmain(int argc, _TCHAR* argv[])
{
DWORD winerr;
ThreadData td;
td.flags = FILE_NOTIFY_CHANGE_FILE_NAME|
FILE_NOTIFY_CHANGE_DIR_NAME|
FILE_NOTIFY_CHANGE_ATTRIBUTES|
FILE_NOTIFY_CHANGE_SIZE|
FILE_NOTIFY_CHANGE_LAST_WRITE|
FILE_NOTIFY_CHANGE_LAST_ACCESS|
FILE_NOTIFY_CHANGE_CREATION|
FILE_NOTIFY_CHANGE_SECURITY;
td.recursive = TRUE;
td.completion_port = NULL;
td.handle = CreateFileW(L"J:\\Font", // arbitrary folder
FILE_LIST_DIRECTORY, // required
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
// Use FILE_FLAG_OVERLAPPED for asynchronous operation with ReadDirectoryChangesW.
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if(td.handle == INVALID_HANDLE_VALUE)
{
winerr = GetLastError();
return 0;
}
td.completion_port = CreateIoCompletionPort(td.handle,
NULL,
(ULONG_PTR)&td,
0); // max num processors
if(td.completion_port == NULL)
{
winerr = GetLastError();
CloseHandle(td.handle);
return 0;
}
ZeroMemory(&td.overlapped, sizeof(td.overlapped)); // <-- add this!
// required if the thread uses GetOverlappedResult()...
// optional if the thread uses GetQueuedCompletionStatus()...
/*
td.overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(td.overlapped.hEvent == NULL)
{
winerr = GetLastError();
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
*/
BOOL rdc = ReadDirectoryChangesW(td.handle,
td.buffer, // read results
MAX_BUFFER,
td.recursive, // watch subdirectories
// NOTE: At least one flag is required!
td.flags, // see Notify Filters below
&td.buffer_len,
&td.overlapped,
NULL); // completion routine
if(rdc == FALSE)
{
winerr = GetLastError();
//CloseHandle(td.overlapped.hEvent);
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
// Launch thread here to handle completions and trigger new ones
...
// Clean up when the thread is done
//CloseHandle(td.overlapped.hEvent);
CloseHandle(td.completion_port);
CloseHandle(td.handle);
return 0;
}
CreateIoCompletionPort returns NULL on error, not INVALID_HANDLE_VALUE. So your first error is on this line:
td.completion_port = INVALID_HANDLE_VALUE;
It must be this instead:
td.completion_port = NULL;
And this incorrect check:
if(td.completion_port == INVALID_HANDLE_VALUE)
Must be this instead:
if(td.completion_port == NULL)
You get NULL in td.completion_port after CreateIoCompletionPort because the initial value of td.completion_port is invalid. Also, you are incorrectly handling the error case (say try close invalid handles).
i'm developing on a Bluetooth Low Energy Device and i need to see in code if the device is connected or not.
First thing i noticed was that there is in the Devicemanager a Attribute "Verbunden"-> English: Connected and it says true or false if my device is connected or not. So i need to read that Attribute in my program.
What i have tried till now:
Getting all Devices with SetupDiGetClassDevs
Getting the FriendlyName with SetupDiGetDeviceRegistryProperty
Searching for my Device with the name.
That works.
Now i wanted to get that Connected-Attribute but i didn't find out what i have to use at SetupDiGetDeviceRegistryProperty.
SetupDiGetDeviceRegistryProperty is described here https://msdn.microsoft.com/en-us/library/windows/hardware/ff551967(v=vs.85).aspx
Maybe someone knows what is the right value for Property.
My Code:
int get_device_info( void )
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
FILE * devices = fopen("devices.txt", "a+");
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
//SPDRP_DEVICEDESC,
//SPDRP_CAPABILITIES,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
if(buffer)
{
if( strcmp("Name of Device",AnsiString(buffer).c_str())==0)
{
fprintf(devices,"Result:[%s]",AnsiString(buffer).c_str());
if (buffer) LocalFree(buffer);
}
}
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
fclose(devices);
return 0;
}
Instead of using SetupDiEnumDeviceInfo, you would try:
1. using SetupDiEnumDeviceInterfaces
2. using SetupDiGetDeviceInterfaceProperty
3. using SetupDiGetDeviceInterfacePropertyKeys to get a list of all Property Keys available for the interface
4. using SetupDiGetDeviceProperty and/or SetupDiGetDeviceRegistryProperty
Instead of using SPDRP_XXX constants, you would use DEVPROP, as defined in 'devpkey.h' ...
Below are a few examples taken from the log of a test prog I wrote to discover the whole thing:
DEVPROPNAME: DEVPKEY_DeviceInterface_Bluetooth_DeviceAddress
DEVPROPGUID: {2BD67D8B-8BEB-48D5-87E0-6CDA3428040A}
DEVPROPPID: 1
DEVPROPTYPE: DEVPROP_TYPE_STRING
Value: c026df001017
DEVPROPNAME: DEVPKEY_Device_Children
DEVPROPGUID: {4340A6C5-93FA-4706-972C-7B648008A5A7}
DEVPROPPID: 9
DEVPROPTYPE: DEVPROP_TYPE_STRING_LIST
Value:
BTHLEDevice\{00001800-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0001
BTHLEDevice\{00001801-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0008
BTHLEDevice\{00001809-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&000c
BTHLEDevice\{0000180f-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0010
BTHLEDevice\{0000180a-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0014
BTHLEDevice\{00001523-1212-efde-1523-785feabcd123}_c026df001017\8&2fd07168&1&0019
On a second subject, you are 'working' on the 'device' itself ( SetupDiGetClassDevs(&BluetoothInterfaceGUID...) [and then working on the \BTHLE\ tree in Registry].
After listing all GattServices of this device and getting their uuids, you could restart that iteration on the device_guid itself SetupDiGetClassDevs(&GattServiceGUID...) [and then working on the \BTHLEDevice\ tree in Registry].
Now, to answer your question, I'm still searching myself :) But I'm not really sure:
1) that it is a working (dynamic) information to know the connection state
2) that it is a 'Property' you can access by the above methods
I have found out a solution.
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);//DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);//DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
LPTSTR buffer1 = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( // Get Name
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
{
if(strcmp("Your Device",AnsiString(buffer).c_str())==0) //Found your device
{
//########
DEVPROPTYPE ulPropertyType;
DWORD dwSize;
ULONG devst;
// memset(devst,0,sizeof(devst));
bool err = SetupDiGetDeviceProperty( //Checking Connection State
hDevInfo,
&DeviceInfoData,
&DEVPKEY_Device_DevNodeStatus, //Connected(0x02000000)
&ulPropertyType,
(BYTE *) &devst,
sizeof(devst),
&dwSize,
0);
DWORD error;
error = GetLastError();
if (devst &0x02000000) {
//"Status: Getrennt "
}
else
{
//"Status: Verbunden"
}
Hope this snippet helps.
I have several processes that try to read and write the same file. I want each of them to lock the file so that only one of them accesses it at a time.
I tried this (edit: this is a complete test code this time):
#include "stdafx.h"
#include "Windows.h"
bool test()
{
const char* path = "test.txt";
HANDLE hFile = CreateFileA(path,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("ERROR: Cannot open file %s\n", path);
return false;
}
// Lock the file
{
OVERLAPPED overlapped = {0};
BOOL res = LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK, 0, ~0, ~0, &overlapped);
if (!res)
{
printf("ERROR: Cannot lock file %s\n", path);
return false;
}
}
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize > 0)
{
char* content = new char[fileSize+1];
// Read the file
BOOL res = ReadFile(hFile, content, fileSize, NULL, NULL);
if (!res)
{
printf("ERROR: Cannot read file %s\n", path);
}
delete[] content;
}
const char* newContent = "bla";
int newContentSize = 3;
// Write the file
BOOL res = WriteFile(hFile, newContent, newContentSize, NULL, NULL);
if (!res)
{
//int err = GetLastError();
printf("ERROR: Cannot write to file\n");
}
// Unlock the file
{
OVERLAPPED overlapped = {0};
UnlockFileEx(hFile, 0, ~0, ~0, &overlapped);
}
CloseHandle(hFile);
return true;
}
int _tmain(int argc, _TCHAR* argv[])
{
bool res = test();
return 0;
}
This works fine on my computer, which has Windows 8. But on my colleague's computer, which has Windows 7, it crashes. Specifically, the calls to ReadFile and WriteFile crash, always.
Note that it never enters the code paths with the error printfs. This code triggers no error except for a write at location 0x00000000 in ReadFile (when run on Windows 7).
We tried to also pass the overlapped struct to the ReadFile and WriteFile calls. It prevents the crash but the lock doesn't work anymore, the file is all scrambled (not with this test code, with the real code).
What am I doing wrong?
Looks like your problem is:
lpNumberOfBytesRead [out, optional] argument is null in your call.
This parameter can be NULL only when the lpOverlapped parameter is not NULL.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
Heres your problem :
You are missing a necessary struct-member and:
0 and ~0 and {0} are all bad code, constant expressions like these will always produce unepected results -- WINAPI doesnt work like libc, parameters are not always compared against constants, instead they are tested against/via macros and other preprocessor-definitions themselves so passing constant values or initializing WINAPI structs with constants will often lead to errors like these.
After years of experimenting i have found that there is only one surefire way of avoiding them, i will express it in corrected code :
OVERLAPPED overlapped;
overlapped.hEvent = CreateEvent( ........... ); // put valid parameters here!
UnlockFileEx(hFile, 0 /*"reserved"*/, ULONG_MAX, ULONG_MAX, &overlapped);
please read this carefully : http://msdn.microsoft.com/en-us/library/windows/desktop/aa365716%28v=vs.85%29.aspx
I am want to find the type of drive for that i used DeviceIoControl function which is working. However when I use the same function in DLL it returns with ERROR_BAD_LENGTH error. Following is my code.
BOOL Globals::IsUsbDevice ( wchar_t letter)
{
wchar_t volumeAccessPath[] = L"\\\\.\\X:";
volumeAccessPath [4] = letter;
HANDLE deviceHandle= CreateFileW(
volumeAccessPath,
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 (deviceHandle == INVALID_HANDLE_VALUE) // cannot open the drive
{
CloseHandle (deviceHandle);
return (FALSE);
}
// Setup query
STORAGE_PROPERTY_QUERY Query;
memset (&Query, 0, sizeof (Query));
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
// Issue query
DWORD bytes;
//STORAGE_DEVICE_DESCRIPTOR Devd;
STORAGE_BUS_TYPE busType = BusTypeUnknown;
char OutBuf[1024] = {0}; // good enough, usually about 100 bytes
PSTORAGE_DEVICE_DESCRIPTOR pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR)OutBuf;
pDevDesc->Size = sizeof(OutBuf);
if (DeviceIoControl (deviceHandle,
IOCTL_STORAGE_QUERY_PROPERTY,
&Query, sizeof(STORAGE_PROPERTY_QUERY),
pDevDesc, pDevDesc->Size,
&bytes,NULL))
{
busType = pDevDesc->BusType;
}
else
{
// Retrieve the system error message for the last-error code
..........
}
CloseHandle (deviceHandle);
return BusTypeUsb == busType;
}
I am executing my program as Administrator.
Any help would be greatly appreciated.
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";
}