www.pudn.com > RemoveDriveByLetter_demo.zip > RemoveDriveByLetter.cpp
// // RemoveDriveByLetter.cpp by Uwe Sieber - www.uwe-sieber.de // // Simple demonstration how to prepare a disk drive for save removal // // Works with removable and fixed drives under W2K, XP, W2K3, Vista // // Console application - expects the drive letter of the drive to remove as parameter // // you are free to use this code in your projects // #include "stdafx.h" #include#include #include #include #include #include //------------------------------------------------- DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, char* szDosDeviceName); //------------------------------------------------- //------------------------------------------------- int main(int argc, char* argv[]) { if ( argc != 2 ) { return 1; } char DriveLetter = argv[1][0]; DriveLetter &= ~0x20; // uppercase if ( DriveLetter < 'A' || DriveLetter > 'Z' ) { return 1; } char szRootPath[] = "X:\\"; // "X:\" -> for GetDriveType szRootPath[0] = DriveLetter; char szDevicePath[] = "X:"; // "X:" -> for QueryDosDevice szDevicePath[0] = DriveLetter; char szVolumeAccessPath[] = "\\\\.\\X:"; // "\\.\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) 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) 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; SP_DEVICE_INTERFACE_DATA devInterfaceData = {0}; devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 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, &devInterfaceData); if ( !res ) { break; } SetupDiEnumInterfaceDevice(hDevInfo, NULL, guid, dwIndex, &spdid); dwSize = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); 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 instance 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("DevInstId=%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, NULL, 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; } //-----------------------------------------------------------