123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- // this file almost totally copied from MSDN
- #include <windows.h>
- #include <stdio.h>
- #include <winioctl.h>
- #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 <stdio.h>
- #include <windows.h>
- #include <Setupapi.h>
- #include <winioctl.h>
- #include <winioctl.h>
- #include <cfgmgr32.h>
- #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
|