Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

638 lines
15 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. ForceCDStop.cpp
  5. Abstract:
  6. This shim is used to fix the problem of contention with the CD Drive.
  7. Some applications try and access the CD even if they are in the middle of
  8. playing a movie or sound via MCI. Note that this shim assumes the app
  9. is running off of a single CDRom drive at a time.
  10. Notes:
  11. This is a general purpose shim.
  12. History:
  13. 04/10/2000 linstev Created
  14. 04/12/2000 a-michni Added _hread, ReadFile and _lseek capability.
  15. 04/28/2000 a-michni changed logic to check for IsACDRom before
  16. checking for a bad handle, this way CD letter
  17. is set for those routines which only have a
  18. handle and no way of finding the drive letter.
  19. 05/30/2000 a-chcoff Changed logic to do a cd stop only if error was device busy..
  20. we were checking every failed access and plane crazy was making
  21. lots of calls that would fail as file not found. as such the cd was
  22. getting stopped when it did not need to be, causing a CD not found.
  23. This shim should be changed to a faster model. maybe later..
  24. --*/
  25. #include "precomp.h"
  26. IMPLEMENT_SHIM_BEGIN(ForceCDStop)
  27. #include "ShimHookMacro.h"
  28. APIHOOK_ENUM_BEGIN
  29. APIHOOK_ENUM_ENTRY(FindFirstFileA)
  30. APIHOOK_ENUM_ENTRY(FindFirstFileW)
  31. APIHOOK_ENUM_ENTRY(FindFirstFileExA)
  32. APIHOOK_ENUM_ENTRY(FindFirstFileExW)
  33. APIHOOK_ENUM_ENTRY(CreateFileA)
  34. APIHOOK_ENUM_ENTRY(CreateFileW)
  35. APIHOOK_ENUM_ENTRY(ReadFile)
  36. APIHOOK_ENUM_ENTRY(_hread)
  37. APIHOOK_ENUM_ENTRY(_lseek)
  38. APIHOOK_ENUM_END
  39. // Include these so we can get to the IOCTLs
  40. #include <devioctl.h>
  41. #include <ntddcdrm.h>
  42. //
  43. // We have to store the first opened CD drive, so that if ReadFile fails, we
  44. // know which drive to stop. Note, we don't need to protect this variable with
  45. // a critical section, since it's basically atomic.
  46. //
  47. WCHAR g_wLastCDDrive = L'\0';
  48. /*++
  49. Initialize the global CD letter variable if required.
  50. --*/
  51. VOID
  52. InitializeCDA(
  53. LPSTR lpFileName
  54. )
  55. {
  56. CHAR cDrive;
  57. if (!g_wLastCDDrive) {
  58. if (GetDriveTypeFromFileNameA(lpFileName, &cDrive) == DRIVE_CDROM) {
  59. g_wLastCDDrive = (WCHAR)cDrive;
  60. }
  61. }
  62. }
  63. /*++
  64. Initialize the global CD letter variable if required.
  65. --*/
  66. VOID
  67. InitializeCDW(
  68. LPWSTR lpFileName
  69. )
  70. {
  71. WCHAR wDrive;
  72. if (!g_wLastCDDrive) {
  73. if (GetDriveTypeFromFileNameW(lpFileName, &wDrive) == DRIVE_CDROM) {
  74. g_wLastCDDrive = wDrive;
  75. }
  76. }
  77. }
  78. /*++
  79. Send a STOP IOCTL to the specified drive.
  80. --*/
  81. BOOL
  82. StopDrive(
  83. WCHAR wDrive
  84. )
  85. {
  86. BOOL bRet = FALSE;
  87. HANDLE hDrive;
  88. WCHAR wzCDROM[7] = L"\\\\.\\C:";
  89. wzCDROM[4] = wDrive;
  90. hDrive = CreateFileW(wzCDROM,
  91. GENERIC_READ,
  92. FILE_SHARE_READ,
  93. NULL,
  94. OPEN_EXISTING,
  95. FILE_ATTRIBUTE_NORMAL,
  96. NULL);
  97. if (hDrive != INVALID_HANDLE_VALUE) {
  98. DWORD dwBytesRead;
  99. // Attempt to stop the audio
  100. bRet = DeviceIoControl(hDrive,
  101. IOCTL_CDROM_STOP_AUDIO,
  102. NULL,
  103. 0,
  104. NULL,
  105. 0,
  106. &dwBytesRead,
  107. NULL);
  108. CloseHandle(hDrive);
  109. if (bRet) {
  110. DPFN( eDbgLevelInfo,
  111. "[StopDrive] Successfully stopped drive.\n");
  112. } else {
  113. DPFN( eDbgLevelError,
  114. "[StopDrive] Failed to stop drive. Error %d.\n", GetLastError());
  115. }
  116. } else {
  117. DPFN( eDbgLevelError,
  118. "[StopDrive] Unable to create cd device handle. %S Error %d.\n",
  119. wzCDROM, GetLastError());
  120. }
  121. return bRet;
  122. }
  123. /*++
  124. Attempts to stop the CD if filename is a file on a CDROM drive.
  125. Returns true on a successful stop.
  126. --*/
  127. BOOL
  128. StopCDA(
  129. LPCSTR lpFileName
  130. )
  131. {
  132. CHAR c;
  133. if (GetDriveTypeFromFileNameA(lpFileName, &c) == DRIVE_CDROM) {
  134. return StopDrive((WCHAR)c);
  135. } else {
  136. return FALSE;
  137. }
  138. }
  139. /*++
  140. Attempts to stop the CD if filename is a file on a CDROM drive.
  141. Returns true on a successful stop.
  142. --*/
  143. BOOL
  144. StopCDW(
  145. LPCWSTR lpFileName
  146. )
  147. {
  148. WCHAR w;
  149. if (GetDriveTypeFromFileNameW(lpFileName, &w) == DRIVE_CDROM) {
  150. return StopDrive(w);
  151. } else {
  152. return FALSE;
  153. }
  154. }
  155. /*++
  156. Attempts to stop the CD on the last opened CDROM Drive.
  157. Returns true on a successful stop.
  158. --*/
  159. BOOL
  160. StopCDH(
  161. HANDLE hFile
  162. )
  163. {
  164. if (g_wLastCDDrive) {
  165. return StopDrive(g_wLastCDDrive);
  166. } else {
  167. return FALSE;
  168. }
  169. }
  170. /*++
  171. Check for CD file.
  172. --*/
  173. HANDLE
  174. APIHOOK(FindFirstFileA)(
  175. LPCSTR lpFileName,
  176. LPWIN32_FIND_DATAA lpFindFileData
  177. )
  178. {
  179. HANDLE hRet = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  180. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  181. StopCDA(lpFileName);
  182. hRet = ORIGINAL_API(FindFirstFileA)(lpFileName, lpFindFileData);
  183. if (hRet == INVALID_HANDLE_VALUE) {
  184. DPFN( eDbgLevelWarning,
  185. "[FindFirstFileA] failure \"%s\" Error %d.\n",
  186. lpFileName, GetLastError());
  187. } else {
  188. LOGN(
  189. eDbgLevelInfo,
  190. "[FindFirstFileA] Success after CD stop: \"%s\".", lpFileName);
  191. }
  192. }
  193. return hRet;
  194. }
  195. /*++
  196. Check for CD file.
  197. --*/
  198. HANDLE
  199. APIHOOK(FindFirstFileW)(
  200. LPCWSTR lpFileName,
  201. LPWIN32_FIND_DATAW lpFindFileData
  202. )
  203. {
  204. HANDLE hRet = ORIGINAL_API(FindFirstFileW)(lpFileName, lpFindFileData);
  205. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  206. StopCDW(lpFileName);
  207. hRet = ORIGINAL_API(FindFirstFileW)(lpFileName, lpFindFileData);
  208. if (hRet == INVALID_HANDLE_VALUE) {
  209. DPFN( eDbgLevelWarning,
  210. "[FindFirstFileW] failure \"%S\" Error %d.\n",
  211. lpFileName, GetLastError());
  212. } else {
  213. LOGN(
  214. eDbgLevelInfo,
  215. "[FindFirstFileW] Success after CD stop: \"%S\".", lpFileName);
  216. }
  217. }
  218. return hRet;
  219. }
  220. /*++
  221. Check for CD file.
  222. --*/
  223. HANDLE
  224. APIHOOK(FindFirstFileExA)(
  225. LPCSTR lpFileName,
  226. FINDEX_INFO_LEVELS fInfoLevelId,
  227. LPVOID lpFindFileData,
  228. FINDEX_SEARCH_OPS fSearchOp,
  229. LPVOID lpSearchFilter,
  230. DWORD dwAdditionalFlags
  231. )
  232. {
  233. HANDLE hRet = ORIGINAL_API(FindFirstFileExA)(
  234. lpFileName,
  235. fInfoLevelId,
  236. lpFindFileData,
  237. fSearchOp,
  238. lpSearchFilter,
  239. dwAdditionalFlags);
  240. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  241. StopCDA(lpFileName);
  242. hRet = ORIGINAL_API(FindFirstFileExA)(
  243. lpFileName,
  244. fInfoLevelId,
  245. lpFindFileData,
  246. fSearchOp,
  247. lpSearchFilter,
  248. dwAdditionalFlags);
  249. if (hRet == INVALID_HANDLE_VALUE) {
  250. DPFN( eDbgLevelWarning,
  251. "[FindFirstFileExA] failure \"%s\" Error %d.\n",
  252. lpFileName, GetLastError());
  253. } else {
  254. LOGN(
  255. eDbgLevelInfo,
  256. "[FindFirstFileExA] Success after CD stop: \"%s\".", lpFileName);
  257. }
  258. }
  259. return hRet;
  260. }
  261. /*++
  262. Check for CD file.
  263. --*/
  264. HANDLE
  265. APIHOOK(FindFirstFileExW)(
  266. LPCWSTR lpFileName,
  267. FINDEX_INFO_LEVELS fInfoLevelId,
  268. LPVOID lpFindFileData,
  269. FINDEX_SEARCH_OPS fSearchOp,
  270. LPVOID lpSearchFilter,
  271. DWORD dwAdditionalFlags
  272. )
  273. {
  274. HANDLE hRet = ORIGINAL_API(FindFirstFileExW)(
  275. lpFileName,
  276. fInfoLevelId,
  277. lpFindFileData,
  278. fSearchOp,
  279. lpSearchFilter,
  280. dwAdditionalFlags);
  281. if ((hRet == INVALID_HANDLE_VALUE) && (ERROR_BUSY == GetLastError())) {
  282. StopCDW(lpFileName);
  283. hRet = ORIGINAL_API(FindFirstFileExW)(
  284. lpFileName,
  285. fInfoLevelId,
  286. lpFindFileData,
  287. fSearchOp,
  288. lpSearchFilter,
  289. dwAdditionalFlags);
  290. if (hRet == INVALID_HANDLE_VALUE) {
  291. DPFN( eDbgLevelWarning,
  292. "[FindFirstFileExW] failure \"%S\" Error %d.\n",
  293. lpFileName, GetLastError());
  294. } else {
  295. LOGN(
  296. eDbgLevelInfo,
  297. "[FindFirstFileExW] Success after CD stop: \"%S\".", lpFileName);
  298. }
  299. }
  300. return hRet;
  301. }
  302. /*++
  303. Check for CD file.
  304. --*/
  305. HANDLE
  306. APIHOOK(CreateFileA)(
  307. LPSTR lpFileName,
  308. DWORD dwDesiredAccess,
  309. DWORD dwShareMode,
  310. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  311. DWORD dwCreationDisposition,
  312. DWORD dwFlagsAndAttributes,
  313. HANDLE hTemplateFile
  314. )
  315. {
  316. HANDLE hRet = ORIGINAL_API(CreateFileA)(
  317. lpFileName,
  318. dwDesiredAccess,
  319. dwShareMode,
  320. lpSecurityAttributes,
  321. dwCreationDisposition,
  322. dwFlagsAndAttributes,
  323. hTemplateFile);
  324. InitializeCDA(lpFileName);
  325. if ((INVALID_HANDLE_VALUE == hRet) && (ERROR_BUSY == GetLastError())) {
  326. StopCDA(lpFileName);
  327. hRet = ORIGINAL_API(CreateFileA)(
  328. lpFileName,
  329. dwDesiredAccess,
  330. dwShareMode,
  331. lpSecurityAttributes,
  332. dwCreationDisposition,
  333. dwFlagsAndAttributes,
  334. hTemplateFile);
  335. if (hRet == INVALID_HANDLE_VALUE) {
  336. DPFN( eDbgLevelWarning,
  337. "[CreateFileA] failure \"%s\" Error %d.\n",
  338. lpFileName, GetLastError());
  339. } else {
  340. LOGN(
  341. eDbgLevelInfo,
  342. "[CreateFileA] Success after CD stop: \"%s\".", lpFileName);
  343. }
  344. }
  345. return hRet;
  346. }
  347. /*++
  348. Check for CD file.
  349. --*/
  350. HANDLE
  351. APIHOOK(CreateFileW)(
  352. LPWSTR lpFileName,
  353. DWORD dwDesiredAccess,
  354. DWORD dwShareMode,
  355. LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  356. DWORD dwCreationDisposition,
  357. DWORD dwFlagsAndAttributes,
  358. HANDLE hTemplateFile
  359. )
  360. {
  361. HANDLE hRet = ORIGINAL_API(CreateFileW)(
  362. lpFileName,
  363. dwDesiredAccess,
  364. dwShareMode,
  365. lpSecurityAttributes,
  366. dwCreationDisposition,
  367. dwFlagsAndAttributes,
  368. hTemplateFile);
  369. InitializeCDW(lpFileName);
  370. if ((INVALID_HANDLE_VALUE == hRet) && (ERROR_BUSY == GetLastError())) {
  371. StopCDW(lpFileName);
  372. hRet = ORIGINAL_API(CreateFileW)(
  373. lpFileName,
  374. dwDesiredAccess,
  375. dwShareMode,
  376. lpSecurityAttributes,
  377. dwCreationDisposition,
  378. dwFlagsAndAttributes,
  379. hTemplateFile);
  380. if (hRet == INVALID_HANDLE_VALUE) {
  381. DPFN( eDbgLevelWarning,
  382. "[CreateFileW] failure \"%S\" Error %d.\n",
  383. lpFileName, GetLastError());
  384. } else {
  385. LOGN(
  386. eDbgLevelInfo,
  387. "[CreateFileW] Success after CD stop: \"%S\".", lpFileName);
  388. }
  389. }
  390. return hRet;
  391. }
  392. /*++
  393. Check for _lseek error.
  394. --*/
  395. long
  396. APIHOOK(_lseek)(
  397. int handle,
  398. long offset,
  399. int origin
  400. )
  401. {
  402. long iRet = ORIGINAL_API(_lseek)(handle, offset, origin);
  403. if (iRet == -1L && IsOnCDRom((HANDLE)handle)) {
  404. StopCDH((HANDLE)handle);
  405. iRet = ORIGINAL_API(_lseek)(handle, offset, origin);
  406. if (iRet == -1L) {
  407. DPFN( eDbgLevelWarning,
  408. "[_lseek] failure: Error %d.\n", GetLastError());
  409. } else {
  410. LOGN(
  411. eDbgLevelInfo,
  412. "[_lseek] Success after CD stop.");
  413. }
  414. }
  415. return iRet;
  416. }
  417. /*++
  418. Check for _hread error.
  419. --*/
  420. long
  421. APIHOOK(_hread)(
  422. HFILE hFile,
  423. LPVOID lpBuffer,
  424. long lBytes
  425. )
  426. {
  427. long iRet = ORIGINAL_API(_hread)(hFile, lpBuffer, lBytes);
  428. if (iRet == HFILE_ERROR && IsOnCDRom((HANDLE)hFile)) {
  429. StopCDH((HANDLE)hFile);
  430. iRet = ORIGINAL_API(_hread)(hFile, lpBuffer, lBytes);
  431. if (iRet == HFILE_ERROR) {
  432. DPFN( eDbgLevelWarning,
  433. "[_hread] failure: Error %d.\n", GetLastError());
  434. } else {
  435. LOGN(
  436. eDbgLevelInfo,
  437. "[_hread] Success after CD stop.");
  438. }
  439. }
  440. return iRet;
  441. }
  442. /*++
  443. Check for ReadFile error.
  444. --*/
  445. BOOL
  446. APIHOOK(ReadFile)(
  447. HANDLE hFile,
  448. LPVOID lpBuffer,
  449. DWORD nNumberOfBytesToRead,
  450. LPDWORD lpNumberOfBytesRead,
  451. LPOVERLAPPED lpOverlapped
  452. )
  453. {
  454. BOOL bRet = ORIGINAL_API(ReadFile)(
  455. hFile,
  456. lpBuffer,
  457. nNumberOfBytesToRead,
  458. lpNumberOfBytesRead,
  459. lpOverlapped);
  460. if ((bRet == FALSE) && (ERROR_BUSY == GetLastError()) && IsOnCDRom(hFile)) {
  461. StopCDH(hFile);
  462. bRet = ORIGINAL_API(ReadFile)(
  463. hFile,
  464. lpBuffer,
  465. nNumberOfBytesToRead,
  466. lpNumberOfBytesRead,
  467. lpOverlapped);
  468. if (bRet == FALSE) {
  469. DPFN( eDbgLevelWarning,
  470. "[ReadFile] failure Error %d.\n", GetLastError());
  471. } else {
  472. LOGN(
  473. eDbgLevelInfo,
  474. "[ReadFile] Success after CD stop.");
  475. }
  476. }
  477. return bRet;
  478. }
  479. /*++
  480. Register hooked functions
  481. --*/
  482. HOOK_BEGIN
  483. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileA)
  484. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileW)
  485. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileExA)
  486. APIHOOK_ENTRY(KERNEL32.DLL, FindFirstFileExW)
  487. APIHOOK_ENTRY(KERNEL32.DLL, CreateFileA)
  488. APIHOOK_ENTRY(KERNEL32.DLL, CreateFileW)
  489. APIHOOK_ENTRY(KERNEL32.DLL, ReadFile)
  490. APIHOOK_ENTRY(KERNEL32.DLL, _hread)
  491. APIHOOK_ENTRY(LIBC.DLL, _lseek)
  492. HOOK_END
  493. IMPLEMENT_SHIM_END