How to eject USB drive on Windows 10 (IOCTL_STORAGE_EJECT_MEDIA no longer enough) - windows

Convention wisdom to eject a USB drive on Windows is the following sequence:
CreateFile (drive letter, with read/write rights, file share read and write)
DeviceIoControl(FSCTL_LOCK_VOLUME)
DeviceIoControl(FSCTL_DISMOUNT_VOLUME)
DeviceIoControl(IOCTL_STORAGE_MEDIA_REMOVAL) PreventMediaRemoval = FALSE
DeviceIoControl(IOCTL_STORAGE_EJECT_MEDIA)
This worked fine until a recent change in Windows 10 (not sure when). Now the drive is still properly ejected, but then Windows immediately remounts the drive.
What needs to be done to eject the drive until the user removes it and puts it in again?

Using CM_Request_Device_EjectW API works for me. You can have a try.
The following is the complete code I tested and it from "How to Prepare a USB Drive for Safe Removal" at codeproject.
(Here the "F" is my USB drive letter. Replace it using your own one.)
#include <stdio.h>
#include <windows.h>
#include <Setupapi.h>
#include <winioctl.h>
#include <winioctl.h>
#include <cfgmgr32.h>
//-------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName);
//-------------------------------------------------
//-------------------------------------------------
int main()
{
char DriveLetter = 'F';
DriveLetter &= ~0x20; // uppercase
if (DriveLetter < 'A' || DriveLetter > 'Z') {
return 1;
}
char szRootPath[] = "F:\\"; // "X:\" -> for GetDriveType
szRootPath[0] = DriveLetter;
char szDevicePath[] = "F:"; // "X:" -> for QueryDosDevice
szDevicePath[0] = DriveLetter;
char szVolumeAccessPath[] = "\\\\.\\F:"; // "\\.\X:" -> to open the volume
szVolumeAccessPath[4] = DriveLetter;
long DeviceNumber = -1;
// open the storage volume
HANDLE hVolume = CreateFile(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
return 1;
}
// get the volume's device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
long res = DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
DeviceNumber = sdn.DeviceNumber;
}
CloseHandle(hVolume);
if (DeviceNumber == -1) {
return 1;
}
// get the drive type which is required to match the device numbers correctely
UINT DriveType = GetDriveType(szRootPath);
// get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
char szDosDeviceName[MAX_PATH];
res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
if (!res) {
return 1;
}
// get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);
if (DevInst == 0) {
return 1;
}
PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
WCHAR VetoNameW[MAX_PATH];
VetoNameW[0] = 0;
bool bSuccess = false;
// get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
DEVINST DevInstParent = 0;
res = CM_Get_Parent(&DevInstParent, DevInst, 0);
for (long tries = 1; tries <= 3; tries++) { // sometimes we need some tries...
VetoNameW[0] = 0;
// CM_Query_And_Remove_SubTree doesn't work for restricted users
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, CM_REMOVE_NO_RESTART); // CM_Query_And_Remove_SubTreeA is not implemented under W2K!
//res = CM_Query_And_Remove_SubTreeW(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP)
res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0);
//res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 0); // with messagebox (W2K, Vista) or balloon (XP)
bSuccess = (res == CR_SUCCESS && VetoType == PNP_VetoTypeUnknown);
if (bSuccess) {
break;
}
Sleep(500); // required to give the next tries a chance!
}
if (bSuccess) {
printf("Success\n\n");
return 0;
}
printf("failed\n");
printf("Result=0x%2X\n", res);
if (VetoNameW[0]) {
printf("VetoName=%ws)\n\n", VetoNameW);
}
return 1;
}
//-----------------------------------------------------------
//----------------------------------------------------------------------
// returns the device instance handle of a storage volume or 0 on error
//----------------------------------------------------------------------
DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName)
{
bool IsFloppy = (strstr(szDosDeviceName, "\\Floppy") != NULL); // who knows a better way?
GUID* guid;
switch (DriveType) {
case DRIVE_REMOVABLE:
if (IsFloppy) {
guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
}
else {
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
}
break;
case DRIVE_FIXED:
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
break;
case DRIVE_CDROM:
guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
break;
default:
return 0;
}
// Get device interface info set handle for all devices attached to system
HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
return 0;
}
// Retrieve a context structure for a device interface of a device information set
DWORD dwIndex = 0;
long res;
BYTE Buf[1024];
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
spdid.cbSize = sizeof(spdid);
while (true) {
res = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &spdid);
if (!res) {
break;
}
dwSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); // check the buffer size
if (dwSize != 0 && dwSize <= sizeof(Buf)) {
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
ZeroMemory(&spdd, sizeof(spdd));
spdd.cbSize = sizeof(spdd);
long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);
if (res) {
// in case you are interested in the USB serial number:
// the device id string contains the serial number if the device has one,
// otherwise a generated id that contains the '&' char...
/*
DEVINST DevInstParent = 0;
CM_Get_Parent(&DevInstParent, spdd.DevInst, 0);
char szDeviceIdString[MAX_PATH];
CM_Get_Device_ID(DevInstParent, szDeviceIdString, MAX_PATH, 0);
printf("DeviceId=%s\n", szDeviceIdString);
*/
// open the disk or cdrom or floppy
HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDrive != INVALID_HANDLE_VALUE) {
// get its device number
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned = 0;
res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
if (res) {
if (DeviceNumber == (long)sdn.DeviceNumber) { // match the given device number with the one of the current device
CloseHandle(hDrive);
SetupDiDestroyDeviceInfoList(hDevInfo);
return spdd.DevInst;
}
}
CloseHandle(hDrive);
}
}
}
dwIndex++;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return 0;
}

Related

win32: improve performance of disk write

In order to write to HD at max. performance I'm using overlapped I/O.
It works.
Upon acquiring 4MB of data (from sensor) I'm writing it to disk.
Then, upon getting the next 4MB I first ask if the previous writing completed.
How can I know what is the optimal block size (4MB ?) that is best for my disk ?
// AsyncFile.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "AsyncFile.h"
/****************************************************************************/
CAsyncFile::CAsyncFile()
{
}
/****************************************************************************/
CAsyncFile::~CAsyncFile()
{
}
/****************************************************************************/
int CAsyncFile::OpenFile(char *pcFileName,
bool bAsync, // Whether async read/write is required
bool bWrite) // True is file is used for writing to
{
DWORD dwAsyncMask = bAsync ? (FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING) : 0;
DWORD dwCreation = bWrite ? CREATE_ALWAYS : OPEN_EXISTING;
DWORD dwAccess = bWrite ? GENERIC_WRITE : GENERIC_READ;
DWORD dwShareMode = bWrite ? FILE_SHARE_READ : FILE_SHARE_WRITE;
if (strlen(pcFileName) < sizeof(m_cFileName))
strcpy_s(m_cFileName, 256, pcFileName);
else
m_cFileName[0] = 0; // NULL (error - file name is too long)
// Calling openFile() sets a valid value to the file handle
m_hFileHandle = INVALID_HANDLE_VALUE;
// Auto reset (manual reset=false), init state = false, no name
m_hIoCompleted = CreateEvent(NULL, FALSE, FALSE, NULL);
// Init OVERLAPPED structure, for async read
m_tOverlapped.Offset = 0;
m_tOverlapped.OffsetHigh = 0;
m_tOverlapped.hEvent = m_hIoCompleted;
m_Event = m_tOverlapped.hEvent;
if (m_hFileHandle != INVALID_HANDLE_VALUE)
{
// File is already openned; check open mode
if ((bAsync == m_bAsync) && (bWrite == m_bWrite))
return (ASYNCFILE_OK);
// File is already openned, but in other mode; Should close file
// before using it again
return ASYNCFILE_FILE_IS_NOT_IN_WRITE_MODE;
}
m_hFileHandle =
CreateFile((LPCTSTR)m_cFileName,
dwAccess, // Open for read or write
dwShareMode, //
NULL, // No SECURITY_ATTRBUTES
dwCreation, // Open exisiting file (if read) \ create new (if write)
dwAsyncMask, // For asynchronous operations, for maximum asynchronous performence
0);
if (m_hFileHandle == INVALID_HANDLE_VALUE)
{
DWORD dwError = GetLastError();
return ASYNCFILE_FAILED_TO_OPEN_FILE;
}
//In case file opened for reading, get its size
if (bWrite == false)
{
GetFileSizeEx(m_hFileHandle, &m_FileSize);
}
// Save open mode
m_bAsync = bAsync;
m_bWrite = bWrite;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::CloseFile()
{
//BOOL Status;
if (!CloseHandle(m_hFileHandle))
return ASYNCFILE_FAILED_TO_CLOSE_FILE;
if (!CloseHandle(m_hIoCompleted))
return ASYNCFILE_FAILED_TO_CLOSE_FILE;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::StartAsyncRead(void* pBuffer,
DWORD dwReadSize,
bool* pbEof)
{
*pbEof = false; // By default, EOF is false
int iError;
if (m_hFileHandle == INVALID_HANDLE_VALUE)
return (false);
if (!ReadFile(m_hFileHandle,
pBuffer,
dwReadSize,
NULL, // actual bytes read is not valid now
&m_tOverlapped))
{
if ((iError = GetLastError()) == ERROR_HANDLE_EOF)
{
*pbEof = true;
return ASYNCFILE_OK;
}
else if (!(m_bAsync && (iError == ERROR_IO_PENDING)))
{
return ASYNCFILE_START_READ_FAILED;
}
}
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::WaitAsyncOperationEnd(DWORD* pdwActualBytesTransferred)
{
if (m_hFileHandle == INVALID_HANDLE_VALUE)
return ASYNCFILE_WAIT_FOR_COMPLETION_FAILED;
// Wait for read operation to complete
if (!GetOverlappedResult(m_hFileHandle,
&m_tOverlapped,
pdwActualBytesTransferred,
true))
return ASYNCFILE_WAIT_FOR_COMPLETION_FAILED;
return ASYNCFILE_OK;
}
/****************************************************************************/
int CAsyncFile::StartAsyncWrite(void* pSrcBuf,
DWORD dwSize) // In bytes
{
int iError;
if (!WriteFile(m_hFileHandle,
pSrcBuf,
dwSize,
NULL, // actual bytes written is not valid now
&m_tOverlapped))
{
iError = GetLastError();
if (iError != ERROR_IO_PENDING)
return ASYNCFILE_START_WRITE_FAILED;
}
return ASYNCFILE_OK;
}
/****************************************************************************/
void CAsyncFile::SetFilePosition(UINT64 Position)
{
m_tOverlapped.Offset = Position & 0xFFFFFFFF;
m_tOverlapped.OffsetHigh = Position >> 32;
}
/****************************************************************************/
UINT64 CAsyncFile::GetFilePosition()
{
UINT64 Position;
Position = (m_tOverlapped.Offset) | ((UINT64)m_tOverlapped.OffsetHigh << 32);
return (Position);
}
/****************************************************************************/
UINT64 CAsyncFile::GetFileSize()
{
return (m_FileSize.QuadPart);
}
Well it depends on the average size of your files as well. If your file sizes are constantly ranging near 7 to 8 MB, then there would be some benefit in increasing the buffer size to 8192KB. How old is the drive? How often have you used it, many other factors come into play. We are able to learn more about this topic from an excellent piece of software called FastCopy. Hope this helps.

Capture encrypted USB decryption event?

hi I am using a Kingston DT4000 G2 USB drive with password protected.
I could track disk plug in & out event under windows by calling RegisterDeviceNotification(),
and receive notification by WM_DEVICECHANGE;
While the problem is the media is not available till I input password.
Before decryption I could see the device and system will show "Please insert a disk into USB drive (E:)".
But I can't capture the event when data is decrypted and media is really available to me.
Is there a such event could be captured using win32?
You could use following sample with GetLockStatus method of the Win32_EncryptableVolume
#include <windows.h>
#include <dbt.h>
#include <string>
#include <initguid.h>
#include <IoEvent.h>
#include <iostream>
#include <comdef.h>
#include <Wbemidl.h>
using namespace std;
#pragma comment(lib, "wbemuuid.lib")
#pragma warning(disable : 4996)
// Function prototype
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
std::string DrivesFromMask(ULONG unitmask);
UINT32 GetLockStatus();
int main(int argc, char** argv)
{
MSG msg; // MSG structure to store messages
HWND hwndMain; // Main window handle
WNDCLASSEX wcx; // WINDOW class information
HDEVNOTIFY hDevnotify;
DWORD len;
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
// 53F56307-B6BF-11D0-94F2-00A0C91EFB8B
GUID FilterGUID = { 0x53F56307,0x0B6BF,0x11D0,{0x94,0xF2,0x00,0xA0,0xC9,0x1E,0xFB,0x8B} };
// Initialize the struct to zero
ZeroMemory(&wcx, sizeof(WNDCLASSEX));
wcx.cbSize = sizeof(WNDCLASSEX); // Window size. Must always be sizeof(WNDCLASSEX)
wcx.style = 0; // Class styles
wcx.lpfnWndProc = (WNDPROC)MainWndProc; // Pointer to the callback procedure
wcx.cbClsExtra = 0; // Extra byte to allocate following the wndclassex structure
wcx.cbWndExtra = 0; // Extra byte to allocate following an instance of the structure
wcx.hInstance = GetModuleHandle(NULL); // Instance of the application
wcx.hIcon = NULL; // Class Icon
wcx.hCursor = NULL; // Class Cursor
wcx.hbrBackground = NULL; // Background brush
wcx.lpszMenuName = NULL; // Menu resource
wcx.lpszClassName = "USB"; // Name of this class
wcx.hIconSm = NULL; // Small icon for this class
// Register this window class with MS-Windows
if (!RegisterClassEx(&wcx))
return 0;
// Create the window
hwndMain = CreateWindowEx(0,// Extended window style
"USB", // Window class name
"", // Window title
WS_POPUP, // Window style
0, 0, // (x,y) pos of the window
0, 0, // Width and height of the window
NULL, // HWND of the parent window (can be null also)
NULL, // Handle to menu
GetModuleHandle(NULL), // Handle to application instance
NULL); // Pointer to window creation data
// Check if window creation was successful
if (!hwndMain)
return 0;
// Make the window invisible
ShowWindow(hwndMain, SW_HIDE);
// Initialize device class structure
len = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
memset(&NotificationFilter, 0, len);
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = 5; // DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = FilterGUID;
// Register
hDevnotify = RegisterDeviceNotification(hwndMain, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if (hDevnotify == NULL)
return 0;
// Process messages coming to this window
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// return value to the system
return msg.wParam;
}
HDEVNOTIFY RegisterDevice(HWND hWnd, PDEV_BROADCAST_DEVICEINTERFACE PdevDEVICEINTERFACE)
{
DEV_BROADCAST_HANDLE broadcast = { 0 };
broadcast.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
broadcast.dbch_devicetype = DBT_DEVTYP_HANDLE;
broadcast.dbch_handle = CreateFile(PdevDEVICEINTERFACE->dbcc_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
return RegisterDeviceNotification(hWnd, &broadcast, DEVICE_NOTIFY_WINDOW_HANDLE);
}
HDEVNOTIFY hDevNotify = NULL;
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_VOLUME PdevVolume;
PDEV_BROADCAST_DEVICEINTERFACE PdevDEVICEINTERFACE;
std::string drvs;
static UINT32 g_LockedDrivesMask;
switch (msg)
{
case WM_DEVICECHANGE:
switch (wParam)
{
// A device or piece of media has been inserted and is now available
case DBT_CUSTOMEVENT:
{
DEV_BROADCAST_HDR* hdr = (DEV_BROADCAST_HDR*)lParam;
switch (hdr->dbch_devicetype)
{
case DBT_DEVTYP_HANDLE:
UINT32 LockedDrivesMask = GetLockStatus();
UINT32 result = LockedDrivesMask ^ g_LockedDrivesMask;
if (result)
{
for (int i = 0; i < 26 && result; ++i)
{
if (result & 0x1)
{
if (0 == (LockedDrivesMask & (0x1 << i)))
printf("%c: unlock!\n", i + 'A');
}
result = result >> 1;
}
}
g_LockedDrivesMask = LockedDrivesMask;
break;
}
}
break;
case DBT_DEVICEARRIVAL:
PdevDEVICEINTERFACE = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
switch (PdevDEVICEINTERFACE->dbcc_devicetype)
{
// Class of devices
case DBT_DEVTYP_DEVICEINTERFACE:
g_LockedDrivesMask = GetLockStatus();
hDevNotify = RegisterDevice(hwnd, PdevDEVICEINTERFACE);
break;
// Logical volume
case DBT_DEVTYP_VOLUME:
PdevVolume = (PDEV_BROADCAST_VOLUME)lParam;
drvs = DrivesFromMask(PdevVolume->dbcv_unitmask);
for (UINT i = 0; i < drvs.length(); i++)
printf("Drive %c:\\ connected\n", drvs[i]);
}
break;
case DBT_DEVICEREMOVEPENDING:
PdevDEVICEINTERFACE = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
UnregisterDeviceNotification(hDevNotify);
}
break;
default:
// Call the default window handler
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
std::string DrivesFromMask(ULONG unitmask)
{
char i;
std::string drv = "";
for (i = 0; i < 26 && unitmask; ++i)
{
if (unitmask & 0x1)
{
drv += i + 'A';
}
unitmask = unitmask >> 1;
}
return drv;
}
UINT32 GetLockStatus()
{
HRESULT hres;
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
hres = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL
);
IWbemLocator* pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID*)&pLoc);
IWbemServices* pSvc = NULL;
hres = pLoc->ConnectServer(
_bstr_t(L"Root\\CIMV2\\Security\\MicrosoftVolumeEncryption"), // Object path of WMI namespace
NULL,
NULL,
0,
NULL,
0,
0,
&pSvc
);
hres = CoSetProxyBlanket(
pSvc,
RPC_C_AUTHN_WINNT,
RPC_C_AUTHZ_NONE,
NULL,
RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
IEnumWbemClassObject* pEnumerator = NULL;
wstring strQuery = L"SELECT * FROM Win32_EncryptableVolume";
hres = pSvc->ExecQuery(BSTR(L"WQL"), BSTR(strQuery.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
IWbemClassObject* pclsObj = NULL;
IWbemClassObject* pOutParams = NULL;
ULONG uReturn = 0;
UINT32 mask = 0;
while (pEnumerator)
{
UINT32 bit = 0;
hres = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn || FAILED(hres))
break;
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(BSTR(L"Win32_EncryptableVolume"), 0, NULL, &pClass, NULL);
VARIANT val;
hres = pclsObj->Get(L"DriveLetter", 0, &val, 0, NULL);
bit = val.bstrVal[0] - 'A';
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(L"GetLockStatus", 0, NULL, NULL);
VARIANT var;
pclsObj->Get(L"__PATH", 0, &var, NULL, NULL);
hres = pSvc->ExecMethod(var.bstrVal, _bstr_t(L"GetLockStatus"), 0,
NULL, NULL, &pOutParams, NULL);
VARIANT varReturnValue;
hres = pOutParams->Get(_bstr_t(L"LockStatus"), 0,
&varReturnValue, NULL, 0);
if (varReturnValue.iVal)
{
mask |= 0x1 << bit;
}
VariantClear(&val);
VariantClear(&var);
VariantClear(&varReturnValue);
pclsObj->Release();
pClass->Release();
pOutParams->Release();
pOutParams = NULL;
}
pEnumerator->Release();
pLoc->Release();
pSvc->Release();
CoUninitialize();
return mask;
}
But please note that due to the Security Considerations, this sample must be run as admin.
Or without administrator privileges, you could use the polling method in this example:
https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/e0585eca-31fa-4fe4-873d-d87934cbbf9d/thread-not-working-if-winmain-arg-is-2?forum=windowssdk

Get information for physical disks when Storage Spaces is enabled

I need to get the following information for all of the physical disks in the system when Windows 10 storage spaces is enabled.
Model
Serial Number
Firmware Version
Capacity
Index of the Disk
Pnp Id of the disk (to get the SCSI controller name using CM_Get_Parent)
Location information (Bus number, Target Id and LUN)
What I have tried so far:
Used WMI class MSFT_PhysicalDisk
Although this class gives me the adapter number (so i can do without the disk PNP), the location information it gives is not complete when a disk is connected to a different PCI storage controller (such as the Marvell 92xx SATA 6g controller).
Used SetupDiGetClassDevs with GUID_DEVINTERFACE_DISK, passed the handle to SetupDiGetDeviceInterface and used SetupDiGetDeviceInterfaceDetail for location information (Bus/Target Id/LUN), PNP Id, and Device Path. I can pass the device path to CreateFile and get the rest of the information (similar to this approach). The problem with this is that it does not give me all the physical disks. The disks under the storage spaces pool are omitted.
Use an approach similar to the second one but instead of SetupDiGetDeviceInterface and SetupDiGetDeviceInterfaceDetail, use SetupDiEnumDeviceInfo and CM_Get_DevNode_Registry_Property (using the Disk Drives Guid from here). Although this gives me the location and PNP id for all of the physical disks, I can't use anything here (that I know of) to call CreateFile and get the rest of the details.
How can I get the above details for each of the physical disks when storage spaces is enabled?
As a side note, if there is a way to get the disk PNP id from the disk index using the CreateFile and DeviceIoControl, that can also be very helpful for me.
at first we need enumerate all disks in system by call CM_Get_Device_Interface_ListW and CM_Get_Device_Interface_List_SizeW with GUID_DEVINTERFACE_DISK
#include <Shlwapi.h>
#include <cfgmgr32.h>
#undef _NTDDSTOR_H_
#include <ntddstor.h>
#include <ntdddisk.h>
static volatile UCHAR guz;
CONFIGRET EnumDisks(PCSTR prefix, PGUID InterfaceClassGuid)
{
CONFIGRET err;
PVOID stack = alloca(guz);
ULONG BufferLen = 0, NeedLen = 256;
union {
PVOID buf;
PWSTR pszDeviceInterface;
};
for(;;)
{
if (BufferLen < NeedLen)
{
BufferLen = RtlPointerToOffset(buf = alloca((NeedLen - BufferLen) * sizeof(WCHAR)), stack) / sizeof(WCHAR);
}
switch (err = CM_Get_Device_Interface_ListW(InterfaceClassGuid,
0, pszDeviceInterface, BufferLen, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
case CR_BUFFER_SMALL:
if (err = CM_Get_Device_Interface_List_SizeW(&NeedLen, InterfaceClassGuid,
0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT))
{
default:
return err;
}
continue;
case CR_SUCCESS:
while (*pszDeviceInterface)
{
DbgPrint("Interface=[%S]\n", pszDeviceInterface);
HANDLE hFile = CreateFileW(pszDeviceInterface, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
GetDiskPropertyByHandle(hFile);
CloseHandle(hFile);
}
GetPropertyByInterface(prefix, pszDeviceInterface);
pszDeviceInterface += 1 + wcslen(pszDeviceInterface);
}
return CR_SUCCESS;
}
}
}
CONFIGRET EnumDisks()
{
char prefix[256];
memset(prefix, '\t', sizeof(prefix));
prefix[sizeof(prefix) - 1] = 0;
prefix[0] = 0;
return EnumDisks(prefix + sizeof(prefix) - 1, const_cast<PGUID>(&GUID_DEVINTERFACE_DISK));
}
CM_Get_Device_Interface_ListW return multiple, NULL-terminated Unicode strings, each representing the symbolic link name of an interface instance.
from one side this symbolic link name can be passed to CreateFileW for open disk device. after this we can set some ioctl to disk - for get
Index of the Disk
Capacity
Serial Number
Partition Information
example:
void GetDiskPropertyByHandle(HANDLE hDisk)
{
HANDLE hPartition;
IO_STATUS_BLOCK iosb;
STORAGE_DEVICE_NUMBER sdn;
GET_LENGTH_INFORMATION li;
NTSTATUS status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, &sdn, sizeof(sdn));
if (0 <= status && sdn.DeviceType == FILE_DEVICE_DISK && !sdn.PartitionNumber)
{
DbgPrint("\\Device\\Harddisk%d\n", sdn.DeviceNumber);
WCHAR sz[64], *c = sz + swprintf(sz, L"\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);
WCHAR szSize[32];
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &li, sizeof(li))))
{
DbgPrint("Length = %S (%I64x)\n",
StrFormatByteSizeW(li.Length.QuadPart, szSize, RTL_NUMBER_OF(szSize)),
li.Length.QuadPart);
}
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
PSTORAGE_DEVICE_DESCRIPTOR psdd;
PCSTR psz;
};
STORAGE_PROPERTY_QUERY spq = { StorageDeviceProperty, PropertyStandardQuery };
ULONG cb = 0, rcb = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 0x40, PartitionCount = 4;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
switch (status = (NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), buf, cb)))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
if (psdd->Version == sizeof(STORAGE_DEVICE_DESCRIPTOR))
{
if (psdd->Size > cb)
{
rcb = psdd->Size;
status = STATUS_BUFFER_OVERFLOW;
}
else
{
if (psdd->SerialNumberOffset)
{
DbgPrint("SerialNumber = %s\n", psz + psdd->SerialNumberOffset);
}
}
}
else
{
status = STATUS_INVALID_PARAMETER;
}
break;
}
} while (status == STATUS_BUFFER_OVERFLOW);
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = NtDeviceIoControlFile(hDisk, 0, 0, 0, &iosb,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX, 0, 0, buf, cb)))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
_itow(PartitionEntry->PartitionNumber, c, 10);
RtlInitUnicodeString(&ObjectName, sz);
DbgPrint("%wZ\nOffset=%S ", &ObjectName,
StrFormatByteSizeW(PartitionEntry->StartingOffset.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
DbgPrint("Length=%S\n",
StrFormatByteSizeW(PartitionEntry->PartitionLength.QuadPart, szSize, RTL_NUMBER_OF(szSize)));
char PartitionName[256], *szPartitionName;
switch (PartitionEntry->PartitionStyle)
{
case PARTITION_STYLE_MBR:
DbgPrint("MBR: type=%x boot=%x", PartitionEntry->Mbr.PartitionType, PartitionEntry->Mbr.BootIndicator);
break;
case PARTITION_STYLE_GPT:
if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_ENTRY_UNUSED_GUID))
{
szPartitionName = "UNUSED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_SYSTEM_GUID))
{
szPartitionName = "SYSTEM";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RESERVED_GUID))
{
szPartitionName = "RESERVED";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_BASIC_DATA_GUID))
{
szPartitionName = "DATA";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_RECOVERY_GUID))
{
szPartitionName = "RECOVERY";
}
else if (IsEqualGUID(PartitionEntry->Gpt.PartitionType, PARTITION_MSFT_SNAPSHOT_GUID))
{
szPartitionName = "SNAPSHOT";
}
else
{
sprintf(szPartitionName = PartitionName, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
PartitionEntry->Gpt.PartitionType.Data1,
PartitionEntry->Gpt.PartitionType.Data2,
PartitionEntry->Gpt.PartitionType.Data3,
PartitionEntry->Gpt.PartitionType.Data4[0],
PartitionEntry->Gpt.PartitionType.Data4[1],
PartitionEntry->Gpt.PartitionType.Data4[2],
PartitionEntry->Gpt.PartitionType.Data4[3],
PartitionEntry->Gpt.PartitionType.Data4[4],
PartitionEntry->Gpt.PartitionType.Data4[5],
PartitionEntry->Gpt.PartitionType.Data4[6],
PartitionEntry->Gpt.PartitionType.Data4[7]);
}
DbgPrint("[%s] %I64x \"%S\"",
szPartitionName,
PartitionEntry->Gpt.Attributes,
PartitionEntry->Gpt.Name);
break;
}
if (0 <= NtOpenFile(&hPartition, FILE_GENERIC_READ, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
union {
BYTE bb[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + 32*sizeof(WCHAR) ];
FILE_FS_ATTRIBUTE_INFORMATION ffai;
};
switch (NtQueryVolumeInformationFile(hPartition, &iosb, &ffai, sizeof(bb), FileFsAttributeInformation))
{
case STATUS_SUCCESS:
case STATUS_BUFFER_OVERFLOW:
DbgPrint(" \"%.*S\"\n", ffai.FileSystemNameLength >> 1 , ffai.FileSystemName);
break;
}
NtClose(hPartition);
}
} while (PartitionEntry++, --PartitionCount);
}
return ;
}
switch (status)
{
case STATUS_BUFFER_OVERFLOW:
PartitionCount = pdli->PartitionCount;
continue;
case STATUS_INFO_LENGTH_MISMATCH:
case STATUS_BUFFER_TOO_SMALL:
PartitionCount <<= 1;
continue;
default:
return ;
}
}
}
}
from another size we can get Device Instance ID from interface string by call CM_Get_Device_Interface_PropertyW with DEVPKEY_Device_InstanceId. after this we call CM_Locate_DevNodeW for get device instance handle.
CONFIGRET GetPropertyByInterface(PCSTR prefix, PCWSTR pszDeviceInterface)
{
ULONG cb = 0, rcb = 256;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
CONFIGRET status;
union {
PVOID pv;
PWSTR DeviceID;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_Device_Interface_PropertyW(pszDeviceInterface, &DEVPKEY_Device_InstanceId, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("%sDeviceID = %S\n", prefix, DeviceID);
DEVINST dnDevInst;
if (CR_SUCCESS == (status = CM_Locate_DevNodeW(&dnDevInst, DeviceID, CM_LOCATE_DEVNODE_NORMAL)))
{
GetPropertyByDeviceID(prefix, dnDevInst);
}
}
else
{
status = CR_WRONG_TYPE;
}
break;
}
} while (status == CR_BUFFER_SMALL);
return status;
}
with device instance handle we can query many device properties via CM_Get_DevNode_PropertyW like:
DEVPKEY_Device_LocationInfo, DEVPKEY_NAME, DEVPKEY_Device_PDOName, DEVPKEY_Device_FirmwareVersion, DEVPKEY_Device_Model, DEVPKEY_Device_DriverVersion and many others - look full list in devpkey.h
finally we can call CM_Get_Parent and recursive query all this properties for parent device(s) until we not rich top of stack:
#define OPEN_PDO
void GetPropertyByDeviceID(PCSTR prefix, DEVINST dnDevInst)
{
#ifdef OPEN_PDO
HANDLE hFile;
IO_STATUS_BLOCK iosb;
UNICODE_STRING ObjectName;
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &ObjectName, OBJ_CASE_INSENSITIVE };
#endif
CONFIGRET status;
ULONG cb = 0, rcb = 0x80;
PVOID stack = alloca(guz);
DEVPROPTYPE PropertyType;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
static struct
{
CONST DEVPROPKEY *PropertyKey;
PCWSTR PropertyName;
} PropertyKeys[] = {
{ &DEVPKEY_Device_PDOName, L"PDOName"},
{ &DEVPKEY_Device_Parent, L"Parent"},
{ &DEVPKEY_Device_DriverVersion, L"DriverVersion"},
{ &DEVPKEY_Device_LocationInfo, L"LocationInfo"},
{ &DEVPKEY_Device_FirmwareVersion, L"FirmwareVersion"},
{ &DEVPKEY_Device_Model, L"Model"},
{ &DEVPKEY_NAME, L"NAME"},
{ &DEVPKEY_Device_InstanceId, L"DeviceID"}
};
do
{
int n = RTL_NUMBER_OF(PropertyKeys);
do
{
CONST DEVPROPKEY *PropertyKey = PropertyKeys[--n].PropertyKey;
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
status = CM_Get_DevNode_PropertyW(dnDevInst, PropertyKey, &PropertyType, pb, &rcb, 0);
if (status == CR_SUCCESS)
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
DbgPrint("%s%S=[%S]\n", prefix, PropertyKeys[n].PropertyName, sz);
#ifdef OPEN_PDO
if (!n)
{
// DEVPKEY_Device_PDOName can use in NtOpenFile
RtlInitUnicodeString(&ObjectName, sz);
if (0 <= NtOpenFile(&hFile, FILE_READ_ATTRIBUTES|SYNCHRONIZE, &oa,
&iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT))
{
NtClose(hFile);
}
}
#endif
}
}
} while (status == CR_BUFFER_SMALL);
} while (n);
if (!*--prefix) break;
} while (CM_Get_Parent(&dnDevInst, dnDevInst, 0) == CR_SUCCESS);
}
also string returned by DEVPKEY_Device_PDOName we can use in NtOpenFile call for open PDO device.

How to get from device-manager device (e.g. from its "Physical Device Object name") to its drive-letter?

From the device manager, I have a USB device node. I extracted its "Physical Device Object name" (e.g. \Device\0000010f).
Fighting for hours with NtOpenDirectoryObject, NtQueryDirectoryObject, NtOpenSymbolicLinkObject, NtQuerySymbolicLinkObject and QueryDosDevice, I couldn't find a way to get from that "Physical Device Object name" to the actual drive-letter (C:, D:, ...).
I'm looking for any storage solution (USB/SATA/...). How do I do that?
(There are many similar questions, none of them answers e.g. how to get from Physical Device Object name to \Device\HarddiskVolumeXYZ or to Volume{SOME_GUID})
what you view \Device\0000010f this is PDO (Physical Device Object) created by some bus driver (it have flag DO_BUS_ENUMERATED_DEVICE)
to it can be attached some FDO (Functional Device Object). if this is from storage stack (based on CompatibleIDs strings returned by bus device for this PDO ) typical FDO name have form \Device\Harddisk%d\DR%d and well known symbolic link to it \Device\Harddisk%d\Partition0
disk driver FDO enumerate partitions on volume and for every partition create PDO device object ( with well known symbolic link \Device\Harddisk%d\Partition%d where partition number always > 0, Partition0 is refer to whole disk FDO)
usual partition is same as volume but not always (Partitions and Volumes) also note IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS - it return array of DISK_EXTENT structures - look here for DiskNumber -The number of the disk that contains this extent. so volume can placed on several disks. but in 99%+ IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS return to you only one DISK_EXTENT
so what you can do if you have path to PDO in storage stack ?
open the device - if use ZwOpenFile (of course this worked in
user mode) we can use \Device\0000010f as is. if we want use win32
api we need use prefix \\?\GLOBALROOT for all names. really we open by this bus PDO but because disk FDO is attached to PDO all our requests will be send via FDO and handled here. desired access ? SYNCHRONIZE is enough (in case CreateFile if we not set FILE_FLAG_OVERLAPPED api implicity add this flag to DESIRED_ACCESS )
send IOCTL_STORAGE_GET_DEVICE_NUMBER to device. check that
DeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0
send IOCTL_DISK_GET_DRIVE_LAYOUT_EX for get a variable-sized
array of PARTITION_INFORMATION_EX structures
based on DeviceNumber (which we get at step 2) and
PartitionNumber (which we get at step 3) format symbolic link to
partition PDO - \\?\GLOBALROOT\Device\Harddisk%d\Partition%d
open partition PDO with SYNCHRONIZE access (enough because all
IOCTL which we use have FILE_ANY_ACCESS type
send IOCTL_MOUNTDEV_QUERY_DEVICE_NAME to partition
now we need handle to MountManager device (\\.\MountPointManager)
for send him IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH this IOCTL defined in mountmgr.h in input it require MOUNTDEV_NAME
which we get at step 6. on output we receive MOUNTMGR_VOLUME_PATHS
structure (also defined in mountmgr.h) alternatively we can use
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS - if all ok we got list of
drive letters like C:, D:, etc..
for enumerate disk drives in system we can use
CM_Get_Device_ID_ListW with
{4d36e967-e325-11ce-bfc1-08002be10318} filter, open every device
instance by CM_Locate_DevNodeW and finally query for
DEVPKEY_Device_PDOName by call
CM_Get_DevNode_Property
ok, here the code example correct which do all this:
#include <mountmgr.h>
// guz == 0 always, volatile for prevent CL "optimization" - it can drop alloca(0) call
static volatile UCHAR guz;
ULONG QueryPartitionW32(HANDLE hPartition, HANDLE hMountManager)
{
MOUNTDEV_STABLE_GUID guid;
ULONG dwBytesRet;
if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_STABLE_GUID, 0, 0, &guid, sizeof(guid), &dwBytesRet, NULL))
{
DbgPrint("StableGuid = \\\\?\\Volume{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
guid.StableGuid.Data1, guid.StableGuid.Data2, guid.StableGuid.Data3,
guid.StableGuid.Data4[0],
guid.StableGuid.Data4[1],
guid.StableGuid.Data4[2],
guid.StableGuid.Data4[3],
guid.StableGuid.Data4[4],
guid.StableGuid.Data4[5],
guid.StableGuid.Data4[6],
guid.StableGuid.Data4[7]
);
}
// assume NumberOfDiskExtents == 1
VOLUME_DISK_EXTENTS vde;
if (DeviceIoControl(hPartition, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, 0, 0, &vde, sizeof(vde), &dwBytesRet, 0))
{
if (vde.NumberOfDiskExtents)
{
DbgPrint("ofs=%I64u, len=%I64u\n", vde.Extents->StartingOffset.QuadPart, vde.Extents->ExtentLength.QuadPart);
}
}
PVOID stack = alloca(guz);
union {
PVOID buf;
PMOUNTDEV_NAME pmdn;
};
ULONG err;
ULONG cb = 0, rcb = sizeof(MOUNTDEV_NAME) + 0x10, InputBufferLength;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hPartition, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, 0, 0, buf, cb, &dwBytesRet, NULL))
{
DbgPrint("%.*S\n", pmdn->NameLength >> 1, pmdn->Name);
union {
PVOID pv;
PMOUNTMGR_VOLUME_PATHS pmvp;
};
cb = 0, rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + 0x10, InputBufferLength = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(pv = alloca(rcb - cb), pmdn);
}
if (DeviceIoControl(hMountManager, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
pmdn, InputBufferLength, pv, cb, &dwBytesRet, NULL))
{
PWSTR sz = pmvp->MultiSz;
while(*sz)
{
DbgPrint("%S\n", sz);
sz += 1 + wcslen(sz);
}
return NOERROR;
}
rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + pmvp->MultiSzLength;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
break;
}
rcb = sizeof(MOUNTDEV_NAME) + pmdn->NameLength;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG EnumDiskPartitionsW32(HANDLE hDisk, HANDLE hMountManager)
{
STORAGE_DEVICE_NUMBER sdn;
ULONG dwBytesRet;
if (!DeviceIoControl(hDisk, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesRet, NULL))
{
return GetLastError();
}
if (sdn.DeviceType != FILE_DEVICE_DISK || sdn.PartitionNumber != 0)
{
return ERROR_GEN_FAILURE;
}
WCHAR sz[128], *c = sz + swprintf(sz, L"\\\\?\\GLOBALROOT\\Device\\Harddisk%d\\Partition", sdn.DeviceNumber);
PVOID stack = alloca(guz);
union {
PVOID buf;
PDRIVE_LAYOUT_INFORMATION_EX pdli;
};
ULONG cb = 0, rcb, PartitionCount = 4;
for (;;)
{
if (cb < (rcb = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[PartitionCount])))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, buf, cb, &dwBytesRet, NULL))
{
if (PartitionCount = pdli->PartitionCount)
{
PPARTITION_INFORMATION_EX PartitionEntry = pdli->PartitionEntry;
do
{
if (!PartitionEntry->PartitionNumber)
{
continue;
}
_itow(PartitionEntry->PartitionNumber, c, 10);
DbgPrint("%S\n", sz);
HANDLE hPartition = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hPartition != INVALID_HANDLE_VALUE)
{
QueryPartitionW32(hPartition, hMountManager);
CloseHandle(hPartition);
}
} while (PartitionEntry++, --PartitionCount);
}
return NOERROR;
}
switch (ULONG err = GetLastError())
{
case ERROR_MORE_DATA:
PartitionCount = pdli->PartitionCount;
continue;
case ERROR_BAD_LENGTH:
case ERROR_INSUFFICIENT_BUFFER:
PartitionCount <<= 1;
continue;
default:
return err;
}
}
}
void DiskEnumW32(HANDLE hMountManager)
{
static const WCHAR DEVCLASS_DISK[] = L"{4d36e967-e325-11ce-bfc1-08002be10318}";
enum { flags = CM_GETIDLIST_FILTER_CLASS|CM_GETIDLIST_FILTER_PRESENT };
ULONG len;
if (!CM_Get_Device_ID_List_SizeW(&len, DEVCLASS_DISK, flags))
{
PWSTR buf = (PWSTR)alloca(len * sizeof(WCHAR));
if (!CM_Get_Device_ID_ListW(DEVCLASS_DISK, buf, len, flags))
{
PVOID stack = buf;
static const WCHAR prefix[] = L"\\\\?\\GLOBALROOT";
ULONG cb = 0, rcb = sizeof(prefix) + 0x20;
while (*buf)
{
DbgPrint("%S\n", buf);
DEVINST dnDevInst;
if (!CM_Locate_DevNodeW(&dnDevInst, buf, CM_LOCATE_DEVNODE_NORMAL))
{
DEVPROPTYPE PropertyType;
int err;
union {
PVOID pv;
PWSTR sz;
PBYTE pb;
};
do
{
if (cb < rcb)
{
rcb = cb = RtlPointerToOffset(pv = alloca(rcb - cb), stack);
}
rcb -= sizeof(prefix) - sizeof(WCHAR);
if (!(err = CM_Get_DevNode_PropertyW(dnDevInst, &DEVPKEY_Device_PDOName, &PropertyType,
pb + sizeof(prefix) - sizeof(WCHAR), &rcb, 0)))
{
if (PropertyType == DEVPROP_TYPE_STRING)
{
memcpy(pv, prefix, sizeof(prefix) - sizeof(WCHAR));
DbgPrint("%S\n", sz);
HANDLE hDisk = CreateFile(sz, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hDisk != INVALID_HANDLE_VALUE)
{
EnumDiskPartitionsW32(hDisk, hMountManager);
CloseHandle(hDisk);
}
}
else
{
err = ERROR_GEN_FAILURE;
}
break;
}
rcb += sizeof(prefix) - sizeof(WCHAR);
} while (err == CR_BUFFER_SMALL);
}
buf += 1 + wcslen(buf);
}
}
}
}
void DiskEnumW32()
{
HANDLE hMountManager = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMountManager != INVALID_HANDLE_VALUE)
{
DiskEnumW32(hMountManager);
CloseHandle(hMountManager);
}
}
Another possible solution for you, based on PowerShell and WMI alone:
$PDO = "\Device\00000052"
$DiskDriverData = Get-WmiObject Win32_PNPSignedDriver | Where {$_.PDO -eq $PDO}
$DeviceID = """" + $DiskDriverData.DeviceID.Replace("\","\\") + """"
$ComputerInfo = Get-WmiObject Win32_Computersystem
$name = $ComputerInfo.Name
$FullString = "\\$name\root\cimv2:Win32_PnPEntity.DeviceID=$DeviceID"
$PNPDevice = Get-WmiObject Win32_PNPDevice | Where {$_.SystemElement -eq $FullString}
$DiskDriveToPartition = Get-WmiObject -Class Win32_DiskDriveToDiskPartition | where {$_.Antecedent -eq $PNPDevice.SameElement}
foreach ($i in $DiskDriveToPartition) {
$Partition = Get-WmiObject -Class Win32_LogicalDiskToPartition | Where {$_.Antecedent -eq $i.Dependent}
$PartitionLetter = $Partition.Dependent.split('"')[1]
Write-Host -ForegroundColor Green "Detected Partition for the given PDO: $PartitionLetter"
}

How to refresh only one time in ReadDirectoryChange in WIN32 API?

I use ReadDirectoryChange in WIN32 API. After I copy 10 folder, my program is running refresh 10 times. However I only want it refresh once.
Can you help me? Here is my code:
bool FolderWatcher::Loop ()
{
// declare a variable and using it to return
int i = 0;
// convert from WCHAR to LPWSTR
LPWSTR ptr = _folder;
char buf[256 * (sizeof(FILE_NOTIFY_INFORMATION) + MAX_PATH * sizeof(WCHAR))] = {0};
DWORD bytesReturned = 0;
BOOL result = FALSE;
FILE_NOTIFY_INFORMATION *fni = NULL;
// using CreateFile to choose folder need to watch
HANDLE hDir = CreateFileW(
ptr,
FILE_LIST_DIRECTORY |
STANDARD_RIGHTS_READ,
//Enables subsequent open operations on a file or device to request read access.
FILE_SHARE_READ |
//Enables subsequent open operations on a file or device to request write access.
FILE_SHARE_WRITE |
//Enables subsequent open operations on a file or device to request delete access.
FILE_SHARE_DELETE,
NULL,
//Opens a file or device, only if it exists.
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
// check if directory is not exists
if (!hDir || hDir == INVALID_HANDLE_VALUE)
{
i = 0;
}
else
{
for (;;)
{
// call ReadDirectoryChangeW to watch folder
result = ReadDirectoryChangesW(hDir,
buf,
sizeof(buf) / sizeof(*buf),
FALSE, /* monitor the entire subtree */
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,
&bytesReturned,
NULL,
NULL);
if (result && bytesReturned)
{
wchar_t filename[MAX_PATH];
wchar_t action[256];
for (fni = (FILE_NOTIFY_INFORMATION*)buf; fni; )
{
switch (fni->Action)
{
// excute if a folder/ text docu is added
case FILE_ACTION_ADDED:
{
i = 1;
PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);
}
break;
// excute if a folder/ text docu is removed
case FILE_ACTION_REMOVED:
{
i = 1;
PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);
}
break;
case FILE_ACTION_MODIFIED:
{
i = 1;
if(lstrcmp(fni->FileName, L"service.log") != 0)
PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);
break;
}
// excute if a folder/ text docu is renamed
case FILE_ACTION_RENAMED_OLD_NAME:
{
i = 1;
PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);
break;
}
// excute if a folder/ text docu is renamed new name
case FILE_ACTION_RENAMED_NEW_NAME:
{
i = 1;
PostMessage (_hwndNotifySink, WM_FOLDER_CHANGE, 0, (LPARAM) _folder);
}
break;
}
if (fni->NextEntryOffset)
{
char *p = (char*)fni;
fni = (FILE_NOTIFY_INFORMATION*)(p + fni->NextEntryOffset);
}
else
{
fni = NULL;
}
}
}
else
{
i = 0;
}
}
CloseHandle(hDir); // close handle
}
return i; // return value of i
}
In case WM_FOLDER_CHANGE, I call refresh function.

Resources