Windows NT 4.0 source code leak
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.

2256 lines
66 KiB

4 years ago
  1. /*****************************Module*Header*********************************\
  2. * Module Name: mcisys.c
  3. *
  4. * Media Control Architecture System Functions
  5. *
  6. * Created: 2/28/90
  7. * Author: DLL (DavidLe)
  8. * 5/22/91: Ported to Win32 - NigelT
  9. *
  10. * History:
  11. * Mar 92 SteveDav - brought up to Win 3.1 ship level
  12. *
  13. * Copyright (c) 1992 Microsoft Corporation
  14. *
  15. \******************************************************************************/
  16. #define UNICODE
  17. #define _CTYPE_DISABLE_MACROS
  18. #include "winmmi.h"
  19. #include "mci.h"
  20. #include "wchar.h"
  21. #include "ctype.h"
  22. extern WSZCODE wszOpen[]; // in MCI.C
  23. STATICDT WSZCODE wszMciExtensions[] = L"Mci Extensions";
  24. #define MCI_EXTENSIONS wszMciExtensions
  25. #define MCI_PROFILE_STRING_LENGTH 255
  26. //#define TOLOWER(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : c)
  27. // The device list is initialized on the first call to mciSendCommand or
  28. // to mciSendString or to mciGetDeviceID or to mciGetErrorString
  29. // We could do it when WINMM is loaded - but that is a bit excessive.
  30. // The user may not need MCI functions.
  31. BOOL MCI_bDeviceListInitialized = FALSE;
  32. // The next device ID to use for a new device
  33. MCIDEVICEID MCI_wNextDeviceID = 1;
  34. // The list of MCI devices. This list grows and shrinks as needed.
  35. // The first offset MCI_lpDeviceList[0] is a placeholder and is unused
  36. // because device 0 is defined as no device.
  37. LPMCI_DEVICE_NODE FAR * MCI_lpDeviceList = NULL;
  38. // The current size of the list of MCI devices
  39. UINT MCI_wDeviceListSize = 0;
  40. #if 0 // we don't use this (NigelT)
  41. // The internal mci heap used by mciAlloc and mciFree
  42. HANDLE hMciHeap = NULL;
  43. #endif
  44. STATICDT WSZCODE wszAllDeviceName[] = L"all";
  45. STATICDT WSZCODE szUnsignedFormat[] = L"%u";
  46. STATICFN void mciFreeDevice(LPMCI_DEVICE_NODE nodeWorking);
  47. //------------------------------------------------------------------
  48. // Initialize device list
  49. // Called once by mciSendString or mciSendCommand
  50. // Returns TRUE on success
  51. //------------------------------------------------------------------
  52. BOOL mciInitDeviceList(void)
  53. {
  54. BOOL fReturn=FALSE;
  55. #if 0 // we don't use this (NigelT)
  56. if ((hMciHeap = HeapCreate(0)) == 0)
  57. {
  58. dprintf1(("Mci heap create failed!"));
  59. return FALSE;
  60. }
  61. #endif
  62. try {
  63. mciEnter("mciInitDeviceList");
  64. if (!MCI_bDeviceListInitialized) {
  65. // We have to retest the init flag to be totally thread safe.
  66. // Otherwise in theory we could end up initializing twice.
  67. if ((MCI_lpDeviceList = mciAlloc( sizeof (LPMCI_DEVICE_NODE) *
  68. (MCI_INIT_DEVICE_LIST_SIZE + 1))) != NULL)
  69. {
  70. MCI_wDeviceListSize = MCI_INIT_DEVICE_LIST_SIZE;
  71. MCI_bDeviceListInitialized = TRUE;
  72. fReturn = TRUE;
  73. } else {
  74. dprintf1(("MCIInit: could not allocate master MCI device list"));
  75. fReturn = FALSE;
  76. }
  77. }
  78. } finally {
  79. mciLeave("mciInitDeviceList");
  80. }
  81. return(fReturn);
  82. }
  83. /*
  84. * @doc EXTERNAL MCI
  85. * @api MCIDEVICEID | mciGetDeviceIDFromElementID | This function
  86. * retrieves the MCI device ID corresponding to and element ID
  87. *
  88. * @parm DWORD | dwElementID | The element ID
  89. *
  90. * @parm LPCTSTR | lpstrType | The type name this element ID belongs to
  91. *
  92. * @rdesc Returns the device ID assigned when it was opened and used in the
  93. * <f mciSendCommand> function. Returns zero if the device name was not known,
  94. * if the device was not open, or if there was not enough memory to complete
  95. * the operation or if lpstrType is NULL.
  96. *
  97. */
  98. MCIDEVICEID APIENTRY mciGetDeviceIDFromElementIDA (
  99. DWORD dwElementID,
  100. LPCSTR lpstrType)
  101. {
  102. LPCWSTR lpwstr;
  103. MCIDEVICEID mr;
  104. lpwstr = AllocUnicodeStr( (LPSTR)lpstrType );
  105. if ( lpwstr == NULL ) {
  106. return (MCIDEVICEID)NULL;
  107. }
  108. mr = mciGetDeviceIDFromElementIDW( dwElementID, lpwstr );
  109. FreeUnicodeStr( (LPWSTR)lpwstr );
  110. return mr;
  111. }
  112. MCIDEVICEID APIENTRY mciGetDeviceIDFromElementIDW (
  113. DWORD dwElementID,
  114. LPCWSTR lpstrType)
  115. {
  116. MCIDEVICEID wID;
  117. LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
  118. WCHAR strTemp[MCI_MAX_DEVICE_TYPE_LENGTH];
  119. if (lpstrType == NULL) {
  120. return 0;
  121. }
  122. mciEnter("mciGetDeviceIDFromElementID");
  123. nodeCounter = &MCI_lpDeviceList[1];
  124. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  125. {
  126. if (NULL == (nodeWorking = *nodeCounter++)) {
  127. continue;
  128. }
  129. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID &&
  130. nodeWorking->dwElementID == dwElementID) {
  131. if (LoadStringW( ghInst, nodeWorking->wDeviceType, strTemp,
  132. sizeof(strTemp) / sizeof(WCHAR) ) != 0
  133. && lstrcmpiW( strTemp, lpstrType) == 0) {
  134. mciLeave("mciGetDeviceIDFromElementID");
  135. return wID;
  136. }
  137. }
  138. }
  139. mciLeave("mciGetDeviceIDFromElementID");
  140. return 0;
  141. }
  142. // Retrieves the device ID corresponding to the name of an opened device
  143. // matching the given task
  144. STATICFN MCIDEVICEID mciGetDeviceIDInternal (
  145. LPCWSTR lpstrName,
  146. HANDLE hCurrentTask)
  147. {
  148. MCIDEVICEID wID;
  149. LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
  150. #if DBG
  151. if (!lpstrName) {
  152. dprintf(("!! NULL POINTER !! Internal error"));
  153. return(0);
  154. }
  155. #endif
  156. if ( lstrcmpiW(wszAllDeviceName, lpstrName) == 0)
  157. return MCI_ALL_DEVICE_ID;
  158. if (MCI_lpDeviceList == NULL)
  159. return 0;
  160. // Loop through the MCI device list. Skip any 16-bit devices.
  161. mciEnter("mciGetDeviceIDInternal");
  162. nodeCounter = &MCI_lpDeviceList[1];
  163. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  164. {
  165. if (NULL == (nodeWorking = *nodeCounter++)) {
  166. continue;
  167. }
  168. // If this device is 16-bit then skip it
  169. if (nodeWorking->dwMCIFlags & MCINODE_16BIT_DRIVER) {
  170. continue;
  171. }
  172. // If this device does not have a name then skip it
  173. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID) {
  174. continue;
  175. }
  176. // If the names match, and the previous device is not being closed
  177. if ( lstrcmpiW( nodeWorking->lpstrName, lpstrName ) == 0 ) {
  178. if (ISAUTOCLOSING(nodeWorking))
  179. {
  180. // As this auto opened device is being closed we do not match
  181. // against its name. The result is that a new auto opened
  182. // device will be used. This would be the case if this
  183. // command was issued momentarily later by which time we
  184. // would have finished closing the existing device.
  185. } else {
  186. // If the device belongs to the current task
  187. if (nodeWorking->hOpeningTask == hCurrentTask ||
  188. nodeWorking->hCreatorTask == hCurrentTask) {
  189. // Return this device ID
  190. mciLeave("mciGetDeviceIDInternal");
  191. return wID;
  192. }
  193. }
  194. }
  195. }
  196. mciLeave("mciGetDeviceIDInternal");
  197. return 0;
  198. }
  199. /*
  200. * @doc EXTERNAL MCI
  201. * @api MCIDEVICEID | mciGetDeviceID | This function retrieves the device
  202. * ID corresponding to the name of an opened device.
  203. *
  204. * @parm LPCTSTR | lpstrName | Points to the device name from SYSTEM.INI, or
  205. * the alias name by which the device is known.
  206. *
  207. * @rdesc Returns the device ID assigned when it was opened and used in the
  208. * <f mciSendCommand> function. Returns zero if the device name was not known,
  209. * if the device was not open, or if there was not enough memory to complete
  210. * the operation. Each compound device element has a unique device ID.
  211. * The ID of the "all" device is MCI_ALL_DEVICE_ID
  212. *
  213. * @xref MCI_OPEN
  214. *
  215. */
  216. MCIDEVICEID mciGetDeviceIDW (
  217. LPCWSTR lpstrName)
  218. {
  219. return mciGetDeviceIDInternal (lpstrName, GetCurrentTask());
  220. }
  221. MCIDEVICEID mciGetDeviceIDA (
  222. LPCSTR lpstrName)
  223. {
  224. LPCWSTR lpwstr;
  225. MCIDEVICEID mr;
  226. lpwstr = AllocUnicodeStr( (LPSTR)lpstrName );
  227. if ( lpwstr == NULL ) {
  228. return (MCIDEVICEID)NULL;
  229. }
  230. mr = mciGetDeviceIDInternal( lpwstr, GetCurrentTask() );
  231. FreeUnicodeStr( (LPWSTR)lpwstr );
  232. return mr;
  233. }
  234. /*
  235. * @doc EXTERNAL MCI
  236. * @api HMODULE | mciGetCreatorTask | This function retrieves the creator task
  237. * corresponding with the device ID passed.
  238. *
  239. * @parm MCIDEVICEID | wDeviceID | Specifies the device ID whose creator task is to
  240. * be returned.
  241. *
  242. * @rdesc Returns the creator task responsible for opening the device, else
  243. * NULL if the device ID passed is invalid.
  244. *
  245. */
  246. HTASK APIENTRY mciGetCreatorTask (
  247. MCIDEVICEID wDeviceID)
  248. {
  249. HTASK hCreatorTask;
  250. mciEnter("mciGetCreatorTask");
  251. if (MCI_VALID_DEVICE_ID(wDeviceID)) {
  252. hCreatorTask = MCI_lpDeviceList[wDeviceID]->hCreatorTask;
  253. } else {
  254. hCreatorTask = NULL;
  255. }
  256. mciLeave("mciGetCreatorTask");
  257. return hCreatorTask;
  258. }
  259. /*
  260. * @doc INTERNAL MCI
  261. * @api BOOL FAR | mciDeviceMatch | Match the first string with the second.
  262. * Any single trailing digit on the first string is ignored. Each string
  263. * must have at least one character
  264. *
  265. * @parm LPWSTR | lpstrDeviceName | The device name, possibly
  266. * with trailing digits but no blanks.
  267. *
  268. * @parm LPWSTR | lpstrDeviceType | The device type with no trailing digits
  269. * or blanks
  270. *
  271. * @rdesc TRUE if the strings match the above test, FALSE otherwise
  272. *
  273. */
  274. STATICFN BOOL mciDeviceMatch (
  275. LPCWSTR lpstrDeviceName,
  276. LPCWSTR lpstrDeviceType)
  277. {
  278. BOOL bRetVal = TRUE, bAtLeastOne = FALSE;
  279. // Scan until one of the strings ends
  280. dprintf2(("mciDeviceMatch: %ls Vs %ls",lpstrDeviceName,lpstrDeviceType));
  281. while (*lpstrDeviceName != '\0' && *lpstrDeviceType != '\0') {
  282. if (towlower(*lpstrDeviceName++) == towlower(*lpstrDeviceType++)) {
  283. bAtLeastOne = TRUE;
  284. } else {
  285. break;
  286. }
  287. }
  288. // If end of device type, scan to the end of device name, trailing digits
  289. // are OK
  290. if (!bAtLeastOne || *lpstrDeviceType != '\0') {
  291. return FALSE;
  292. }
  293. while (*lpstrDeviceName != '\0')
  294. {
  295. // No match, but that is OK if a digit trails
  296. // Is the remainder of the string a digit? We could check using
  297. // a simple if test (<0 or >9) but that would run into problems if
  298. // anyone ever passed a unicode "numeric" string outside the ascii
  299. // number range. Using isdigit should be safer if marginally slower.
  300. #ifdef UNICODE
  301. #ifdef LATER
  302. BUGBUG
  303. This must be fixed. Currently the unicode isdigit function is broken.
  304. Raid bug 14443/1830
  305. #endif
  306. if (0==(*((LPSTR)lpstrDeviceName)+1) && !isdigit(*(LPSTR)lpstrDeviceName)) {
  307. #else
  308. if (!isdigit(*lpstrDeviceName)) {
  309. #endif
  310. // No match - a non digit trails
  311. return FALSE;
  312. }
  313. ++lpstrDeviceName;
  314. }
  315. return TRUE;
  316. }
  317. /*
  318. * @doc INTERNAL MCI
  319. * @api UINT | mciLookUpType | Look up the type given a type name
  320. *
  321. * @parm LPCWSTR | lpstrTypeName | The type name to look up. Trailing
  322. * digits are ignored.
  323. *
  324. * @rdesc The MCI type number (MCI_DEVTYPE_<x>) or 0 if not found
  325. *
  326. */
  327. UINT mciLookUpType (
  328. LPCWSTR lpstrTypeName)
  329. {
  330. UINT wType;
  331. WCHAR strType[MCI_MAX_DEVICE_TYPE_LENGTH];
  332. for (wType = MCI_DEVTYPE_FIRST; wType <= MCI_DEVTYPE_LAST; ++wType)
  333. {
  334. if ( LoadStringW( ghInst,
  335. wType,
  336. strType,
  337. sizeof(strType) / sizeof(WCHAR) ) == 0)
  338. {
  339. dprintf1(("mciLookUpType: could not load string for type"));
  340. continue;
  341. }
  342. if (mciDeviceMatch (lpstrTypeName, strType)) {
  343. return wType;
  344. }
  345. }
  346. return 0;
  347. }
  348. /*
  349. * @doc INTERNAL MCI
  350. * @api DWORD | mciSysinfo | Get system information about a device
  351. *
  352. * @parm MCIDEVICEID | wDeviceID | Device ID, may be 0
  353. *
  354. * @parm DWORD | dwFlags | SYSINFO flags
  355. *
  356. * @parm LPMCI_SYSINFO_PARMS | lpSysinfo | SYSINFO parameters
  357. *
  358. * @rdesc 0 if successful, otherwise error code
  359. *
  360. */
  361. DWORD mciSysinfo (
  362. MCIDEVICEID wDeviceID,
  363. DWORD dwFlags,
  364. LPMCI_SYSINFO_PARMSW lpSysinfo)
  365. {
  366. UINT nCounted;
  367. WCHAR strBuffer[MCI_PROFILE_STRING_LENGTH];
  368. LPWSTR lpstrBuffer = strBuffer, lpstrStart;
  369. if (dwFlags & MCI_SYSINFO_NAME && lpSysinfo->dwNumber == 0)
  370. return MCIERR_OUTOFRANGE;
  371. if (lpSysinfo->lpstrReturn == NULL || lpSysinfo->dwRetSize == 0)
  372. return MCIERR_PARAM_OVERFLOW;
  373. #ifdef LATER
  374. // if ((dwFlags & (MCI_SYSINFO_NAME | MCI_SYSINFO_INSTALLNAME))
  375. // && (dwFlags & MCI_SYSINFO_QUANTITY))
  376. // Should be invalid to ask for Quantity and any sort of name
  377. #endif
  378. if (dwFlags & MCI_SYSINFO_NAME && dwFlags & MCI_SYSINFO_QUANTITY)
  379. return MCIERR_FLAGS_NOT_COMPATIBLE;
  380. if (dwFlags & MCI_SYSINFO_INSTALLNAME)
  381. {
  382. LPMCI_DEVICE_NODE nodeWorking;
  383. if (wDeviceID == MCI_ALL_DEVICE_ID)
  384. return MCIERR_CANNOT_USE_ALL;
  385. mciEnter("mciSysinfo");
  386. if (!MCI_VALID_DEVICE_ID (wDeviceID)) {
  387. mciLeave("mciSysinfo");
  388. return MCIERR_INVALID_DEVICE_NAME;
  389. }
  390. #if DBG
  391. if ((nodeWorking = MCI_lpDeviceList[wDeviceID]) == NULL ||
  392. nodeWorking->lpstrInstallName == NULL)
  393. {
  394. dprintf1(("mciSysinfo: NULL device node or installname"));
  395. mciLeave("mciSysinfo");
  396. return MCIERR_INTERNAL;
  397. }
  398. #else
  399. nodeWorking = MCI_lpDeviceList[wDeviceID];
  400. #endif
  401. if ( (DWORD)wcslen( nodeWorking->lpstrInstallName ) >=
  402. lpSysinfo->dwRetSize )
  403. {
  404. mciLeave("mciSysinfo");
  405. return MCIERR_PARAM_OVERFLOW;
  406. }
  407. wcscpy (lpSysinfo->lpstrReturn, nodeWorking->lpstrInstallName);
  408. mciLeave("mciSysinfo");
  409. return 0;
  410. } else if (!(dwFlags & MCI_SYSINFO_OPEN))
  411. {
  412. if (wDeviceID != MCI_ALL_DEVICE_ID &&
  413. lpSysinfo->wDeviceType == 0) {
  414. return MCIERR_DEVICE_TYPE_REQUIRED;
  415. }
  416. if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
  417. return MCIERR_MISSING_PARAMETER;
  418. GetPrivateProfileStringW( MCI_HANDLERS, NULL, wszNull,
  419. lpstrBuffer,
  420. MCI_PROFILE_STRING_LENGTH,
  421. MCIDRIVERS_INI_FILE);
  422. nCounted = 0;
  423. while (TRUE)
  424. {
  425. if (dwFlags & MCI_SYSINFO_QUANTITY)
  426. {
  427. if (*lpstrBuffer == '\0')
  428. {
  429. if ( (lpSysinfo->lpstrReturn == NULL) ||
  430. (sizeof(DWORD) > lpSysinfo->dwRetSize))
  431. return MCIERR_PARAM_OVERFLOW;
  432. *(UNALIGNED DWORD *)lpSysinfo->lpstrReturn = (DWORD)nCounted;
  433. return MCI_INTEGER_RETURNED;
  434. }
  435. if (wDeviceID == MCI_ALL_DEVICE_ID ||
  436. mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
  437. ++nCounted;
  438. // Skip past the terminating '\0'
  439. while (*lpstrBuffer++ != '\0') {}
  440. }
  441. else if (dwFlags & MCI_SYSINFO_NAME) // if test is redundant
  442. {
  443. if (nCounted == lpSysinfo->dwNumber)
  444. {
  445. /* NOTE:
  446. * We know that lpSysinfo->dwNumber > 0
  447. * Hence we will have been through the loop at least once
  448. * Hence lpstrStart has been set up
  449. */
  450. if ( (DWORD)wcslen( lpstrStart ) >= lpSysinfo->dwRetSize )
  451. {
  452. return MCIERR_PARAM_OVERFLOW;
  453. }
  454. wcscpy (lpSysinfo->lpstrReturn, lpstrStart);
  455. return 0L;
  456. } else if (*lpstrBuffer == '\0')
  457. return MCIERR_OUTOFRANGE;
  458. else
  459. {
  460. lpstrStart = lpstrBuffer;
  461. if (wDeviceID == MCI_ALL_DEVICE_ID ||
  462. mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
  463. ++nCounted;
  464. // Skip past the terminating '\0'
  465. while (*lpstrBuffer++ != '\0') {}
  466. }
  467. }
  468. }
  469. } else
  470. // Process MCI_SYSINFO_OPEN cases
  471. {
  472. MCIDEVICEID wID;
  473. HANDLE hCurrentTask = GetCurrentTask();
  474. LPMCI_DEVICE_NODE Node;
  475. if (wDeviceID != MCI_ALL_DEVICE_ID &&
  476. lpSysinfo->wDeviceType == 0)
  477. return MCIERR_DEVICE_TYPE_REQUIRED;
  478. if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
  479. return MCIERR_MISSING_PARAMETER;
  480. nCounted = 0;
  481. mciEnter("mciSysinfo");
  482. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  483. {
  484. if ((Node = MCI_lpDeviceList[wID]) == 0)
  485. continue;
  486. if (wDeviceID == MCI_ALL_DEVICE_ID &&
  487. Node->hOpeningTask == hCurrentTask) {
  488. ++nCounted;
  489. }
  490. else
  491. {
  492. if (Node->wDeviceType == lpSysinfo->wDeviceType &&
  493. Node->hOpeningTask == hCurrentTask)
  494. ++nCounted;
  495. }
  496. if (dwFlags & MCI_SYSINFO_NAME &&
  497. nCounted == lpSysinfo->dwNumber)
  498. {
  499. DWORD dwReturn;
  500. if ( (DWORD)wcslen( Node->lpstrName ) >= lpSysinfo->dwRetSize )
  501. {
  502. dwReturn = MCIERR_PARAM_OVERFLOW;
  503. } else {
  504. wcscpy (lpSysinfo->lpstrReturn, Node->lpstrName);
  505. dwReturn = 0;
  506. }
  507. mciLeave("mciSysinfo");
  508. return dwReturn;
  509. }
  510. }
  511. mciLeave("mciSysinfo");
  512. if (dwFlags & MCI_SYSINFO_NAME)
  513. {
  514. if (lpSysinfo->lpstrReturn != NULL)
  515. lpSysinfo->lpstrReturn = '\0';
  516. return MCIERR_OUTOFRANGE;
  517. } else if (dwFlags & MCI_SYSINFO_QUANTITY && // checking for QUANTITY is redundant
  518. lpSysinfo->lpstrReturn != NULL &&
  519. lpSysinfo->dwRetSize >= 4) {
  520. *(UNALIGNED DWORD *)lpSysinfo->lpstrReturn = nCounted;
  521. return MCI_INTEGER_RETURNED;
  522. }
  523. }
  524. return MCIERR_PARAM_OVERFLOW;
  525. }
  526. /*
  527. * @doc INTERNAL MCI
  528. * @api MCIDEVICEID | wReserveDeviceID | Copy the given global handle into the
  529. * first free entry in the MCI device table and return that entry's ID#
  530. *
  531. * @parm HANDLE | hNode | Local handle to device description
  532. *
  533. * @rdesc The ID value that has been reserved for this device or 0 if
  534. * there are no more free entries
  535. *
  536. */
  537. STATICFN MCIDEVICEID wReserveDeviceID (
  538. LPMCI_DEVICE_NODE node)
  539. {
  540. UINT wDeviceID;
  541. LPMCI_DEVICE_NODE FAR *lpTempList;
  542. mciEnter("wReserveDeviceID");
  543. // Search for an empty slot
  544. for (wDeviceID = 1; wDeviceID < MCI_wNextDeviceID; ++wDeviceID)
  545. if (MCI_lpDeviceList[wDeviceID] == NULL) {
  546. goto slot_found;
  547. }
  548. // No empty slots found so add to end
  549. if (wDeviceID >= MCI_wDeviceListSize)
  550. {
  551. // The list is full (or non existent) so try to grow it
  552. if ((lpTempList = mciReAlloc (MCI_lpDeviceList,
  553. sizeof (LPMCI_DEVICE_NODE) * (MCI_wDeviceListSize + 1 +
  554. MCI_DEVICE_LIST_GROW_SIZE)))
  555. == NULL)
  556. {
  557. dprintf1(("wReserveDeviceID: cannot grow device list"));
  558. mciLeave("wReserveDeviceID");
  559. return 0;
  560. }
  561. MCI_lpDeviceList = lpTempList;
  562. MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
  563. }
  564. ++MCI_wNextDeviceID;
  565. slot_found:;
  566. MCI_lpDeviceList[wDeviceID] = node;
  567. mciLeave("wReserveDeviceID");
  568. return (MCIDEVICEID)wDeviceID;
  569. }
  570. //
  571. // Allocate space for the given string and assign the name to the given
  572. // device.
  573. // Return FALSE if could not allocate memory
  574. //
  575. STATICFN BOOL NEAR mciAddDeviceName(
  576. LPMCI_DEVICE_NODE nodeWorking,
  577. LPCWSTR lpDeviceName)
  578. {
  579. nodeWorking->lpstrName = (LPWSTR)mciAlloc(
  580. BYTE_GIVEN_CHAR( wcslen(lpDeviceName) + 1 ) );
  581. if (nodeWorking->lpstrName == NULL)
  582. {
  583. dprintf1(("mciAddDeviceName: Out of memory allocating device name"));
  584. return FALSE;
  585. }
  586. // copy device name to mci node and lowercase it
  587. wcscpy(nodeWorking->lpstrName, (LPWSTR)lpDeviceName);
  588. //!! mciToLower(nodeWorking->lpstrName);
  589. return TRUE;
  590. }
  591. /*
  592. * @doc INTERNAL MCI
  593. * @api HANDLE | mciAllocateNode | Allocate a new driver entry
  594. *
  595. * @parm DWORD | dwFlags | As sent with MCI_OPEN message
  596. * @parm LPCWSTR | lpDeviceName | The device name
  597. * @parm LPMCI_DEVICE_NODE * | *lpNewNode | Return pointer location
  598. *
  599. * @rdesc The device ID to the new node. 0 on error.
  600. *
  601. * @comm Leaves the new node locked
  602. *
  603. */
  604. STATICFN MCIDEVICEID NEAR mciAllocateNode (
  605. DWORD dwFlags,
  606. LPCWSTR lpDeviceName,
  607. LPMCI_DEVICE_NODE FAR *lpnodeNew)
  608. {
  609. LPMCI_DEVICE_NODE nodeWorking;
  610. if ((nodeWorking = mciAlloc(sizeof(MCI_DEVICE_NODE))) == NULL)
  611. {
  612. dprintf1(("Out of memory in mciAllocateNode"));
  613. return 0;
  614. }
  615. /* Fill in the new node */
  616. /* Get a new device ID, if there are none available then bail */
  617. if ((nodeWorking->wDeviceID = wReserveDeviceID(nodeWorking)) == 0)
  618. {
  619. dprintf1(("mciAllocateNode: Cannot allocate new node"));
  620. mciFree(nodeWorking);
  621. return 0;
  622. }
  623. // Initialize node
  624. nodeWorking->dwMCIOpenFlags = dwFlags;
  625. nodeWorking->hCreatorTask = GetCurrentTask ();
  626. nodeWorking->hOpeningTask = nodeWorking->hCreatorTask;
  627. // The new node is zeroed
  628. // nodeWorking->fpYieldProc = NULL;
  629. // nodeWorking->dwMCIFlags = 0;
  630. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  631. // No device name, just an element ID
  632. nodeWorking->dwElementID = (DWORD)lpDeviceName;
  633. else
  634. if (!mciAddDeviceName (nodeWorking, lpDeviceName))
  635. {
  636. mciFree (nodeWorking);
  637. return 0;
  638. }
  639. *lpnodeNew = nodeWorking;
  640. return nodeWorking->wDeviceID;
  641. }
  642. //
  643. // Reparse the original command parameters
  644. // Returns MCIERR code. If the reparse fails the original error code
  645. // from the first parsing is returned.
  646. //
  647. STATICFN UINT mciReparseOpen (
  648. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
  649. UINT wCustomTable,
  650. UINT wTypeTable,
  651. LPDWORD lpdwFlags,
  652. LPMCI_OPEN_PARMSW FAR *lplpOpen,
  653. MCIDEVICEID wDeviceID)
  654. {
  655. LPWSTR lpCommand;
  656. LPDWORD lpdwParams = NULL;
  657. UINT wErr;
  658. UINT wTable = wCustomTable;
  659. DWORD dwOldFlags = *lpdwFlags;
  660. // If the custom table contains no open command
  661. if (wCustomTable == MCI_TABLE_NOT_PRESENT ||
  662. (lpCommand = FindCommandInTable (wCustomTable, wszOpen, NULL)) == NULL)
  663. {
  664. // Try the type specific table
  665. lpCommand = FindCommandInTable (wTypeTable, wszOpen, NULL);
  666. // If it still cannot be parsed
  667. if (lpCommand == NULL)
  668. return lpOpenInfo->wParsingError;
  669. wCustomTable = wTypeTable;
  670. }
  671. // A new version of 'open' was found
  672. // Free previous set of parameters
  673. mciParserFree (lpOpenInfo->lpstrPointerList);
  674. *lpdwFlags = 0;
  675. if ((lpdwParams =
  676. (LPDWORD)mciAlloc (sizeof(DWORD) * MCI_MAX_PARAM_SLOTS))
  677. == NULL)
  678. return MCIERR_OUT_OF_MEMORY;
  679. wErr = mciParseParams ( MCI_OPEN ,
  680. lpOpenInfo->lpstrParams, lpCommand,
  681. lpdwFlags,
  682. (LPWSTR)lpdwParams,
  683. sizeof(DWORD) * MCI_MAX_PARAM_SLOTS,
  684. &lpOpenInfo->lpstrPointerList, NULL);
  685. // We don't need this around anymore
  686. mciUnlockCommandTable (wTable);
  687. // If there was a parsing error
  688. if (wErr != 0)
  689. {
  690. // Close device down
  691. mciCloseDevice (wDeviceID, 0L, NULL, FALSE);
  692. // Make sure this does not get free'd by mciSendString
  693. lpOpenInfo->lpstrPointerList = NULL;
  694. mciFree (lpdwParams);
  695. return wErr;
  696. }
  697. if (dwOldFlags & MCI_OPEN_TYPE)
  698. {
  699. // Device type was already extracted so add it manually
  700. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrDeviceType
  701. = (*lplpOpen)->lpstrDeviceType;
  702. *lpdwFlags |= MCI_OPEN_TYPE;
  703. }
  704. if (dwOldFlags & MCI_OPEN_ELEMENT)
  705. {
  706. // Element name was already extracted so add it manually
  707. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrElementName
  708. = (*lplpOpen)->lpstrElementName;
  709. *lpdwFlags |= MCI_OPEN_ELEMENT;
  710. }
  711. if (dwOldFlags & MCI_OPEN_ALIAS)
  712. {
  713. // Alias name was already extracted so add it manually
  714. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrAlias
  715. = (*lplpOpen)->lpstrAlias;
  716. *lpdwFlags |= MCI_OPEN_ALIAS;
  717. }
  718. if (dwOldFlags & MCI_NOTIFY)
  719. // Notify was already extracted so add it manually
  720. ((LPMCI_OPEN_PARMSW)lpdwParams)->dwCallback
  721. = (*lplpOpen)->dwCallback;
  722. // Replace old parameter list with new list
  723. *lplpOpen = (LPMCI_OPEN_PARMSW)lpdwParams;
  724. return 0;
  725. }
  726. //**************************************************************************
  727. // mciFindDriverName
  728. //
  729. // See if lpstrDriverName exists in the profile strings of the [mci]
  730. // section and return the keyname in lpstrDevice and the
  731. // profile string in lpstrProfString
  732. // Returns 0 on success or an error code
  733. //**************************************************************************
  734. STATICFN DWORD mciFindDriverName (
  735. LPCWSTR lpstrDriverName,
  736. LPWSTR lpstrDevice,
  737. LPWSTR lpstrProfString,
  738. UINT wProfLength) // this should be a character count
  739. {
  740. LPWSTR lpstrEnum, lpstrEnumStart;
  741. UINT wEnumLen = 100;
  742. DWORD wErr;
  743. LPWSTR lpstrDriverTemp, lpstrProfTemp;
  744. // Enumerate values, trying until they fit into the buffer
  745. while (TRUE) {
  746. if ((lpstrEnum = mciAlloc( BYTE_GIVEN_CHAR(wEnumLen) ) ) == NULL)
  747. return MCIERR_OUT_OF_MEMORY;
  748. wErr = GetPrivateProfileStringW( MCI_HANDLERS,
  749. NULL, wszNull,
  750. lpstrEnum,
  751. wEnumLen,
  752. MCIDRIVERS_INI_FILE );
  753. if (*lpstrEnum == '\0')
  754. {
  755. mciFree (lpstrEnum);
  756. return MCIERR_DEVICE_NOT_INSTALLED;
  757. }
  758. if (wErr == wEnumLen - 2)
  759. {
  760. wEnumLen *= 2;
  761. mciFree (lpstrEnum);
  762. } else
  763. break;
  764. }
  765. lpstrEnumStart = lpstrEnum;
  766. if ( wcslen(lpstrDriverName) >= MCI_MAX_DEVICE_TYPE_LENGTH ) {
  767. wErr = MCIERR_DEVICE_LENGTH;
  768. goto exit_fn;
  769. }
  770. wcscpy(lpstrDevice, lpstrDriverName);
  771. //!! mciToLower (lpstrDevice);
  772. // Walk through each string
  773. while (TRUE) {
  774. wErr = GetPrivateProfileStringW( MCI_HANDLERS,
  775. lpstrEnum, wszNull, lpstrProfString,
  776. wProfLength,
  777. MCIDRIVERS_INI_FILE );
  778. if (*lpstrProfString == '\0')
  779. {
  780. dprintf1(("mciFindDriverName: cannot load valid keyname"));
  781. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  782. goto exit_fn;
  783. }
  784. // See if driver pathname matches input
  785. //!! mciToLower (lpstrProfString);
  786. lpstrDriverTemp = lpstrDevice;
  787. lpstrProfTemp = lpstrProfString;
  788. // Find end of file name
  789. while (*lpstrProfTemp != '\0' && *lpstrProfTemp != ' ')
  790. ++lpstrProfTemp;
  791. // Find begining of simple file name
  792. --lpstrProfTemp;
  793. while (*lpstrProfTemp != '\\' && *lpstrProfTemp != '/' &&
  794. *lpstrProfTemp != ':')
  795. if (--lpstrProfTemp < lpstrProfString)
  796. break;
  797. ++lpstrProfTemp;
  798. // Compare to input
  799. while (*lpstrDriverTemp != '\0')
  800. if (*lpstrDriverTemp++ != *lpstrProfTemp++ ||
  801. (UINT)(lpstrProfTemp - lpstrProfString) >= wProfLength)
  802. {
  803. --lpstrProfTemp;
  804. break;
  805. }
  806. // If the input was contained in the profile string and followed by
  807. // a space or a '.' then we've got it!
  808. if (*lpstrDriverTemp == '\0' &&
  809. (*lpstrProfTemp == ' ' || *lpstrProfTemp == '.'))
  810. {
  811. if (wcslen (lpstrEnum) >= MCI_MAX_DEVICE_TYPE_LENGTH)
  812. {
  813. dprintf1(("mciFindDriverName: device name too long"));
  814. wErr = MCIERR_DEVICE_LENGTH;
  815. goto exit_fn;
  816. }
  817. wcscpy (lpstrDevice, lpstrEnum);
  818. wErr = 0;
  819. goto exit_fn;
  820. }
  821. // Skip to next keyname
  822. while (*lpstrEnum++ != '\0') {}
  823. // Error if no more left
  824. if (*lpstrEnum == 0)
  825. {
  826. wErr = MCIERR_INVALID_DEVICE_NAME;
  827. goto exit_fn;
  828. }
  829. }
  830. exit_fn:
  831. mciFree (lpstrEnumStart);
  832. return wErr;
  833. }
  834. //
  835. // Identifies the driver name to load
  836. // Loads the driver
  837. // Reparses open command if necessary
  838. // Sets a default break key
  839. //
  840. // lpOpenInfo contains various info for reparsing
  841. //
  842. // bDefaultAlias indicates that the alias need not be verified because
  843. // it was internally assigned
  844. //
  845. STATICFN DWORD mciLoadDevice (
  846. DWORD dwFlags,
  847. LPMCI_OPEN_PARMSW lpOpen,
  848. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
  849. BOOL bDefaultAlias)
  850. {
  851. LPMCI_DEVICE_NODE nodeWorking;
  852. HANDLE hDriver;
  853. MCIDEVICEID wID;
  854. DWORD wErr;
  855. WCHAR strProfileString[MCI_PROFILE_STRING_LENGTH];
  856. WCHAR szDriverParms[128];
  857. MCI_OPEN_DRIVER_PARMS DriverOpen;
  858. HANDLE hDrvDriver;
  859. LPWSTR lpstrParams;
  860. LPCWSTR lpstrInstallName, lpstrDeviceName;
  861. LPWSTR lpstrCopy = NULL;
  862. LPMCI_OPEN_PARMSW lpOriginalOpenParms = lpOpen;
  863. /* Open a normal device */
  864. #if DBG
  865. if (lpOpen && lpOpen->lpstrDeviceType) {
  866. dprintf2(("mciLoadDevice(%ls)", lpOpen->lpstrDeviceType));
  867. } else {
  868. dprintf2(("mciLoadDevice()"));
  869. }
  870. #endif
  871. /* Check for the device name in MCIDRIVERS_INI_FILE */
  872. lpstrInstallName = lpOpen->lpstrDeviceType;
  873. wErr = GetPrivateProfileStringW( MCI_HANDLERS,
  874. lpstrInstallName,
  875. wszNull,
  876. strProfileString,
  877. MCI_PROFILE_STRING_LENGTH,
  878. MCIDRIVERS_INI_FILE );
  879. // If device name not found
  880. if (wErr == 0)
  881. {
  882. int nLen = wcslen(lpstrInstallName);
  883. int index;
  884. // Try for the device name with a '1' thru a '9' appended to it
  885. if ((lpstrCopy = (LPWSTR)mciAlloc( BYTE_GIVEN_CHAR(nLen+2)
  886. /* space for digit too */ ) ) == NULL)
  887. {
  888. dprintf1(("mciLoadDevice: cannot allocate device name copy"));
  889. return MCIERR_OUT_OF_MEMORY;
  890. }
  891. wcscpy( lpstrCopy, lpstrInstallName );
  892. lpstrCopy[nLen + 1] = '\0';
  893. for (index = 1; index <= 9; ++index)
  894. {
  895. lpstrCopy[nLen] = (WCHAR)('0' + index);
  896. wErr = GetPrivateProfileStringW(
  897. MCI_HANDLERS,
  898. lpstrCopy,
  899. wszNull,
  900. strProfileString,
  901. MCI_PROFILE_STRING_LENGTH,
  902. MCIDRIVERS_INI_FILE );
  903. if (wErr != 0) {
  904. dprintf2(("Loaded driver name %ls >> %ls", lpstrCopy, strProfileString));
  905. break;
  906. }
  907. }
  908. if (wErr == 0)
  909. {
  910. mciFree (lpstrCopy);
  911. if ((lpstrCopy = (LPWSTR)mciAlloc( BYTE_GIVEN_CHAR( MCI_MAX_DEVICE_TYPE_LENGTH )))
  912. == NULL)
  913. {
  914. dprintf1(("mciLoadDevice: cannot allocate device name copy"));
  915. return MCIERR_OUT_OF_MEMORY;
  916. }
  917. if ((wErr = mciFindDriverName(
  918. lpstrInstallName,
  919. lpstrCopy,
  920. strProfileString,
  921. MCI_PROFILE_STRING_LENGTH )) != 0)
  922. {
  923. dprintf1(("mciLoadDevice - invalid device name %ls", lpstrInstallName));
  924. goto exit_fn;
  925. }
  926. }
  927. lpstrInstallName = lpstrCopy;
  928. }
  929. // Break out the device driver pathname and the parameter list
  930. lpstrParams = strProfileString;
  931. // Eat characters until blank or null reached
  932. while (*lpstrParams != ' ' && *lpstrParams != '\0') {
  933. ++lpstrParams;
  934. }
  935. // Terminate driver file name, and separate the driver file name from its
  936. // parameters. If there are no parameters, i.e. *lpstrParams=='\0',
  937. // leave lpstrParams pointing at the null. Otherwise put a null
  938. // character to terminate the driver file name and step the pointer to
  939. // the first character in the parameter string.
  940. if (*lpstrParams == ' ') { *lpstrParams++ = '\0'; }
  941. //
  942. // We have changed from Win 3.1. Because users cannot write to
  943. // system.ini the parameters have to be read from Win.Ini
  944. // section name [dll_name]
  945. // keyword alias=parameters
  946. // If there are any parameters on the line read from [Drivers] use
  947. // them as a default. This does preserve compatibility for those
  948. // applications that write directly to system.ini (and have the
  949. // privileges to get away with it).
  950. //
  951. // LATER: This stuff will be in the registry once the drivers themselves
  952. // (or it could be the drivers applet) creates a registry mapping.
  953. GetProfileString(strProfileString, lpstrInstallName, lpstrParams,
  954. szDriverParms, sizeof(szDriverParms)/sizeof(WCHAR));
  955. lpstrParams = szDriverParms;
  956. dprintf3(("Parameters for device %ls (Driver %ls) >%ls<",
  957. lpstrInstallName, strProfileString, szDriverParms));
  958. //Now "strProfileString" is the device driver and "lpstrParams" is
  959. //the parameter string
  960. if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID)) {
  961. lpstrDeviceName = lpOpen->lpstrElementName;
  962. } else {
  963. lpstrDeviceName = lpOpen->lpstrDeviceType;
  964. }
  965. if (dwFlags & MCI_OPEN_ALIAS)
  966. {
  967. // If the alias is default then we've already checked its uniqueness
  968. if (!bDefaultAlias
  969. && mciGetDeviceIDInternal (lpOpen->lpstrAlias,
  970. lpOpenInfo->hCallingTask) != 0)
  971. {
  972. wErr = MCIERR_DUPLICATE_ALIAS;
  973. dprintf1(("mciLoadDevice - duplicate alias"));
  974. goto exit_fn;
  975. }
  976. lpstrDeviceName = lpOpen->lpstrAlias;
  977. }
  978. wID = mciAllocateNode (dwFlags, lpstrDeviceName, &nodeWorking);
  979. if (wID == 0)
  980. {
  981. dprintf1(("mciLoadDevice - cannot allocate new node, driver not loaded"));
  982. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  983. goto exit_fn;
  984. }
  985. // Identify the task which initiated the open command
  986. if (lpOpenInfo->hCallingTask != NULL) {
  987. nodeWorking->hOpeningTask = lpOpenInfo->hCallingTask;
  988. } else {
  989. nodeWorking->hOpeningTask = GetCurrentTask();
  990. }
  991. if (nodeWorking->hOpeningTask != nodeWorking->hCreatorTask)
  992. nodeWorking->dwMCIFlags |= MCINODE_ISAUTOOPENED;
  993. // Initialize the driver
  994. DriverOpen.lpstrParams = lpstrParams;
  995. DriverOpen.wCustomCommandTable = MCI_TABLE_NOT_PRESENT;
  996. DriverOpen.wType = 0;
  997. DriverOpen.wDeviceID = wID;
  998. // Load the driver
  999. hDrvDriver = DrvOpen (strProfileString, MCI_HANDLERS,
  1000. (DWORD)(LPMCI_OPEN_DRIVER_PARMS)&DriverOpen);
  1001. if (hDrvDriver == NULL)
  1002. {
  1003. dprintf1(("mciLoadDevice: DrvOpen failed"));
  1004. // Assume driver has free'd any custom command table when it failed the open
  1005. mciFreeDevice (nodeWorking);
  1006. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  1007. goto exit_fn;
  1008. }
  1009. lpOpen->wDeviceID = wID;
  1010. //lpOpen->wReserved0 = 0; Field does not exist in 32bit NT
  1011. hDriver = DrvGetModuleHandle (hDrvDriver);
  1012. nodeWorking->hDrvDriver = hDrvDriver;
  1013. nodeWorking->hDriver = hDriver;
  1014. // Driver provides custom device table and type
  1015. nodeWorking->wCustomCommandTable = DriverOpen.wCustomCommandTable;
  1016. nodeWorking->wDeviceType = DriverOpen.wType;
  1017. // Load driver's type table
  1018. if ((nodeWorking->wCommandTable = mciLoadTableType (DriverOpen.wType))
  1019. == MCI_TABLE_NOT_PRESENT) {
  1020. // Load from a file if necessary
  1021. nodeWorking->wCommandTable =
  1022. mciLoadCommandResource (ghInst, lpOpen->lpstrDeviceType,
  1023. DriverOpen.wType);
  1024. dprintf3((" Command table id: %08XH", nodeWorking->wCommandTable));
  1025. }
  1026. // Record this for 'sysinfo installname'
  1027. if ((nodeWorking->lpstrInstallName =
  1028. mciAlloc( BYTE_GIVEN_CHAR( wcslen( lpstrInstallName ) + 1 )))
  1029. == NULL)
  1030. {
  1031. mciCloseDevice (wID, 0L, NULL, FALSE);
  1032. dprintf1(("mciLoadDevice - out of memory"));
  1033. wErr = MCIERR_OUT_OF_MEMORY;
  1034. goto exit_fn;
  1035. } else
  1036. wcscpy( nodeWorking->lpstrInstallName, lpstrInstallName );
  1037. // Reparse the input command if no type was known the first time or if
  1038. // there was a custom command table
  1039. // and there were any open command parameters
  1040. if (lpOpenInfo->lpstrParams != NULL)
  1041. {
  1042. if ((wErr = mciReparseOpen (lpOpenInfo,
  1043. nodeWorking->wCustomCommandTable,
  1044. nodeWorking->wCommandTable,
  1045. &dwFlags, &lpOpen, wID)) != 0)
  1046. {
  1047. dprintf1(("mciLoadDevice - error reparsing input command"));
  1048. mciCloseDevice (wID, 0L, NULL, FALSE);
  1049. goto exit_fn;
  1050. }
  1051. // If there is no custom command table but mciSendString had a parsing
  1052. // error then close the device and report the error now
  1053. } else if (lpOpenInfo->wParsingError != 0)
  1054. {
  1055. mciCloseDevice (wID, 0L, NULL, FALSE);
  1056. wErr = lpOpenInfo->wParsingError;
  1057. goto exit_fn;
  1058. }
  1059. /* Send MCI_OPEN_DRIVER command to device */
  1060. wErr = LOWORD(mciSendCommandW(wID, MCI_OPEN_DRIVER,
  1061. dwFlags, (DWORD)lpOpen));
  1062. // If the OPEN failed then close the device (don't send a CLOSE though)
  1063. if (wErr != 0)
  1064. mciCloseDevice (wID, 0L, NULL, FALSE);
  1065. else
  1066. // Set default break key
  1067. mciSetBreakKey (wID, VK_CANCEL, NULL);
  1068. // If we replaced the open parms here then free them
  1069. if (lpOriginalOpenParms != lpOpen && lpOpen != NULL)
  1070. mciFree (lpOpen);
  1071. exit_fn:
  1072. if (lpstrCopy != NULL)
  1073. mciFree (lpstrCopy);
  1074. return wErr;
  1075. }
  1076. /*
  1077. * @doc INTERNAL MCI
  1078. * @api BOOL | mciExtractDeviceType | If the given device name ends with
  1079. * a file extension (.???) then try to get a typename from the
  1080. * [mci extensions] section of WIN.INI
  1081. *
  1082. * @parm LPCWSTR | lpstrDeviceName | The name to get the type from
  1083. *
  1084. * @parm LPWSTR | lpstrDeviceType | The device type, returned to caller.
  1085. *
  1086. * @parm UINT | wBufLen | The length of the output buffer
  1087. *
  1088. * @rdesc TRUE if the type was found, FALSE otherwise
  1089. *
  1090. */
  1091. BOOL mciExtractDeviceType (
  1092. LPCWSTR lpstrDeviceName,
  1093. LPWSTR lpstrDeviceType,
  1094. UINT wBufLen)
  1095. {
  1096. LPCWSTR lpstrExt = lpstrDeviceName;
  1097. int i;
  1098. dprintf2(("mciExtractDeviceType(%ls)", lpstrDeviceName));
  1099. #if 0
  1100. #ifdef BAD_CODE
  1101. //This block cannot be used because it returns FALSE whenever a ! is found.
  1102. //Hence if the directory name has a ! ...
  1103. //N.B. The ! is used by MCI as a compound device name separator, but that is
  1104. //not applicable when going through this routine.
  1105. // Goto end of string
  1106. while (*lpstrExt != '\0')
  1107. {
  1108. // WARNING: This causes problems when the directory name has a !
  1109. // '!' case is handled elsewhere
  1110. if (*lpstrExt++ == '!')
  1111. return FALSE;
  1112. // Pointer has been incremented in the test
  1113. }
  1114. #else
  1115. // Goto end of string
  1116. lpstrExt += wcslen(lpstrExt);
  1117. #endif
  1118. #else
  1119. /*
  1120. ** scan the string looking for a '!' character. If we find one
  1121. ** replace it with a NULL and see if the string to its left is a
  1122. ** supported device type. If it is return FALSE, either way replace the
  1123. ** '\0' character with a '!'.
  1124. */
  1125. {
  1126. LPWSTR lpwstr = wcschr(lpstrExt, '!' );
  1127. /*
  1128. ** If we found a '!' and it wasn't the first character in the
  1129. ** the string we might have a compound device name.
  1130. */
  1131. if ( (lpwstr != NULL) && (lpwstr != lpstrExt) ) {
  1132. int i;
  1133. WCHAR wTmp[33];
  1134. /*
  1135. ** We're not interested in the actual string returned only if
  1136. ** it is present in the list of mci devices. A return code
  1137. ** of 0 from GetPrivateProfileStringW means we don't have a
  1138. ** compound name.
  1139. */
  1140. *lpwstr = '\0';
  1141. i = GetPrivateProfileStringW( MCI_HANDLERS, lpstrExt, wszNull,
  1142. wTmp, sizeof(wTmp) / sizeof(WCHAR),
  1143. MCIDRIVERS_INI_FILE);
  1144. /*
  1145. ** Restore the original string
  1146. */
  1147. *lpwstr = '!';
  1148. if ( i != 0 ) {
  1149. return FALSE;
  1150. }
  1151. }
  1152. }
  1153. // Goto end of string
  1154. lpstrExt += wcslen(lpstrExt);
  1155. #endif
  1156. // Must be at least 2 characters in string
  1157. if (lpstrExt - lpstrDeviceName < 2) {
  1158. return FALSE;
  1159. }
  1160. // Now looking at the NULL terminator. Check the
  1161. // previous characters for a '.'
  1162. for (i=1; i<=32; ++i)
  1163. {
  1164. --lpstrExt;
  1165. // Cannot have path separator here
  1166. if (*lpstrExt == '/' || *lpstrExt == '\\') {
  1167. return FALSE;
  1168. }
  1169. if (*lpstrExt == '.')
  1170. {
  1171. if (1==i) {
  1172. return(FALSE);
  1173. // Would mean that extension is a null string
  1174. }
  1175. #if DBG
  1176. if (0 != (GetProfileStringW(MCI_EXTENSIONS, ++lpstrExt,
  1177. wszNull, lpstrDeviceType, wBufLen))) {
  1178. dprintf2(("Read extension %ls from section %ls. Driver=%ls", lpstrExt, MCI_EXTENSIONS, lpstrDeviceType));
  1179. return(TRUE);
  1180. } else {
  1181. dprintf2(("Failed to read extension %s from section %s.", lpstrExt, MCI_EXTENSIONS));
  1182. return(FALSE);
  1183. }
  1184. #else
  1185. return(0 != (GetProfileStringW(MCI_EXTENSIONS, ++lpstrExt,
  1186. wszNull, lpstrDeviceType, wBufLen)));
  1187. #endif
  1188. }
  1189. if (lpstrExt == lpstrDeviceName) {
  1190. return FALSE;
  1191. // We have run out of string
  1192. }
  1193. }
  1194. return FALSE;
  1195. }
  1196. // Copy characters up to cSeparater into output which is allocated
  1197. // by this function using mciAlloc. Return the input pointer pointing
  1198. // to the character after cSeparator
  1199. // unless the separator is '\0' in which case it points to the end.
  1200. //
  1201. // Return the allocated pointer
  1202. //
  1203. // If bMustFind then the output string is created only if the token
  1204. // is found and is otherwise NULL. Else the output string is always created.
  1205. //
  1206. // cSeparator is ignored inside matching quotes ("abd"), the quotes
  1207. // are not coppied and doubled
  1208. // quotes inside are compressed to one. There must be a terminating quote.
  1209. // Quotes are treated normally unless the first character is a quote
  1210. //
  1211. // Function return value is 0 or an MCIERR code. A missing separator does
  1212. // not cause an error return.
  1213. UINT mciEatToken (
  1214. LPCWSTR *lplpstrInput,
  1215. WCHAR cSeparater,
  1216. LPWSTR *lplpstrOutput,
  1217. BOOL bMustFind)
  1218. {
  1219. LPCWSTR lpstrEnd = *lplpstrInput, lpstrCounter;
  1220. LPWSTR lpstrOutput;
  1221. UINT wLen;
  1222. BOOL bInQuotes = FALSE, bParseQuotes = TRUE, bQuoted = FALSE;
  1223. // Clear output
  1224. *lplpstrOutput = NULL;
  1225. // Scan for token or end of string
  1226. while ((*lpstrEnd != cSeparater || bInQuotes) && *lpstrEnd != '\0')
  1227. {
  1228. // If quote
  1229. if (*lpstrEnd == '"' && bParseQuotes)
  1230. {
  1231. // If inside quotes
  1232. if (bInQuotes)
  1233. {
  1234. // If next character is a quote also
  1235. if (*(lpstrEnd + 1) == '"')
  1236. // Skip it
  1237. ++lpstrEnd;
  1238. else
  1239. bInQuotes = FALSE;
  1240. } else {
  1241. bInQuotes = TRUE;
  1242. bQuoted = TRUE;
  1243. }
  1244. } else if (!bInQuotes)
  1245. {
  1246. if (bQuoted)
  1247. return MCIERR_EXTRA_CHARACTERS;
  1248. // A non-quote was read first so treat any quotes as normal characters
  1249. bParseQuotes = FALSE;
  1250. }
  1251. ++lpstrEnd;
  1252. }
  1253. if (bInQuotes)
  1254. return MCIERR_NO_CLOSING_QUOTE;
  1255. // Fail if the token was not found and bMustFind is TRUE
  1256. if (*lpstrEnd != cSeparater && bMustFind)
  1257. return 0;
  1258. // Length of new string (INCLUDES QUOTES NOT COPIED)
  1259. wLen = lpstrEnd - *lplpstrInput + 1;
  1260. if ((*lplpstrOutput = mciAlloc( BYTE_GIVEN_CHAR( wLen ) )) == NULL)
  1261. return MCIERR_OUT_OF_MEMORY;
  1262. // Copy into allocated space
  1263. lpstrCounter = *lplpstrInput;
  1264. lpstrOutput = *lplpstrOutput;
  1265. bInQuotes = FALSE;
  1266. while (lpstrCounter != lpstrEnd)
  1267. {
  1268. if (*lpstrCounter == '"' && bParseQuotes)
  1269. {
  1270. if (bInQuotes)
  1271. {
  1272. // If this is a doubled quote
  1273. if (*(lpstrCounter + 1) == '"')
  1274. // Copy it
  1275. *lpstrOutput++ = *lpstrCounter++;
  1276. else
  1277. bInQuotes = FALSE;
  1278. } else
  1279. bInQuotes = TRUE;
  1280. // Skip the quote
  1281. ++lpstrCounter;
  1282. } else
  1283. *lpstrOutput++ = *lpstrCounter++;
  1284. }
  1285. *lpstrOutput = '\0';
  1286. if (*lpstrEnd == '\0')
  1287. *lplpstrInput = lpstrEnd;
  1288. else
  1289. *lplpstrInput = lpstrEnd + 1;
  1290. return 0;
  1291. }
  1292. // Take the type number from the open parameters and return
  1293. // it as a string in lplpstrType which must be free'd with mciFree
  1294. // Returns 0 or an MCI error code
  1295. UINT mciExtractTypeFromID (
  1296. LPMCI_OPEN_PARMSW lpOpen)
  1297. {
  1298. int nSize;
  1299. LPWSTR lpstrType;
  1300. if ((lpstrType = mciAlloc( BYTE_GIVEN_CHAR( MCI_MAX_DEVICE_TYPE_LENGTH ))) == NULL)
  1301. return MCIERR_OUT_OF_MEMORY;
  1302. // Load the type string corresponding to the ID
  1303. if ((nSize = LoadStringW( ghInst,
  1304. LOWORD ((DWORD)lpOpen->lpstrDeviceType),
  1305. lpstrType, MCI_MAX_DEVICE_TYPE_LENGTH ) ) == 0) {
  1306. mciFree(lpstrType);
  1307. return MCIERR_EXTENSION_NOT_FOUND;
  1308. }
  1309. // Add ordinal (if any) onto the end of the device type name
  1310. if (HIWORD (lpOpen->lpstrDeviceType) != 0)
  1311. {
  1312. if (nSize > MCI_MAX_DEVICE_TYPE_LENGTH - 11)
  1313. {
  1314. dprintf1(("mciExtractTypeFromID: type + ordinal too long"));
  1315. mciFree(lpstrType);
  1316. return MCIERR_DEVICE_ORD_LENGTH;
  1317. }
  1318. wsprintfW (lpstrType + nSize, szUnsignedFormat,
  1319. HIWORD ((DWORD)lpOpen->lpstrDeviceType));
  1320. }
  1321. lpOpen->lpstrDeviceType = lpstrType;
  1322. return 0;
  1323. }
  1324. /*
  1325. * @doc INTERNAL MCI
  1326. * @func UINT | mciOpenDevice | Open an MCI device for access.
  1327. * Used in processing the MCI_OPEN message.
  1328. *
  1329. * @parm DWORD | dwFlags | Open Flags
  1330. * @parm LPMCI_OPEN_PARMS | lpOpen | Description of device
  1331. * @parm LPMCI_INTERNAL_OPEN_PARMS | lpOpenInfo | Internal device description
  1332. *
  1333. * @rdesc 0 if successful or an error code
  1334. * @flag MCIERR_INVALID_DEVICE_NAME | Name not known
  1335. * @flag MCIERR_DEVICE_OPEN | Device is already open and is not sharable
  1336. *
  1337. * @comm This function does the following:
  1338. * 1) Check to see if device is already open. If so, increase the use count
  1339. * and return the device ID
  1340. *
  1341. * Otherwise:
  1342. *
  1343. * 2) Locate the device name in the SYSTEM.INI file and load
  1344. * the corresponding device driver DLL
  1345. *
  1346. * 3) Allocate and initialize a new device description block
  1347. *
  1348. */
  1349. UINT mciOpenDevice (
  1350. DWORD dwStartingFlags,
  1351. LPMCI_OPEN_PARMSW lpOpen,
  1352. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
  1353. {
  1354. LPWSTR lpstrNewType = NULL;
  1355. UINT wID;
  1356. DWORD wReturn;
  1357. LPCWSTR lpstrDeviceName;
  1358. LPWSTR lpstrNewElement = NULL;
  1359. BOOL bFromTypeID = FALSE;
  1360. LPCWSTR lpstrOriginalType;
  1361. LPCWSTR lpstrOriginalElement;
  1362. LPCWSTR lpstrOriginalAlias;
  1363. DWORD dwFlags = dwStartingFlags;
  1364. BOOL bDefaultAlias = FALSE;
  1365. // Initialize
  1366. if (lpOpen == NULL) {
  1367. dprintf2(("mciOpenDevice() NULL parameter block"));
  1368. return MCIERR_NULL_PARAMETER_BLOCK;
  1369. }
  1370. lpstrOriginalType = lpOpen->lpstrDeviceType;
  1371. lpstrOriginalElement = lpOpen->lpstrElementName;
  1372. lpstrOriginalAlias = lpOpen->lpstrAlias;
  1373. // The type number is given explicitly, convert it to a type name
  1374. if (dwFlags & MCI_OPEN_TYPE_ID) {
  1375. if ((wReturn = mciExtractTypeFromID (lpOpen)) != 0)
  1376. return (UINT)wReturn;
  1377. else
  1378. bFromTypeID = TRUE;
  1379. }
  1380. // The device name is the device type of a simple device or the device
  1381. // element of a compound device
  1382. if (dwFlags & MCI_OPEN_ELEMENT)
  1383. lpstrDeviceName = lpstrOriginalElement;
  1384. else if (dwFlags & MCI_OPEN_TYPE)
  1385. lpstrDeviceName = lpOpen->lpstrDeviceType;
  1386. else
  1387. return MCIERR_MISSING_PARAMETER;
  1388. if (lpstrDeviceName == NULL)
  1389. {
  1390. dprintf1(("mciOpenDevice: Device name is NULL"));
  1391. return MCIERR_INVALID_DEVICE_NAME;
  1392. }
  1393. // Is the device already open?
  1394. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  1395. wID = mciGetDeviceIDFromElementIDW( (DWORD)lpstrDeviceName,
  1396. lpOpen->lpstrDeviceType);
  1397. else
  1398. wID = mciGetDeviceIDInternal ((dwFlags & MCI_OPEN_ALIAS ?
  1399. lpOpen->lpstrAlias : lpstrDeviceName),
  1400. lpOpenInfo->hCallingTask);
  1401. // If the device is open already then return an error
  1402. if (wID != 0)
  1403. return dwFlags & MCI_OPEN_ALIAS ? MCIERR_DUPLICATE_ALIAS :
  1404. MCIERR_DEVICE_OPEN;
  1405. // The device is not already open in that task by the name
  1406. // If the type was derived then skip all this crap
  1407. if (bFromTypeID)
  1408. goto load_device;
  1409. // If an element name is given but no type name (only via mciSendCommand)
  1410. if (dwFlags & MCI_OPEN_ELEMENT && !(dwFlags & MCI_OPEN_TYPE))
  1411. {
  1412. // Allocate a piece of memory for resolving the device type
  1413. lpstrNewType = mciAlloc( BYTE_GIVEN_CHAR(MCI_MAX_DEVICE_TYPE_LENGTH) );
  1414. if (lpstrNewType == NULL) {
  1415. return MCIERR_OUT_OF_MEMORY;
  1416. }
  1417. // Try to get the device type from the element name via a file extension
  1418. if (mciExtractDeviceType( lpstrOriginalElement, lpstrNewType,
  1419. MCI_MAX_DEVICE_TYPE_LENGTH))
  1420. {
  1421. lpOpen->lpstrDeviceType = lpstrNewType;
  1422. dwFlags |= MCI_OPEN_TYPE;
  1423. } else
  1424. {
  1425. mciFree (lpstrNewType);
  1426. return MCIERR_EXTENSION_NOT_FOUND;
  1427. }
  1428. } else if (dwFlags & MCI_OPEN_TYPE && !(dwFlags & MCI_OPEN_ELEMENT))
  1429. // A type name is given but no element
  1430. {
  1431. // Allocate a piece of memory for resolving the device type
  1432. lpstrNewType = mciAlloc( BYTE_GIVEN_CHAR(MCI_MAX_DEVICE_TYPE_LENGTH) );
  1433. if (lpstrNewType == NULL) {
  1434. return MCIERR_OUT_OF_MEMORY;
  1435. }
  1436. // Try to extract a device type from the given device name via a file extension
  1437. if (mciExtractDeviceType (lpOpen->lpstrDeviceType, lpstrNewType,
  1438. MCI_MAX_DEVICE_TYPE_LENGTH))
  1439. {
  1440. // Fix up the type and element names
  1441. dwFlags |= MCI_OPEN_ELEMENT;
  1442. lpOpen->lpstrElementName = lpOpen->lpstrDeviceType;
  1443. lpOpen->lpstrDeviceType = lpstrNewType;
  1444. } else
  1445. // Failed to extract type so...
  1446. // Try to get a compound element name ('!' separator)
  1447. {
  1448. LPCWSTR lpstrTemp = lpOpen->lpstrDeviceType;
  1449. mciFree (lpstrNewType);
  1450. lpstrNewType = NULL;
  1451. if ((wReturn = mciEatToken (&lpstrTemp, '!', &lpstrNewType, TRUE))
  1452. != 0)
  1453. goto cleanup;
  1454. else if (lpstrNewType != NULL)
  1455. {
  1456. if ((wReturn = mciEatToken (&lpstrTemp, '\0',
  1457. &lpstrNewElement, TRUE))
  1458. != 0)
  1459. goto cleanup;
  1460. else if (lpstrNewElement != NULL &&
  1461. *lpstrNewElement != '\0')
  1462. {
  1463. // See if this element name is in use
  1464. if (!(dwFlags & MCI_OPEN_ALIAS))
  1465. if (mciGetDeviceIDInternal (lpstrNewElement,
  1466. lpOpenInfo->hCallingTask))
  1467. {
  1468. wReturn = MCIERR_DEVICE_OPEN;
  1469. goto cleanup;
  1470. }
  1471. // Swap type and element for new ones
  1472. lpOpen->lpstrElementName = lpstrNewElement;
  1473. lpOpen->lpstrDeviceType = lpstrNewType;
  1474. dwFlags |= MCI_OPEN_ELEMENT;
  1475. }
  1476. }
  1477. }
  1478. } else
  1479. lpstrNewType = NULL;
  1480. // Tack on a default alias if none is given
  1481. if (! (dwFlags & MCI_OPEN_ALIAS))
  1482. {
  1483. LPCWSTR lpstrAlias;
  1484. // If an element name exists then the alias is the element name
  1485. if (dwFlags & MCI_OPEN_ELEMENT)
  1486. {
  1487. // If a device ID was specified then there is no alias
  1488. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  1489. lpstrAlias = NULL;
  1490. else
  1491. lpstrAlias = lpOpen->lpstrElementName;
  1492. // Otherwise the alias is the device type
  1493. } else
  1494. lpstrAlias = lpOpen->lpstrDeviceType;
  1495. if (lpstrAlias != NULL)
  1496. {
  1497. lpOpen->lpstrAlias = lpstrAlias;
  1498. dwFlags |= MCI_OPEN_ALIAS;
  1499. bDefaultAlias = TRUE;
  1500. }
  1501. }
  1502. load_device:;
  1503. wReturn = mciLoadDevice (dwFlags, lpOpen, lpOpenInfo, bDefaultAlias);
  1504. cleanup:
  1505. if (lpstrNewElement != NULL)
  1506. mciFree (lpstrNewElement);
  1507. if (lpstrNewType != NULL)
  1508. mciFree (lpstrNewType);
  1509. if (bFromTypeID)
  1510. mciFree (lpOpen->lpstrDeviceType);
  1511. // Replace original items
  1512. lpOpen->lpstrDeviceType = lpstrOriginalType;
  1513. lpOpen->lpstrElementName = lpstrOriginalElement;
  1514. lpOpen->lpstrAlias = lpstrOriginalAlias;
  1515. return (UINT)wReturn;
  1516. }
  1517. STATICFN void mciFreeDevice (LPMCI_DEVICE_NODE nodeWorking)
  1518. {
  1519. LPMCI_DEVICE_NODE FAR *lpTempList;
  1520. MCIDEVICEID uID = nodeWorking->wDeviceID;
  1521. mciEnter("mciFreeDevice");
  1522. if (nodeWorking->lpstrName != NULL)
  1523. mciFree (nodeWorking->lpstrName);
  1524. if (nodeWorking->lpstrInstallName != NULL)
  1525. mciFree (nodeWorking->lpstrInstallName);
  1526. mciFree(MCI_lpDeviceList[uID]);
  1527. MCI_lpDeviceList[uID] = NULL;
  1528. /* If this was the last device in the list, decrement next ID value */
  1529. if (uID + (MCIDEVICEID)1 == MCI_wNextDeviceID)
  1530. {
  1531. --MCI_wNextDeviceID;
  1532. // Try to reclaim any excess free space
  1533. if (MCI_wDeviceListSize - MCI_wNextDeviceID + 1
  1534. > MCI_DEVICE_LIST_GROW_SIZE)
  1535. {
  1536. MCI_wDeviceListSize -= MCI_DEVICE_LIST_GROW_SIZE;
  1537. if ((lpTempList =
  1538. mciReAlloc (MCI_lpDeviceList, sizeof (LPMCI_DEVICE_NODE) *
  1539. MCI_wDeviceListSize)) == NULL)
  1540. MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
  1541. else
  1542. MCI_lpDeviceList = lpTempList;
  1543. }
  1544. }
  1545. mciLeave("mciFreeDevice");
  1546. }
  1547. typedef struct tagNotificationMsg {
  1548. WPARAM wParam;
  1549. LPARAM lParam;
  1550. } NOTIFICATIONMSG;
  1551. /*
  1552. * @doc INTERNAL MCI
  1553. * @api void | FilterNotification | Removes notifications for a given node
  1554. * from our notification window's message queue
  1555. *
  1556. * @parm LPMCI_DEVICE_NODE | nodeWorking | The internal device node
  1557. *
  1558. * @comm This function removes all MM_MCINOTIFY messages from hwndNotify's
  1559. * message queue by removing all notifications for devices that have been
  1560. * closed (i.e. do not belong to us), then putting the others back
  1561. */
  1562. void FilterNotification(
  1563. LPMCI_DEVICE_NODE nodeWorking)
  1564. {
  1565. NOTIFICATIONMSG anotmsg[256];
  1566. UINT uCurrentMsg;
  1567. MSG msg;
  1568. /* We can't have the mci critical section on here because this PeekMessage
  1569. will dispatch other messages in the queue */
  1570. uCurrentMsg = 0;
  1571. while (PeekMessage(&msg, hwndNotify, MM_MCINOTIFY, MM_MCINOTIFY, PM_NOYIELD | PM_REMOVE)) {
  1572. if (LOWORD(msg.lParam) != nodeWorking->wDeviceID) {
  1573. anotmsg[uCurrentMsg].wParam = msg.wParam;
  1574. anotmsg[uCurrentMsg].lParam = msg.lParam;
  1575. uCurrentMsg++;
  1576. }
  1577. }
  1578. for (; uCurrentMsg;) {
  1579. uCurrentMsg--;
  1580. PostMessage(hwndNotify, MM_MCINOTIFY, anotmsg[uCurrentMsg].wParam, anotmsg[uCurrentMsg].lParam);
  1581. }
  1582. }
  1583. /*
  1584. * @doc INTERNAL MCI
  1585. * @api UINT | mciCloseDevice | Close an MCI device. Used in
  1586. * processing the MCI_CLOSE message.
  1587. *
  1588. * @parm MCIDEVICEID | uID | The ID of the device to close
  1589. * @parm DWORD | dwFlags | Close Flags
  1590. * @parm LPMCI_GENERIC_PARMS | lpClose | Generic parameters
  1591. * @parm BOOL | bCloseDriver | TRUE if the CLOSE command should be sent
  1592. * on to the driver.
  1593. *
  1594. * @rdesc 0 if successful or an error code
  1595. *
  1596. * @comm This function sends an MCI_CLOSE_DEVICE message to the corresponding
  1597. * driver if the use count is zero and then unloads the driver DLL
  1598. *
  1599. */
  1600. UINT mciCloseDevice (
  1601. MCIDEVICEID uID,
  1602. DWORD dwFlags,
  1603. LPMCI_GENERIC_PARMS lpGeneric,
  1604. BOOL bCloseDriver)
  1605. {
  1606. LPMCI_DEVICE_NODE nodeWorking;
  1607. UINT wErr;
  1608. UINT wTable;
  1609. mciEnter("mciCloseDevice");
  1610. nodeWorking = MCI_lpDeviceList[uID];
  1611. if (nodeWorking == NULL)
  1612. {
  1613. mciLeave("mciCloseDevice");
  1614. dprintf1(("mciCloseDevice: NULL node from device ID--error if not auto-close"));
  1615. return 0;
  1616. }
  1617. // We should never be closed from the wrong task
  1618. #if 0
  1619. WinAssert(nodeWorking->hCreatorTask == GetCurrentTask());
  1620. #endif
  1621. // If a close is in progress (usually this message comes from a Yield
  1622. // after a mciDriverNotify actuated by the active close) then exit
  1623. if (ISCLOSING(nodeWorking)) {
  1624. mciLeave("mciCloseDevice");
  1625. return 0;
  1626. }
  1627. SETISCLOSING(nodeWorking);
  1628. if (bCloseDriver)
  1629. {
  1630. MCI_GENERIC_PARMS GenericParms;
  1631. mciLeave("mciCloseDevice");
  1632. // Make fake generic params if close came internally
  1633. if (lpGeneric == NULL) {
  1634. lpGeneric = &GenericParms;
  1635. }
  1636. wErr = LOWORD(mciSendCommandW(uID, MCI_CLOSE_DRIVER, dwFlags,
  1637. (DWORD)lpGeneric));
  1638. mciEnter("mciCloseDevice");
  1639. }
  1640. else
  1641. wErr = 0;
  1642. wTable = nodeWorking->wCustomCommandTable;
  1643. //
  1644. // Must zero this to allow the table to be freed later by driver
  1645. //
  1646. // We mustn't call mciFreeCommandResource for the custom table
  1647. // because the driver is going to do that when it gets DRV_FREE
  1648. //
  1649. nodeWorking->wCustomCommandTable = 0;
  1650. wTable = nodeWorking->wCommandTable;
  1651. nodeWorking->wCommandTable = 0;
  1652. mciLeave("mciCloseDevice");
  1653. mciFreeCommandResource (wTable);
  1654. //
  1655. // We're closing this node so remove any notifications queued to
  1656. // hwndNotify because these would cause this node to be erroneously
  1657. // closed again
  1658. //
  1659. if (ISAUTOOPENED(nodeWorking)) {
  1660. FilterNotification(nodeWorking);
  1661. }
  1662. DrvClose (nodeWorking->hDrvDriver, 0L, 0L); // ala CloseDriver
  1663. mciFreeDevice (nodeWorking);
  1664. return wErr;
  1665. }
  1666. /*
  1667. * @doc INTERNAL MCI DDK
  1668. * @api DWORD | mciGetDriverData | Returns a pointer to the instance
  1669. * data associated with an MCI device
  1670. *
  1671. * @parm MCIDEVICEID | wDeviceID | The MCI device ID
  1672. *
  1673. * @rdesc The driver instance data. On error, returns 0 but since
  1674. * the driver data might be zero, this cannot be verified by the caller
  1675. * unless the instance data is known to be non-zero (e.g. a pointer)
  1676. *
  1677. * @xref mciSetDriverData
  1678. */
  1679. DWORD mciGetDriverData (
  1680. MCIDEVICEID wDeviceID)
  1681. {
  1682. DWORD lpDriverData;
  1683. mciEnter("mciGetDriverData");
  1684. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  1685. {
  1686. dprintf1(("mciGetDriverData: invalid device ID"));
  1687. lpDriverData = 0;
  1688. } else {
  1689. if (NULL == MCI_lpDeviceList[wDeviceID])
  1690. {
  1691. dprintf1(("mciGetDriverData: NULL node from device ID"));
  1692. lpDriverData = 0;
  1693. } else {
  1694. lpDriverData = MCI_lpDeviceList[wDeviceID]->lpDriverData;
  1695. }
  1696. }
  1697. mciLeave("mciGetDriverData");
  1698. return lpDriverData;
  1699. }
  1700. /*
  1701. * @doc INTERNAL MCI DDK
  1702. * @api BOOL | mciSetDriverData | Sets the instance
  1703. * data associated with an MCI device
  1704. *
  1705. * @parm MCIDEVICEID | uDeviceID | The MCI device ID
  1706. *
  1707. * @parm DWORD | dwData | Driver data to set
  1708. *
  1709. * @rdesc 0 if the device ID is not known or there is insufficient
  1710. * memory to load the device description.
  1711. *
  1712. */
  1713. BOOL mciSetDriverData (
  1714. MCIDEVICEID wDeviceID,
  1715. DWORD dwData)
  1716. {
  1717. BOOL fReturn = TRUE;
  1718. mciEnter("mciSetDriverData");
  1719. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  1720. {
  1721. dprintf1(("mciSetDriverData: NULL node from device ID"));
  1722. fReturn = FALSE;
  1723. } else {
  1724. MCI_lpDeviceList[wDeviceID]->lpDriverData = dwData;
  1725. }
  1726. mciLeave("mciSetDriverData");
  1727. return fReturn;
  1728. }
  1729. /*
  1730. * @doc INTERNAL MCI DDK
  1731. * @api UINT | mciDriverYield | Used in a driver's idle loop
  1732. * to yield to Windows
  1733. *
  1734. * @parm MCIDEVICEID | wDeviceID | Device ID that is yielding.
  1735. *
  1736. * @rdesc Non-zero if the driver should abort the operation.
  1737. *
  1738. */
  1739. UINT mciDriverYield (
  1740. MCIDEVICEID wDeviceID)
  1741. {
  1742. mciEnter("mciDriverYield");
  1743. if (MCI_VALID_DEVICE_ID(wDeviceID))
  1744. {
  1745. YIELDPROC YieldProc = (MCI_lpDeviceList[wDeviceID])->fpYieldProc;
  1746. if (YieldProc != NULL) {
  1747. DWORD YieldData = (MCI_lpDeviceList[wDeviceID])->dwYieldData;
  1748. mciLeave("mciDriverYield");
  1749. mciCheckOut();
  1750. return (YieldProc)(wDeviceID, YieldData);
  1751. }
  1752. }
  1753. mciLeave("mciDriverYield");
  1754. Yield();
  1755. return 0;
  1756. }
  1757. /*
  1758. * @doc EXTERNAL MCI
  1759. * @api BOOL | mciSetYieldProc | This function sets the address
  1760. * of a procedure to be called periodically
  1761. * when an MCI device is waiting for a command to complete because the WAIT
  1762. * parameter was specified.
  1763. *
  1764. * @parm MCIDEVICEID | wDeviceID | Specifies the device ID to assign a procedure to.
  1765. *
  1766. * @parm YIELDPROC | fpYieldProc | Specifies the procedure to call
  1767. * when yielding for the given device. Set to NULL to disable
  1768. * any existing yield proc.
  1769. *
  1770. * @parm DWORD | dwYieldData | Specifies the data sent to the yield procedure
  1771. * when it is called for the given device.
  1772. *
  1773. * @rdesc Returns TRUE if successful. Returns FALSE for an invalid device ID.
  1774. *
  1775. * @comm This call overides any previous yield procedure for this device.
  1776. *
  1777. */
  1778. BOOL APIENTRY mciSetYieldProc (
  1779. MCIDEVICEID wDeviceID,
  1780. YIELDPROC fpYieldProc,
  1781. DWORD dwYieldData)
  1782. {
  1783. BOOL fReturn = FALSE;
  1784. mciEnter("mciSetYieldProc");
  1785. if (MCI_VALID_DEVICE_ID(wDeviceID))
  1786. {
  1787. LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
  1788. node->fpYieldProc = fpYieldProc;
  1789. node->dwYieldData = dwYieldData;
  1790. fReturn = TRUE;
  1791. } else
  1792. fReturn = FALSE;
  1793. mciLeave("mciSetYieldProc");
  1794. return fReturn;
  1795. }
  1796. /*
  1797. * @doc EXTERNAL MCI
  1798. * @api YIELDPROC | mciGetYieldProc | This function gets the address
  1799. * of the callback procedure to be called periodically when an MCI device
  1800. * is completing a command specified with the WAIT flag.
  1801. *
  1802. * @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
  1803. * which the yield procedure is to be retrieved from.
  1804. *
  1805. * @parm LPDWORD | lpdwYieldData | Optionally specifies a buffer to place
  1806. * the yield data passed to the function in. If the parameter is NULL, it
  1807. * is ignored.
  1808. *
  1809. * @rdesc Returns the current yield proc if any, else returns NULL for an
  1810. * invalid device ID.
  1811. *
  1812. */
  1813. YIELDPROC WINAPI mciGetYieldProc (
  1814. UINT wDeviceID,
  1815. LPDWORD lpdwYieldData)
  1816. {
  1817. YIELDPROC fpYieldProc;
  1818. mciEnter("mciGetYieldProc");
  1819. if (MCI_VALID_DEVICE_ID(wDeviceID))
  1820. {
  1821. if (lpdwYieldData != NULL) {
  1822. V_WPOINTER(lpdwYieldData, sizeof(DWORD), NULL);
  1823. *lpdwYieldData = MCI_lpDeviceList[wDeviceID]->dwYieldData;
  1824. }
  1825. fpYieldProc = MCI_lpDeviceList[wDeviceID]->fpYieldProc;
  1826. } else {
  1827. fpYieldProc = NULL;
  1828. }
  1829. mciLeave("mciGetYieldProc");
  1830. return fpYieldProc;
  1831. }
  1832. /*
  1833. * @doc INTERNAL MCI
  1834. * @api int | mciBreakKeyYieldProc | Procedure called to check a
  1835. * key state for the given device
  1836. *
  1837. * @parm MCIDEVICEID | wDeviceID | Device ID which is yielding
  1838. *
  1839. * @parm DWORD | dwYieldData | Data for this device's yield proc
  1840. *
  1841. * @rdesc Non-zero if the driver should abort the operation. Currently
  1842. * always returns 0.
  1843. *
  1844. */
  1845. UINT mciBreakKeyYieldProc (
  1846. MCIDEVICEID wDeviceID,
  1847. DWORD dwYieldData)
  1848. {
  1849. HWND hwndCheck = NULL;
  1850. int nVirtKey, nState;
  1851. nVirtKey = dwYieldData;
  1852. UNREFERENCED_PARAMETER(wDeviceID);
  1853. nState = GetAsyncKeyState (nVirtKey);
  1854. // Break if key is down or has been down
  1855. if (nState & 1 /* used to be 0x8000*/ )
  1856. {
  1857. MSG msg;
  1858. while (PeekMessage (&msg, hwndCheck, WM_KEYFIRST, WM_KEYLAST,
  1859. PM_REMOVE));
  1860. return MCI_ERROR_VALUE;
  1861. }
  1862. Yield();
  1863. return 0;
  1864. }
  1865. /*
  1866. * @doc INTERNAL MCI
  1867. * @api UINT FAR | mciSetBreakKey | Set a key which will break a wait loop
  1868. * for a given driver
  1869. *
  1870. * @parm UINT | uDeviceID | The device ID to assign a break key to
  1871. *
  1872. * @parm int | nVirtKey | Virtual key code to trap
  1873. *
  1874. * @parm HWND | hwndTrap | The handle to a window that must be active
  1875. * for the key to be trapped. If NULL then all windows will be checked
  1876. *
  1877. * @rdesc TRUE if successful, FALSE if invalid device ID
  1878. *
  1879. */
  1880. UINT FAR mciSetBreakKey (
  1881. MCIDEVICEID wDeviceID,
  1882. int nVirtKey,
  1883. HWND hwndTrap)
  1884. {
  1885. dprintf2(("Setting break key for device %d to %x", wDeviceID, nVirtKey));
  1886. return mciSetYieldProc (wDeviceID, mciBreakKeyYieldProc, nVirtKey);
  1887. // Note: we have no way of passing hwndTrap... will check all windows
  1888. // on this thread of the application
  1889. }
  1890. /*
  1891. * @doc INTERNAL MCI
  1892. * @api BOOL | mciDriverNotify | Used by a driver to send
  1893. * a notification message
  1894. *
  1895. * @parm HANDLE | hCallback | The window to notify
  1896. *
  1897. * @parm UINT | wDeviceID | The device ID which triggered the callback
  1898. *
  1899. * @parm UINT | wStatus | The status of the callback. May be one of
  1900. * MCI_NOTIFY_SUCCESSFUL or MCI_NOTIFY_SUPERSEDED or MCI_NOTIFY_ABORTED or
  1901. * MCI_NOTIFY_FAILURE
  1902. *
  1903. * @rdesc returns TRUE if notify was successfully sent, FALSE otherwise.
  1904. *
  1905. * @comm This function is callable at interrupt time
  1906. *
  1907. */
  1908. BOOL mciDriverNotify (
  1909. HANDLE hCallback,
  1910. MCIDEVICEID wDeviceID,
  1911. UINT uStatus)
  1912. {
  1913. BOOL f;
  1914. #if DBG
  1915. // IsWindow() is in segment marked PRELOAD for WIN3.0 so OK at interrupt time
  1916. if (hCallback != NULL && !IsWindow(hCallback))
  1917. {
  1918. dprintf1(("mciDriverNotify: invalid window!"));
  1919. return FALSE;
  1920. }
  1921. #endif
  1922. f = PostMessage(hCallback, MM_MCINOTIFY, uStatus, wDeviceID);
  1923. #if DBG
  1924. if (!f)
  1925. dprintf1(("mciDriverNotify: PostMessage failed!"));
  1926. #endif
  1927. return f;
  1928. }