// this file almost totally copied from MSDN #include #include #include #define LOCK_TIMEOUT 3000 // 10 Seconds #define LOCK_RETRIES 20 #if 1 // old way static HANDLE OpenVolume(TCHAR cDriveLetter) { HANDLE hVolume; UINT uDriveType; wchar_t szVolumeName[8] = {0}; wchar_t szRootName[5] = {0}; DWORD dwAccessFlags = 0; cDriveLetter &= ~0x20; // capitalize wsprintf(szRootName, L"%c:\\", cDriveLetter); uDriveType = GetDriveType(szRootName); switch(uDriveType) { case DRIVE_REMOVABLE: dwAccessFlags = GENERIC_READ | GENERIC_WRITE; break; case DRIVE_CDROM: dwAccessFlags = GENERIC_READ; break; default: printf("Cannot eject. Drive type is incorrect.\n"); return INVALID_HANDLE_VALUE; } wsprintf(szVolumeName, L"\\\\.\\%c:", cDriveLetter); hVolume = CreateFile( szVolumeName, dwAccessFlags, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (hVolume == INVALID_HANDLE_VALUE) printf("CreateFile error %d\n", GetLastError()); return hVolume; } static BOOL CloseVolume(HANDLE hVolume) { return CloseHandle(hVolume); } static BOOL LockVolume(HANDLE hVolume) { DWORD dwBytesReturned; DWORD dwSleepAmount; int nTryCount; dwSleepAmount = LOCK_TIMEOUT / LOCK_RETRIES; // Do this in a loop until a timeout period has expired for (nTryCount = 0; nTryCount < LOCK_RETRIES; nTryCount++) { if (DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL)) return TRUE; Sleep(dwSleepAmount); } return FALSE; } static BOOL DismountVolume(HANDLE hVolume) { DWORD dwBytesReturned; return DeviceIoControl( hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL); } static BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL fPreventRemoval) { DWORD dwBytesReturned; PREVENT_MEDIA_REMOVAL PMRBuffer; PMRBuffer.PreventMediaRemoval = fPreventRemoval; return DeviceIoControl( hVolume, IOCTL_STORAGE_MEDIA_REMOVAL, &PMRBuffer, sizeof(PREVENT_MEDIA_REMOVAL), NULL, 0, &dwBytesReturned, NULL); } static int AutoEjectVolume(HANDLE hVolume) { DWORD dwBytesReturned; return DeviceIoControl( hVolume, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &dwBytesReturned, NULL); } BOOL EjectVolume(TCHAR cDriveLetter) { HANDLE hVolume; BOOL fAutoEject = FALSE; hVolume = OpenVolume(cDriveLetter); if (hVolume == INVALID_HANDLE_VALUE) return FALSE; // Lock and dismount the volume. if (LockVolume(hVolume) && DismountVolume(hVolume)) { // Set prevent removal to false and eject the volume. if (PreventRemovalOfVolume(hVolume, FALSE) && AutoEjectVolume(hVolume)) fAutoEject = TRUE; } // Close the volume so other processes can use the drive. if (!CloseVolume(hVolume)) return FALSE; if (fAutoEject) return TRUE; else return FALSE; } #else #include #include #include #include #include #include #pragma comment(lib, "setupapi.lib") //------------------------------------------------- //---------------------------------------------------------------------- // returns the device instance handle of a storage volume or 0 on error //---------------------------------------------------------------------- static DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, const wchar_t* szDosDeviceName) { bool IsFloppy = (wcsstr(szDosDeviceName, L"\\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] = {0}; 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] = {0}; 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; } //------------------------------------------------- //------------------------------------------------- BOOL EjectVolume(TCHAR DriveLetter) { DriveLetter &= ~0x20; // uppercase if ( DriveLetter < 'A' || DriveLetter > 'Z' ) { return FALSE; } wchar_t szRootPath[] = L"X:\\"; // "X:\" -> for GetDriveType szRootPath[0] = DriveLetter; wchar_t szDevicePath[] = L"X:"; // "X:" -> for QueryDosDevice szDevicePath[0] = DriveLetter; wchar_t szVolumeAccessPath[] = L"\\\\.\\X:"; // "\\.\X:" -> to open the volume szVolumeAccessPath[4] = DriveLetter; long DeviceNumber = -1; // open the storage volume HANDLE hVolume = CreateFileW(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); if (hVolume == INVALID_HANDLE_VALUE) { return FALSE; } // 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 FALSE; } // 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? wchar_t szDosDeviceName[MAX_PATH] = {0}; res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH); if ( !res ) { return FALSE; } // 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 FALSE; } PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown; wchar_t VetoNameW[MAX_PATH] = {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 TRUE; } printf("failed\n"); printf("Result=0x%2X\n", res); if ( VetoNameW[0] ) { printf("VetoName=%ws)\n\n", VetoNameW); } return FALSE; } //----------------------------------------------------------- #endif