eject.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. // this file almost totally copied from MSDN
  2. #include <windows.h>
  3. #include <stdio.h>
  4. #include <winioctl.h>
  5. #define LOCK_TIMEOUT 3000 // 10 Seconds
  6. #define LOCK_RETRIES 20
  7. #if 1 // old way
  8. static HANDLE OpenVolume(TCHAR cDriveLetter)
  9. {
  10. HANDLE hVolume;
  11. UINT uDriveType;
  12. wchar_t szVolumeName[8] = {0};
  13. wchar_t szRootName[5] = {0};
  14. DWORD dwAccessFlags = 0;
  15. cDriveLetter &= ~0x20; // capitalize
  16. wsprintf(szRootName, L"%c:\\", cDriveLetter);
  17. uDriveType = GetDriveType(szRootName);
  18. switch(uDriveType) {
  19. case DRIVE_REMOVABLE:
  20. dwAccessFlags = GENERIC_READ | GENERIC_WRITE;
  21. break;
  22. case DRIVE_CDROM:
  23. dwAccessFlags = GENERIC_READ;
  24. break;
  25. default:
  26. printf("Cannot eject. Drive type is incorrect.\n");
  27. return INVALID_HANDLE_VALUE;
  28. }
  29. wsprintf(szVolumeName, L"\\\\.\\%c:", cDriveLetter);
  30. hVolume = CreateFile( szVolumeName,
  31. dwAccessFlags,
  32. FILE_SHARE_READ | FILE_SHARE_WRITE,
  33. NULL,
  34. OPEN_EXISTING,
  35. 0,
  36. NULL );
  37. if (hVolume == INVALID_HANDLE_VALUE)
  38. printf("CreateFile error %d\n", GetLastError());
  39. return hVolume;
  40. }
  41. static BOOL CloseVolume(HANDLE hVolume)
  42. {
  43. return CloseHandle(hVolume);
  44. }
  45. static BOOL LockVolume(HANDLE hVolume)
  46. {
  47. DWORD dwBytesReturned;
  48. DWORD dwSleepAmount;
  49. int nTryCount;
  50. dwSleepAmount = LOCK_TIMEOUT / LOCK_RETRIES;
  51. // Do this in a loop until a timeout period has expired
  52. for (nTryCount = 0; nTryCount < LOCK_RETRIES; nTryCount++) {
  53. if (DeviceIoControl(hVolume,
  54. FSCTL_LOCK_VOLUME,
  55. NULL, 0,
  56. NULL, 0,
  57. &dwBytesReturned,
  58. NULL))
  59. return TRUE;
  60. Sleep(dwSleepAmount);
  61. }
  62. return FALSE;
  63. }
  64. static BOOL DismountVolume(HANDLE hVolume)
  65. {
  66. DWORD dwBytesReturned;
  67. return DeviceIoControl( hVolume,
  68. FSCTL_DISMOUNT_VOLUME,
  69. NULL, 0,
  70. NULL, 0,
  71. &dwBytesReturned,
  72. NULL);
  73. }
  74. static BOOL PreventRemovalOfVolume(HANDLE hVolume, BOOL fPreventRemoval)
  75. {
  76. DWORD dwBytesReturned;
  77. PREVENT_MEDIA_REMOVAL PMRBuffer;
  78. PMRBuffer.PreventMediaRemoval = fPreventRemoval;
  79. return DeviceIoControl( hVolume,
  80. IOCTL_STORAGE_MEDIA_REMOVAL,
  81. &PMRBuffer, sizeof(PREVENT_MEDIA_REMOVAL),
  82. NULL, 0,
  83. &dwBytesReturned,
  84. NULL);
  85. }
  86. static int AutoEjectVolume(HANDLE hVolume)
  87. {
  88. DWORD dwBytesReturned;
  89. return DeviceIoControl( hVolume,
  90. IOCTL_STORAGE_EJECT_MEDIA,
  91. NULL, 0,
  92. NULL, 0,
  93. &dwBytesReturned,
  94. NULL);
  95. }
  96. BOOL EjectVolume(TCHAR cDriveLetter)
  97. {
  98. HANDLE hVolume;
  99. BOOL fAutoEject = FALSE;
  100. hVolume = OpenVolume(cDriveLetter);
  101. if (hVolume == INVALID_HANDLE_VALUE)
  102. return FALSE;
  103. // Lock and dismount the volume.
  104. if (LockVolume(hVolume) && DismountVolume(hVolume)) {
  105. // Set prevent removal to false and eject the volume.
  106. if (PreventRemovalOfVolume(hVolume, FALSE) && AutoEjectVolume(hVolume))
  107. fAutoEject = TRUE;
  108. }
  109. // Close the volume so other processes can use the drive.
  110. if (!CloseVolume(hVolume))
  111. return FALSE;
  112. if (fAutoEject) return TRUE;
  113. else return FALSE;
  114. }
  115. #else
  116. #include <stdio.h>
  117. #include <windows.h>
  118. #include <Setupapi.h>
  119. #include <winioctl.h>
  120. #include <winioctl.h>
  121. #include <cfgmgr32.h>
  122. #pragma comment(lib, "setupapi.lib")
  123. //-------------------------------------------------
  124. //----------------------------------------------------------------------
  125. // returns the device instance handle of a storage volume or 0 on error
  126. //----------------------------------------------------------------------
  127. static DEVINST GetDrivesDevInstByDeviceNumber(long DeviceNumber, UINT DriveType, const wchar_t* szDosDeviceName)
  128. {
  129. bool IsFloppy = (wcsstr(szDosDeviceName, L"\\Floppy") != NULL); // who knows a better way?
  130. GUID* guid;
  131. switch (DriveType) {
  132. case DRIVE_REMOVABLE:
  133. if ( IsFloppy ) {
  134. guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
  135. } else {
  136. guid = (GUID*)&GUID_DEVINTERFACE_DISK;
  137. }
  138. break;
  139. case DRIVE_FIXED:
  140. guid = (GUID*)&GUID_DEVINTERFACE_DISK;
  141. break;
  142. case DRIVE_CDROM:
  143. guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
  144. break;
  145. default:
  146. return 0;
  147. }
  148. // Get device interface info set handle for all devices attached to system
  149. HDEVINFO hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  150. if (hDevInfo == INVALID_HANDLE_VALUE) {
  151. return 0;
  152. }
  153. // Retrieve a context structure for a device interface of a device information set
  154. DWORD dwIndex = 0;
  155. long res;
  156. BYTE Buf[1024] = {0};
  157. PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
  158. SP_DEVICE_INTERFACE_DATA spdid;
  159. SP_DEVINFO_DATA spdd;
  160. DWORD dwSize;
  161. spdid.cbSize = sizeof(spdid);
  162. while ( true ) {
  163. res = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guid, dwIndex, &spdid);
  164. if ( !res ) {
  165. break;
  166. }
  167. dwSize = 0;
  168. SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, NULL, 0, &dwSize, NULL); // check the buffer size
  169. if ( dwSize!=0 && dwSize<=sizeof(Buf) ) {
  170. pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
  171. ZeroMemory(&spdd, sizeof(spdd));
  172. spdd.cbSize = sizeof(spdd);
  173. long res = SetupDiGetDeviceInterfaceDetail(hDevInfo, &spdid, pspdidd, dwSize, &dwSize, &spdd);
  174. if ( res ) {
  175. // in case you are interested in the USB serial number:
  176. // the device id string contains the serial number if the device has one,
  177. // otherwise a generated id that contains the '&' char...
  178. /*
  179. DEVINST DevInstParent = 0;
  180. CM_Get_Parent(&DevInstParent, spdd.DevInst, 0);
  181. char szDeviceIdString[MAX_PATH] = {0};
  182. CM_Get_Device_ID(DevInstParent, szDeviceIdString, MAX_PATH, 0);
  183. printf("DeviceId=%s\n", szDeviceIdString);
  184. */
  185. // open the disk or cdrom or floppy
  186. HANDLE hDrive = CreateFile(pspdidd->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
  187. if ( hDrive != INVALID_HANDLE_VALUE ) {
  188. // get its device number
  189. STORAGE_DEVICE_NUMBER sdn;
  190. DWORD dwBytesReturned = 0;
  191. res = DeviceIoControl(hDrive, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
  192. if ( res ) {
  193. if ( DeviceNumber == (long)sdn.DeviceNumber ) { // match the given device number with the one of the current device
  194. CloseHandle(hDrive);
  195. SetupDiDestroyDeviceInfoList(hDevInfo);
  196. return spdd.DevInst;
  197. }
  198. }
  199. CloseHandle(hDrive);
  200. }
  201. }
  202. }
  203. dwIndex++;
  204. }
  205. SetupDiDestroyDeviceInfoList(hDevInfo);
  206. return 0;
  207. }
  208. //-------------------------------------------------
  209. //-------------------------------------------------
  210. BOOL EjectVolume(TCHAR DriveLetter)
  211. {
  212. DriveLetter &= ~0x20; // uppercase
  213. if ( DriveLetter < 'A' || DriveLetter > 'Z' ) {
  214. return FALSE;
  215. }
  216. wchar_t szRootPath[] = L"X:\\"; // "X:\" -> for GetDriveType
  217. szRootPath[0] = DriveLetter;
  218. wchar_t szDevicePath[] = L"X:"; // "X:" -> for QueryDosDevice
  219. szDevicePath[0] = DriveLetter;
  220. wchar_t szVolumeAccessPath[] = L"\\\\.\\X:"; // "\\.\X:" -> to open the volume
  221. szVolumeAccessPath[4] = DriveLetter;
  222. long DeviceNumber = -1;
  223. // open the storage volume
  224. HANDLE hVolume = CreateFileW(szVolumeAccessPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
  225. if (hVolume == INVALID_HANDLE_VALUE) {
  226. return FALSE;
  227. }
  228. // get the volume's device number
  229. STORAGE_DEVICE_NUMBER sdn;
  230. DWORD dwBytesReturned = 0;
  231. long res = DeviceIoControl(hVolume, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &dwBytesReturned, NULL);
  232. if ( res ) {
  233. DeviceNumber = sdn.DeviceNumber;
  234. }
  235. CloseHandle(hVolume);
  236. if ( DeviceNumber == -1 ) {
  237. return FALSE;
  238. }
  239. // get the drive type which is required to match the device numbers correctely
  240. UINT DriveType = GetDriveType(szRootPath);
  241. // get the dos device name (like \device\floppy0) to decide if it's a floppy or not - who knows a better way?
  242. wchar_t szDosDeviceName[MAX_PATH] = {0};
  243. res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
  244. if ( !res ) {
  245. return FALSE;
  246. }
  247. // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number
  248. DEVINST DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber, DriveType, szDosDeviceName);
  249. if ( DevInst == 0 ) {
  250. return FALSE;
  251. }
  252. PNP_VETO_TYPE VetoType = PNP_VetoTypeUnknown;
  253. wchar_t VetoNameW[MAX_PATH] = {0};
  254. bool bSuccess = false;
  255. // get drives's parent, e.g. the USB bridge, the SATA port, an IDE channel with two drives!
  256. DEVINST DevInstParent = 0;
  257. res = CM_Get_Parent(&DevInstParent, DevInst, 0);
  258. for ( long tries=1; tries<=3; tries++ ) { // sometimes we need some tries...
  259. VetoNameW[0] = 0;
  260. // CM_Query_And_Remove_SubTree doesn't work for restricted users
  261. //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!
  262. //res = CM_Query_And_Remove_SubTreeW(DevInstParent, NULL, NULL, 0, CM_REMOVE_NO_RESTART); // with messagebox (W2K, Vista) or balloon (XP)
  263. res = CM_Request_Device_EjectW(DevInstParent, &VetoType, VetoNameW, MAX_PATH, 0);
  264. //res = CM_Request_Device_EjectW(DevInstParent, NULL, NULL, 0, 0); // with messagebox (W2K, Vista) or balloon (XP)
  265. bSuccess = (res==CR_SUCCESS && VetoType==PNP_VetoTypeUnknown);
  266. if ( bSuccess ) {
  267. break;
  268. }
  269. Sleep(500); // required to give the next tries a chance!
  270. }
  271. if ( bSuccess ) {
  272. printf("Success\n\n");
  273. return TRUE;
  274. }
  275. printf("failed\n");
  276. printf("Result=0x%2X\n", res);
  277. if ( VetoNameW[0] ) {
  278. printf("VetoName=%ws)\n\n", VetoNameW);
  279. }
  280. return FALSE;
  281. }
  282. //-----------------------------------------------------------
  283. #endif