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.

2249 lines
66 KiB

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