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.

1817 lines
53 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. *
  9. * History:
  10. *
  11. * Copyright (c) 1990 Microsoft Corporation
  12. *
  13. \******************************************************************************/
  14. #include <windows.h>
  15. #define MMNOMIDI
  16. #define MMNOWAVE
  17. #define MMNOSOUND
  18. #define MMNOTIMER
  19. #define MMNOJOY
  20. #define MMNOSEQ
  21. #include "mmsystem.h"
  22. #define NOMIDIDEV
  23. #define NOWAVEDEV
  24. #define NOTIMERDEV
  25. #define NOJOYDEV
  26. #define NOSEQDEV
  27. #define NOTASKDEV
  28. #include "mmddk.h"
  29. #include "mmsysi.h"
  30. #include "thunks.h"
  31. #ifndef STATICFN
  32. #define STATICFN
  33. #endif
  34. extern char far szOpen[]; // in MCI.C
  35. static SZCODE szNull[] = "";
  36. static SZCODE szMciExtensions[] = "mci extensions";
  37. #define MCI_EXTENSIONS szMciExtensions
  38. #define MCI_PROFILE_STRING_LENGTH 255
  39. //!!#define TOLOWER(c) ((c) >= 'A' && (c) <= 'Z' ? (c) + 'a' - 'A' : c)
  40. // The device list is initialized on the first call to mciSendCommand or
  41. // to mciSendString
  42. BOOL MCI_bDeviceListInitialized;
  43. // The next device ID to use for a new device
  44. UINT MCI_wNextDeviceID = 1;
  45. // The list of MCI devices. This list grows and shrinks as needed.
  46. // The first offset MCI_lpDeviceList[0] is a placeholder and is unused
  47. // because device 0 is defined as no device.
  48. LPMCI_DEVICE_NODE FAR * MCI_lpDeviceList;
  49. // The current size of the list of MCI devices
  50. UINT MCI_wDeviceListSize;
  51. // The internal mci heap used by mciAlloc and mciFree
  52. HGLOBAL hMciHeap;
  53. // File containing MCI device profile strings
  54. extern char far szSystemIni[]; // in INIT.C
  55. // Name of the section contining MCI device profile strings
  56. static SZCODE szMCISectionName[] = "mci";
  57. static SZCODE szAllDeviceName[] = "all";
  58. static SZCODE szUnsignedFormat[] = "%u";
  59. static void PASCAL NEAR mciFreeDevice(LPMCI_DEVICE_NODE nodeWorking);
  60. BOOL NEAR PASCAL CouldBe16bitDrv(UINT wDeviceID)
  61. {
  62. if (wDeviceID == MCI_ALL_DEVICE_ID) return TRUE;
  63. if (MCI_VALID_DEVICE_ID(wDeviceID)) {
  64. if (MCI_lpDeviceList[wDeviceID]->dwMCIFlags & MCINODE_16BIT_DRIVER) {
  65. return TRUE;
  66. }
  67. }
  68. return FALSE;
  69. }
  70. BOOL NEAR PASCAL Is16bitDrv(UINT wDeviceID)
  71. {
  72. if (wDeviceID == MCI_ALL_DEVICE_ID) return FALSE;
  73. if (MCI_VALID_DEVICE_ID(wDeviceID)) {
  74. if (MCI_lpDeviceList[wDeviceID]->dwMCIFlags & MCINODE_16BIT_DRIVER) {
  75. return TRUE;
  76. }
  77. }
  78. return FALSE;
  79. }
  80. //
  81. // Initialize device list
  82. // Called once by mciSendString or mciSendCommand
  83. // Returns TRUE on success
  84. BOOL NEAR PASCAL mciInitDeviceList(void)
  85. {
  86. if ((hMciHeap = HeapCreate(0)) == 0)
  87. {
  88. DOUT("Mci heap create failed!\r\n");
  89. return FALSE;
  90. }
  91. if ((MCI_lpDeviceList = mciAlloc (sizeof (LPMCI_DEVICE_NODE) *
  92. (MCI_INIT_DEVICE_LIST_SIZE + 1))) != NULL)
  93. {
  94. MCI_wDeviceListSize = MCI_INIT_DEVICE_LIST_SIZE;
  95. MCI_bDeviceListInitialized = TRUE;
  96. return TRUE;
  97. } else
  98. {
  99. DOUT ("MCIInit: could not allocate master MCI device list\r\n");
  100. return FALSE;
  101. }
  102. }
  103. /*
  104. * @doc EXTERNAL MCI
  105. * @api UINT | mciGetDeviceIDFromElementID | This function
  106. * retrieves the MCI device ID corresponding to and element ID
  107. *
  108. * @parm DWORD | dwElementID | The element ID
  109. *
  110. * @parm LPCSTR | lpstrType | The type name this element ID belongs to
  111. *
  112. * @rdesc Returns the device ID assigned when it was opened and used in the
  113. * <f mciSendCommand> function. Returns zero if the device name was not known,
  114. * if the device was not open, or if there was not enough memory to complete
  115. * the operation or if lpstrType is NULL.
  116. *
  117. */
  118. UINT WINAPI mciGetDeviceIDFromElementID (
  119. DWORD dwElementID,
  120. LPCSTR lpstrType)
  121. {
  122. UINT wID;
  123. LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
  124. char strTemp[MCI_MAX_DEVICE_TYPE_LENGTH];
  125. if (lpstrType == NULL)
  126. return 0;
  127. wID = (UINT)mciMessage( THUNK_MCI_GETDEVIDFROMELEMID, dwElementID,
  128. (DWORD)lpstrType, 0L, 0L );
  129. if ( wID == 0 ) {
  130. nodeCounter = &MCI_lpDeviceList[1];
  131. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  132. {
  133. nodeWorking = *nodeCounter++;
  134. if (nodeWorking == NULL)
  135. continue;
  136. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID &&
  137. nodeWorking->dwElementID == dwElementID)
  138. if (LoadString (ghInst, nodeWorking->wDeviceType, strTemp,
  139. sizeof(strTemp)) != 0
  140. && lstrcmpi ((LPSTR)strTemp, lpstrType) == 0) {
  141. return (wID);
  142. }
  143. }
  144. return 0;
  145. }
  146. return wID;
  147. }
  148. // Retrieves the device ID corresponding to the name of an opened device
  149. // matching the given task
  150. // This fn only looks for 16-bit devices
  151. // See mciGetDeviceIDInternalEx that looks for all of them
  152. UINT NEAR PASCAL mciGetDeviceIDInternal (
  153. LPCSTR lpstrName,
  154. HTASK hTask)
  155. {
  156. UINT wID;
  157. LPMCI_DEVICE_NODE nodeWorking, FAR *nodeCounter;
  158. if (lstrcmpi (lpstrName, szAllDeviceName) == 0)
  159. return MCI_ALL_DEVICE_ID;
  160. if (MCI_lpDeviceList == NULL)
  161. return 0;
  162. // Loop through the MCI device list
  163. nodeCounter = &MCI_lpDeviceList[1];
  164. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  165. {
  166. nodeWorking = *nodeCounter++;
  167. if (nodeWorking == NULL)
  168. continue;
  169. // If this device does not have a name then skip it
  170. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID)
  171. continue;
  172. // If the names match
  173. if (lstrcmpi(nodeWorking->lpstrName, lpstrName) == 0)
  174. // If the device belongs to the indicated task
  175. if (nodeWorking->hOpeningTask == hTask)
  176. // Return this device ID
  177. return wID;
  178. }
  179. return 0;
  180. }
  181. /*
  182. * @doc EXTERNAL MCI
  183. * @api UINT | mciGetDeviceID | This function retrieves the device
  184. * ID corresponding to the name of an open MCI device.
  185. *
  186. * @parm LPCSTR | lpstrName | Specifies the device name used to open the
  187. * MCI device.
  188. *
  189. * @rdesc Returns the device ID assigned when the device was opened.
  190. * Returns zero if the device name isn't known,
  191. * if the device isn't open, or if there was insufficient memory to complete
  192. * the operation. Each compound device element has a unique device ID.
  193. * The ID of the "all" device is MCI_ALL_DEVICE_ID.
  194. *
  195. * @xref MCI_OPEN
  196. *
  197. */
  198. UINT WINAPI mciGetDeviceID (
  199. LPCSTR lpstrName)
  200. {
  201. UINT wDevID;
  202. /*
  203. ** Try the 32 bit side first
  204. */
  205. wDevID = (UINT)mciMessage( THUNK_MCI_GETDEVICEID, (DWORD)lpstrName,
  206. 0L, 0L, 0L );
  207. if ( wDevID == 0 ) {
  208. /*
  209. ** The 32 bit call failed so let the 16 bit side have a go.
  210. */
  211. wDevID = mciGetDeviceIDInternal (lpstrName, GetCurrentTask());
  212. }
  213. return wDevID;
  214. }
  215. //
  216. // This function is same as mciGetDeviceID but it won't call GetCurrentTask
  217. // Used when mci needs to verify the dev alias had not been allocated yet
  218. //
  219. //
  220. UINT NEAR PASCAL mciGetDeviceIDInternalEx(
  221. LPCSTR lpstrName,
  222. HTASK hTask)
  223. {
  224. UINT uiDevID;
  225. uiDevID = (UINT)mciMessage( THUNK_MCI_GETDEVICEID, (DWORD)lpstrName,
  226. 0L, 0L, 0L );
  227. if (0 == uiDevID) {
  228. uiDevID = mciGetDeviceIDInternal(lpstrName, hTask);
  229. }
  230. return uiDevID;
  231. }
  232. /*
  233. * @doc EXTERNAL MCI
  234. * @api HTASK | mciGetCreatorTask | This function retrieves the creator task
  235. * corresponding with the device ID passed.
  236. *
  237. * @parm UINT | wDeviceID | Specifies the device ID whose creator task is to
  238. * be returned.
  239. *
  240. * @rdesc Returns the creator task responsible for opening the device, else
  241. * NULL if the device ID passed is invalid.
  242. *
  243. */
  244. HTASK WINAPI mciGetCreatorTask (
  245. UINT wDeviceID)
  246. {
  247. /*
  248. ** Is this a 16 bit device ID
  249. */
  250. if (Is16bitDrv(wDeviceID)) {
  251. return MCI_lpDeviceList[wDeviceID]->hCreatorTask;
  252. }
  253. /*
  254. ** No, so pass it on to the 32 bit code.
  255. */
  256. return (HTASK)mciMessage( THUNK_MCI_GETCREATORTASK, (DWORD)wDeviceID,
  257. 0L, 0L, 0L );
  258. }
  259. /*
  260. * @doc INTERNAL MCI
  261. * @func BOOL | 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 LPCSTR | lpstrDeviceName | The device name, possibly
  266. * with trailing digits but no blanks.
  267. *
  268. * @parm LPCSTR | 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 PASCAL NEAR
  275. mciDeviceMatch(
  276. LPCSTR lpstrDeviceName,
  277. LPCSTR lpstrDeviceType
  278. )
  279. {
  280. BOOL bAtLeastOne;
  281. for (bAtLeastOne = FALSE;;)
  282. if (!*lpstrDeviceType)
  283. break;
  284. else if (!*lpstrDeviceName || ((BYTE)(WORD)(DWORD)AnsiLower((LPSTR)(DWORD)(WORD)(*lpstrDeviceName++)) != (BYTE)(WORD)(DWORD)AnsiLower((LPSTR)(DWORD)(WORD)(*lpstrDeviceType++))))
  285. return FALSE;
  286. else
  287. bAtLeastOne = TRUE;
  288. if (!bAtLeastOne)
  289. return FALSE;
  290. for (; *lpstrDeviceName; lpstrDeviceName++)
  291. if ((*lpstrDeviceName < '0') || (*lpstrDeviceName > '9'))
  292. return FALSE;
  293. return TRUE;
  294. }
  295. /*
  296. * @doc INTERNAL MCI
  297. * @func UINT | mciLookUpType | Look up the type given a type name
  298. *
  299. * @parm LPCSTR | lpstrTypeName | The type name to look up. Trailing
  300. * digits are ignored.
  301. *
  302. * @rdesc The MCI type number (MCI_DEVTYPE_<x>) or 0 if not found
  303. *
  304. !! * @comm Converts the input string to lower case as a side effect
  305. *
  306. */
  307. UINT PASCAL NEAR mciLookUpType (
  308. LPCSTR lpstrTypeName)
  309. {
  310. UINT wType;
  311. char strType[MCI_MAX_DEVICE_TYPE_LENGTH];
  312. //!! mciToLower (lpstrTypeName);
  313. for (wType = MCI_DEVTYPE_FIRST; wType <= MCI_DEVTYPE_LAST; ++wType)
  314. {
  315. if (LoadString (ghInst, wType, strType, sizeof(strType)) == 0)
  316. {
  317. DOUT ("mciLookUpType: could not load string for type\r\n");
  318. continue;
  319. }
  320. if (mciDeviceMatch (lpstrTypeName, strType))
  321. return wType;
  322. }
  323. return 0;
  324. }
  325. /*
  326. * @doc INTERNAL MCI
  327. * @func DWORD | mciSysinfo | Get system information about a device
  328. *
  329. * @parm UINT | wDeviceID | Device ID, may be 0
  330. *
  331. * @parm DWORD | dwFlags | SYSINFO flags
  332. *
  333. * @parm LPMCI_SYSINFO_PARMS | lpSysinfo | SYSINFO parameters
  334. *
  335. * @rdesc 0 if successful, otherwise error code
  336. *
  337. */
  338. DWORD PASCAL NEAR mciSysinfo (
  339. UINT wDeviceID,
  340. DWORD dwFlags,
  341. LPMCI_SYSINFO_PARMS lpSysinfo)
  342. {
  343. UINT wCounted;
  344. char strBuffer[MCI_PROFILE_STRING_LENGTH];
  345. LPSTR lpstrBuffer = (LPSTR)strBuffer, lpstrStart;
  346. if (dwFlags & MCI_SYSINFO_NAME && lpSysinfo->dwNumber == 0)
  347. return MCIERR_OUTOFRANGE;
  348. if (lpSysinfo->lpstrReturn == NULL || lpSysinfo->dwRetSize == 0)
  349. return MCIERR_PARAM_OVERFLOW;
  350. if (dwFlags & MCI_SYSINFO_NAME && dwFlags & MCI_SYSINFO_QUANTITY)
  351. return MCIERR_FLAGS_NOT_COMPATIBLE;
  352. if (dwFlags & MCI_SYSINFO_INSTALLNAME)
  353. {
  354. LPMCI_DEVICE_NODE nodeWorking;
  355. if (wDeviceID == MCI_ALL_DEVICE_ID)
  356. return MCIERR_CANNOT_USE_ALL;
  357. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  358. return MCIERR_INVALID_DEVICE_NAME;
  359. nodeWorking = MCI_lpDeviceList[wDeviceID];
  360. if ((DWORD)lstrlen (nodeWorking->lpstrInstallName) >= lpSysinfo->dwRetSize)
  361. return MCIERR_PARAM_OVERFLOW;
  362. lstrcpy (lpSysinfo->lpstrReturn, nodeWorking->lpstrInstallName);
  363. return 0;
  364. } else if (!(dwFlags & MCI_SYSINFO_OPEN))
  365. {
  366. if (wDeviceID != MCI_ALL_DEVICE_ID && lpSysinfo->wDeviceType == 0)
  367. return MCIERR_DEVICE_TYPE_REQUIRED;
  368. if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
  369. return MCIERR_MISSING_PARAMETER;
  370. GetPrivateProfileString (szMCISectionName, NULL, szNull,
  371. lpstrBuffer, MCI_PROFILE_STRING_LENGTH,
  372. szSystemIni);
  373. wCounted = 0;
  374. while (TRUE)
  375. {
  376. if (dwFlags & MCI_SYSINFO_QUANTITY)
  377. {
  378. if (*lpstrBuffer == '\0')
  379. {
  380. *(LPDWORD)lpSysinfo->lpstrReturn = (DWORD)wCounted;
  381. return MCI_INTEGER_RETURNED;
  382. }
  383. if (wDeviceID == MCI_ALL_DEVICE_ID ||
  384. mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
  385. ++wCounted;
  386. // Skip past the terminating '\0'
  387. while (*lpstrBuffer != '\0')
  388. *lpstrBuffer++;
  389. lpstrBuffer++;
  390. } else if (dwFlags & MCI_SYSINFO_NAME)
  391. {
  392. if (wCounted == (UINT)lpSysinfo->dwNumber)
  393. {
  394. lstrcpy (lpSysinfo->lpstrReturn, lpstrStart);
  395. return 0L;
  396. } else if (*lpstrBuffer == '\0')
  397. return MCIERR_OUTOFRANGE;
  398. else
  399. {
  400. lpstrStart = lpstrBuffer;
  401. if (wDeviceID == MCI_ALL_DEVICE_ID ||
  402. mciLookUpType (lpstrBuffer) == lpSysinfo->wDeviceType)
  403. ++wCounted;
  404. // Skip past the terminating '\0'
  405. while (*lpstrBuffer != '\0')
  406. *lpstrBuffer++;
  407. lpstrBuffer++;
  408. }
  409. }
  410. }
  411. } else
  412. // Process MCI_SYSINFO_OPEN cases
  413. {
  414. UINT wID;
  415. HTASK hCurrentTask = GetCurrentTask();
  416. LPMCI_DEVICE_NODE Node;
  417. if (wDeviceID != MCI_ALL_DEVICE_ID &&
  418. lpSysinfo->wDeviceType == 0)
  419. return MCIERR_DEVICE_TYPE_REQUIRED;
  420. if ((dwFlags & (MCI_SYSINFO_QUANTITY | MCI_SYSINFO_NAME)) == 0)
  421. return MCIERR_MISSING_PARAMETER;
  422. wCounted = 0;
  423. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  424. {
  425. if ((Node = MCI_lpDeviceList[wID]) == 0)
  426. continue;
  427. if (wDeviceID == MCI_ALL_DEVICE_ID &&
  428. Node->hOpeningTask == hCurrentTask)
  429. ++wCounted;
  430. else
  431. {
  432. if (Node->wDeviceType == lpSysinfo->wDeviceType &&
  433. Node->hOpeningTask == hCurrentTask)
  434. ++wCounted;
  435. }
  436. if (dwFlags & MCI_SYSINFO_NAME &&
  437. wCounted == (UINT)lpSysinfo->dwNumber)
  438. {
  439. lstrcpy (lpSysinfo->lpstrReturn, Node->lpstrName);
  440. return 0L;
  441. }
  442. }
  443. if (dwFlags & MCI_SYSINFO_NAME)
  444. {
  445. if (lpSysinfo->lpstrReturn != NULL)
  446. lpSysinfo->lpstrReturn = '\0';
  447. return MCIERR_OUTOFRANGE;
  448. } else if (dwFlags & MCI_SYSINFO_QUANTITY &&
  449. lpSysinfo->lpstrReturn != NULL &&
  450. lpSysinfo->dwRetSize >= 4)
  451. *(LPDWORD)lpSysinfo->lpstrReturn = wCounted;
  452. }
  453. return MCI_INTEGER_RETURNED;
  454. }
  455. /*
  456. * @doc INTERNAL MCI
  457. * @func UINT | wAddDeviceNodeToList | Add the given global handle into the
  458. * MCI device table and return that entry's ID#
  459. *
  460. * @parm LPMCI_DEVICE_NODE | node | device description
  461. *
  462. * @rdesc The ID value for this device or 0 if there is no memory to expand
  463. * the device list
  464. *
  465. */
  466. STATICFN UINT PASCAL NEAR
  467. wAddDeviceNodeToList(
  468. LPMCI_DEVICE_NODE node
  469. )
  470. {
  471. UINT wDeviceID = node->wDeviceID;
  472. LPMCI_DEVICE_NODE FAR *lpTempList;
  473. UINT iReallocSize;
  474. while (wDeviceID >= MCI_wDeviceListSize)
  475. {
  476. // The list is full so try to grow it
  477. iReallocSize = MCI_wDeviceListSize + 1 + MCI_DEVICE_LIST_GROW_SIZE;
  478. iReallocSize *= sizeof(LPMCI_DEVICE_NODE);
  479. if ((lpTempList = mciReAlloc(MCI_lpDeviceList, iReallocSize)) == NULL)
  480. {
  481. DOUT ("wReserveDeviceID: cannot grow device list\r\n");
  482. return 0;
  483. }
  484. MCI_lpDeviceList = lpTempList;
  485. MCI_wDeviceListSize += MCI_DEVICE_LIST_GROW_SIZE;
  486. }
  487. if (wDeviceID >= MCI_wNextDeviceID) {
  488. MCI_wNextDeviceID = wDeviceID + 1;
  489. }
  490. MCI_lpDeviceList[wDeviceID] = node;
  491. return wDeviceID;
  492. }
  493. //
  494. // Allocate space for the given string and assign the name to the given
  495. // device.
  496. // Return FALSE if could not allocate memory
  497. //
  498. STATICFN BOOL PASCAL NEAR
  499. mciAddDeviceName(
  500. LPMCI_DEVICE_NODE nodeWorking,
  501. LPCSTR lpDeviceName
  502. )
  503. {
  504. nodeWorking->lpstrName = mciAlloc(lstrlen(lpDeviceName)+1);
  505. if (nodeWorking->lpstrName == NULL)
  506. {
  507. DOUT ("mciAddDeviceName: Out of memory allocating device name\r\n");
  508. return FALSE;
  509. }
  510. // copy device name to mci node and lowercase it
  511. lstrcpy(nodeWorking->lpstrName, lpDeviceName);
  512. //!! mciToLower(nodeWorking->lpstrName);
  513. return TRUE;
  514. }
  515. /*
  516. * @doc INTERNAL MCI
  517. * @func UINT | mciAllocateNode | Allocate a new driver entry
  518. *
  519. * @parm DWORD | dwFlags | As sent with MCI_OPEN message
  520. * @parm LPCSTR | lpDeviceName | The device name
  521. * @parm LPMCI_DEVICE_NODE FAR * | lpnodeNew | new node allocated
  522. *
  523. * @rdesc The device ID to the new node. 0 on error.
  524. *
  525. */
  526. STATICFN UINT PASCAL NEAR mciAllocateNode(
  527. DWORD dwFlags,
  528. LPCSTR lpDeviceName,
  529. LPMCI_DEVICE_NODE FAR *lpnodeNew
  530. )
  531. {
  532. LPMCI_DEVICE_NODE nodeWorking;
  533. UINT wDeviceID;
  534. if ((nodeWorking = mciAlloc(sizeof(MCI_DEVICE_NODE))) == NULL)
  535. {
  536. DOUT("Out of memory in mciAllocateNode\r\n");
  537. return 0;
  538. }
  539. // The device ID is a global resource so we fetch it from 32-bit MCI.
  540. // A node is also allocated on the 32-bit side, and marked as 16-bit. The
  541. // node will be freed during mciFreeDevice, and acts as a place holder for
  542. // the device ID.
  543. wDeviceID = (UINT) mciMessage(THUNK_MCI_ALLOCATE_NODE,
  544. dwFlags,
  545. (DWORD)lpDeviceName,
  546. 0L, 0L);
  547. // Copy the working node to the device list
  548. nodeWorking->wDeviceID = wDeviceID;
  549. if (wAddDeviceNodeToList(nodeWorking) == 0)
  550. {
  551. DOUT ("mciAllocateNode: Cannot allocate new node\r\n");
  552. mciFree(nodeWorking);
  553. return 0;
  554. }
  555. // Initialize node
  556. nodeWorking->hCreatorTask = GetCurrentTask ();
  557. nodeWorking->dwMCIFlags |= MCINODE_16BIT_DRIVER;
  558. if (dwFlags & MCI_OPEN_ELEMENT_ID) {
  559. // No device name, just an element ID
  560. nodeWorking->dwElementID = (DWORD)lpDeviceName;
  561. }
  562. else {
  563. if (!mciAddDeviceName (nodeWorking, lpDeviceName))
  564. {
  565. mciFree (nodeWorking);
  566. return 0;
  567. }
  568. }
  569. *lpnodeNew = nodeWorking;
  570. return nodeWorking->wDeviceID;
  571. }
  572. //
  573. // Reparse the original command parameters
  574. // Returns MCIERR code. If the reparse fails the original error code
  575. // from the first parsing is returned.
  576. //
  577. STATICFN UINT PASCAL NEAR
  578. mciReparseOpen(
  579. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
  580. UINT wCustomTable,
  581. UINT wTypeTable,
  582. LPDWORD lpdwFlags,
  583. LPMCI_OPEN_PARMS FAR *lplpOpen,
  584. UINT wDeviceID
  585. )
  586. {
  587. LPSTR lpCommand;
  588. LPDWORD lpdwParams;
  589. UINT wErr;
  590. DWORD dwOldFlags = *lpdwFlags;
  591. // If the custom table contains no open command
  592. if (wCustomTable == -1 ||
  593. (lpCommand = FindCommandInTable (wCustomTable, szOpen, NULL)) == NULL)
  594. {
  595. // Try the type specific table
  596. lpCommand = FindCommandInTable (wTypeTable, szOpen, NULL);
  597. // If it still cannot be parsed
  598. if (lpCommand == NULL)
  599. return lpOpenInfo->wParsingError;
  600. wCustomTable = wTypeTable;
  601. }
  602. // A new version of 'open' was found
  603. // Free previous set of parameters
  604. mciParserFree (lpOpenInfo->lpstrPointerList);
  605. *lpdwFlags = 0;
  606. if ((lpdwParams =
  607. (LPDWORD)mciAlloc (sizeof(DWORD) * MCI_MAX_PARAM_SLOTS))
  608. == NULL)
  609. return MCIERR_OUT_OF_MEMORY;
  610. wErr = mciParseParams (lpOpenInfo->lpstrParams, lpCommand,
  611. lpdwFlags,
  612. (LPSTR)lpdwParams,
  613. sizeof(DWORD) * MCI_MAX_PARAM_SLOTS,
  614. &lpOpenInfo->lpstrPointerList, NULL);
  615. // We don't need this around anymore
  616. mciUnlockCommandTable (wCustomTable);
  617. // If there was a parsing error
  618. if (wErr != 0)
  619. {
  620. // Make sure this does not get free'd by mciSendString
  621. lpOpenInfo->lpstrPointerList = NULL;
  622. mciFree (lpdwParams);
  623. return wErr;
  624. }
  625. if (dwOldFlags & MCI_OPEN_TYPE)
  626. {
  627. // Device type was already extracted so add it manually
  628. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrDeviceType
  629. = (*lplpOpen)->lpstrDeviceType;
  630. *lpdwFlags |= MCI_OPEN_TYPE;
  631. }
  632. if (dwOldFlags & MCI_OPEN_ELEMENT)
  633. {
  634. // Element name was already extracted so add it manually
  635. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrElementName
  636. = (*lplpOpen)->lpstrElementName;
  637. *lpdwFlags |= MCI_OPEN_ELEMENT;
  638. }
  639. if (dwOldFlags & MCI_OPEN_ALIAS)
  640. {
  641. // Alias name was already extracted so add it manually
  642. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrAlias
  643. = (*lplpOpen)->lpstrAlias;
  644. *lpdwFlags |= MCI_OPEN_ALIAS;
  645. }
  646. if (dwOldFlags & MCI_NOTIFY)
  647. // Notify was already extracted so add it manually
  648. ((LPMCI_OPEN_PARMS)lpdwParams)->dwCallback
  649. = (*lplpOpen)->dwCallback;
  650. // Replace old parameter list with new list
  651. *lplpOpen = (LPMCI_OPEN_PARMS)lpdwParams;
  652. return 0;
  653. }
  654. // See if lpstrDriverName exists in the profile strings of the [mci]
  655. // section and return the keyname in lpstrDevice and the
  656. // profile string in lpstrProfString
  657. // Returns 0 on success or an error code
  658. STATICFN UINT PASCAL NEAR
  659. mciFindDriverName(
  660. LPCSTR lpstrDriverName,
  661. LPSTR lpstrDevice,
  662. LPSTR lpstrProfString,
  663. UINT wProfLength
  664. )
  665. {
  666. LPSTR lpstrEnum, lpstrEnumStart;
  667. UINT wEnumLen = 100;
  668. UINT wErr;
  669. LPSTR lpstrDriverTemp, lpstrProfTemp;
  670. // Enumerate values, trying until they fit into the buffer
  671. while (TRUE) {
  672. if ((lpstrEnum = mciAlloc (wEnumLen)) == NULL)
  673. return MCIERR_OUT_OF_MEMORY;
  674. wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
  675. NULL, szNull, lpstrEnum, wEnumLen,
  676. szSystemIni);
  677. if (*lpstrEnum == '\0')
  678. {
  679. mciFree (lpstrEnum);
  680. return MCIERR_DEVICE_NOT_INSTALLED;
  681. }
  682. if (wErr == wEnumLen - 2)
  683. {
  684. wEnumLen *= 2;
  685. mciFree (lpstrEnum);
  686. } else
  687. break;
  688. }
  689. lpstrEnumStart = lpstrEnum;
  690. if (lstrlen(lpstrDriverName) >= MCI_MAX_DEVICE_TYPE_LENGTH) {
  691. wErr = MCIERR_DEVICE_LENGTH;
  692. goto exit_fn;
  693. }
  694. lstrcpy(lpstrDevice, lpstrDriverName);
  695. //!! mciToLower (lpstrDevice);
  696. // Walk through each string
  697. while (TRUE) {
  698. wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
  699. lpstrEnum, szNull, lpstrProfString,
  700. wProfLength,
  701. szSystemIni);
  702. if (*lpstrProfString == '\0')
  703. {
  704. DOUT ("mciFindDriverName: cannot load valid keyname\r\n");
  705. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  706. goto exit_fn;
  707. }
  708. // See if driver pathname matches input
  709. //!! mciToLower (lpstrProfString);
  710. lpstrDriverTemp = lpstrDevice;
  711. lpstrProfTemp = lpstrProfString;
  712. // Find end of file name
  713. while (*lpstrProfTemp != '\0' && *lpstrProfTemp != ' ')
  714. ++lpstrProfTemp;
  715. // Find begining of simple file name
  716. --lpstrProfTemp;
  717. while (*lpstrProfTemp != '\\' && *lpstrProfTemp != '/' &&
  718. *lpstrProfTemp != ':')
  719. if (--lpstrProfTemp < lpstrProfString)
  720. break;
  721. ++lpstrProfTemp;
  722. // Compare to input
  723. while (*lpstrDriverTemp != '\0')
  724. if (*lpstrDriverTemp++ != *lpstrProfTemp++ ||
  725. (UINT)(lpstrProfTemp - lpstrProfString) >= wProfLength)
  726. {
  727. --lpstrProfTemp;
  728. break;
  729. }
  730. // If the input was contained in the profile string and followed by
  731. // a space or a '.' the we've got it!
  732. if (*lpstrDriverTemp == '\0' &&
  733. (*lpstrProfTemp == ' ' || *lpstrProfTemp == '.'))
  734. {
  735. if (lstrlen (lpstrEnum) >= MCI_MAX_DEVICE_TYPE_LENGTH)
  736. {
  737. DOUT ("mciFindDriverName: device name too long\r\n");
  738. wErr = MCIERR_DEVICE_LENGTH;
  739. goto exit_fn;
  740. }
  741. lstrcpy (lpstrDevice, lpstrEnum);
  742. wErr = 0;
  743. goto exit_fn;
  744. }
  745. // Skip to next keyname
  746. while (*lpstrEnum++ != '\0') {}
  747. // Error if no more left
  748. if (*lpstrEnum == 0)
  749. {
  750. wErr = MCIERR_INVALID_DEVICE_NAME;
  751. goto exit_fn;
  752. }
  753. }
  754. exit_fn:
  755. mciFree (lpstrEnumStart);
  756. return wErr;
  757. }
  758. //
  759. // Identifies the driver name to load
  760. // Loads the driver
  761. // Reparses open command if necessary
  762. // Sets a default break key
  763. //
  764. // lpOpenInfo contains various info for reparsing
  765. //
  766. // bDefaultAlias indicates that the alias need not be verified because
  767. // it was internally assigned
  768. //
  769. STATICFN UINT PASCAL NEAR
  770. mciLoadDevice(
  771. DWORD dwFlags,
  772. LPMCI_OPEN_PARMS lpOpen,
  773. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo,
  774. BOOL bDefaultAlias
  775. )
  776. {
  777. LPMCI_DEVICE_NODE nodeWorking;
  778. HINSTANCE hDriver;
  779. UINT wID, wErr;
  780. char strProfileString[MCI_PROFILE_STRING_LENGTH];
  781. MCI_OPEN_DRIVER_PARMS DriverOpen;
  782. HDRVR hDrvDriver;
  783. LPSTR lpstrParams;
  784. LPCSTR lpstrInstallName, lpstrDeviceName;
  785. LPSTR lpstrCopy = NULL;
  786. LPMCI_OPEN_PARMS lpOriginalOpenParms = lpOpen;
  787. /* Check for the device name in SYSTEM.INI */
  788. lpstrInstallName = lpOpen->lpstrDeviceType;
  789. wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
  790. lpstrInstallName,
  791. szNull, (LPSTR)strProfileString,
  792. MCI_PROFILE_STRING_LENGTH,
  793. szSystemIni);
  794. // If device name not found
  795. if (wErr == 0)
  796. {
  797. int nLen = lstrlen (lpstrInstallName);
  798. int index;
  799. // Try for the device name with a '1' thru a '9' appended to it
  800. if ((lpstrCopy = (LPSTR)mciAlloc (nLen + 2)) // space for digit too
  801. == NULL)
  802. {
  803. DOUT ("mciLoadDevice: cannot allocate device name copy\r\n");
  804. return MCIERR_OUT_OF_MEMORY;
  805. }
  806. lstrcpy (lpstrCopy, lpstrInstallName);
  807. lpstrCopy[nLen + 1] = '\0';
  808. for (index = 1; index <= 9; ++index)
  809. {
  810. lpstrCopy[nLen] = (char)('0' + index);
  811. wErr = GetPrivateProfileString ((LPSTR)szMCISectionName,
  812. lpstrCopy,
  813. szNull, (LPSTR)strProfileString,
  814. MCI_PROFILE_STRING_LENGTH,
  815. szSystemIni);
  816. if (wErr != 0)
  817. break;
  818. }
  819. if (wErr == 0)
  820. {
  821. mciFree (lpstrCopy);
  822. if ((lpstrCopy = (LPSTR)mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH))
  823. == NULL)
  824. {
  825. DOUT ("mciLoadDevice: cannot allocate device name copy\r\n");
  826. return MCIERR_OUT_OF_MEMORY;
  827. }
  828. if ((wErr = mciFindDriverName (lpstrInstallName, lpstrCopy,
  829. (LPSTR)strProfileString,
  830. MCI_PROFILE_STRING_LENGTH)) != 0)
  831. goto exit_fn;
  832. }
  833. lpstrInstallName = lpstrCopy;
  834. }
  835. // Break out the device driver pathname and the parameter list
  836. lpstrParams = strProfileString;
  837. // Eat blanks
  838. while (*lpstrParams != ' ' && *lpstrParams != '\0')
  839. ++lpstrParams;
  840. // Terminate driver file name
  841. if (*lpstrParams == ' ') *lpstrParams++ = '\0';
  842. //Now "strProfileString" is the device driver and "lpstrParams" is
  843. //the parameter string
  844. if (dwFlags & (MCI_OPEN_ELEMENT | MCI_OPEN_ELEMENT_ID))
  845. lpstrDeviceName = lpOpen->lpstrElementName;
  846. else
  847. lpstrDeviceName = lpOpen->lpstrDeviceType;
  848. if (dwFlags & MCI_OPEN_ALIAS)
  849. {
  850. // If the alias is default then we've already checked its uniqueness
  851. if (!bDefaultAlias &&
  852. mciGetDeviceIDInternalEx (lpOpen->lpstrAlias,
  853. lpOpenInfo->hCallingTask) != 0)
  854. {
  855. wErr = MCIERR_DUPLICATE_ALIAS;
  856. goto exit_fn;
  857. }
  858. lpstrDeviceName = lpOpen->lpstrAlias;
  859. }
  860. wID = mciAllocateNode (dwFlags, lpstrDeviceName, &nodeWorking);
  861. if (wID == 0)
  862. {
  863. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  864. goto exit_fn;
  865. }
  866. // Identify the task which initiated the open command
  867. nodeWorking->hOpeningTask = lpOpenInfo->hCallingTask;
  868. // Initialize the driver
  869. DriverOpen.lpstrParams = lpstrParams;
  870. DriverOpen.wCustomCommandTable = (UINT)-1;
  871. DriverOpen.wType = 0;
  872. DriverOpen.wDeviceID = wID;
  873. // Load the driver
  874. hDrvDriver = OpenDriver ((LPSTR)strProfileString, szMCISectionName,
  875. (LPARAM)(DWORD)(LPMCI_OPEN_DRIVER_PARMS)&DriverOpen);
  876. if (hDrvDriver == NULL)
  877. {
  878. DOUT ("mciLoadDevice: OpenDriver failed\r\n");
  879. // Assume driver has free'd any custom command table when it failed the open
  880. mciFreeDevice (nodeWorking);
  881. wErr = MCIERR_CANNOT_LOAD_DRIVER;
  882. goto exit_fn;
  883. }
  884. lpOpen->wDeviceID = wID;
  885. lpOpen->wReserved0 = 0;
  886. hDriver = GetDriverModuleHandle (hDrvDriver);
  887. nodeWorking->hDrvDriver = hDrvDriver;
  888. nodeWorking->hDriver = hDriver;
  889. // Driver provides custom device table and type
  890. nodeWorking->wCustomCommandTable = DriverOpen.wCustomCommandTable;
  891. nodeWorking->wDeviceType = DriverOpen.wType;
  892. // Load driver's type table
  893. if ((nodeWorking->wCommandTable = mciLoadTableType (DriverOpen.wType))
  894. == -1)
  895. // Load from a file if necessary
  896. nodeWorking->wCommandTable =
  897. mciLoadCommandResource (ghInst, lpOpen->lpstrDeviceType,
  898. DriverOpen.wType);
  899. // Record this for 'sysinfo installname'
  900. if ((nodeWorking->lpstrInstallName =
  901. mciAlloc (lstrlen (lpstrInstallName) + 1))
  902. == NULL)
  903. {
  904. mciCloseDevice (wID, 0L, NULL, FALSE);
  905. wErr = MCIERR_OUT_OF_MEMORY;
  906. goto exit_fn;
  907. } else
  908. lstrcpy (nodeWorking->lpstrInstallName, lpstrInstallName);
  909. // Reparse the input command if no type was known the first time or if
  910. // there was a custom command table
  911. // and there were any open command parameters
  912. if (lpOpenInfo->lpstrParams != NULL)
  913. {
  914. if ((wErr = mciReparseOpen (lpOpenInfo,
  915. nodeWorking->wCustomCommandTable,
  916. nodeWorking->wCommandTable,
  917. &dwFlags, &lpOpen, wID)) != 0)
  918. {
  919. mciCloseDevice (wID, 0L, NULL, FALSE);
  920. goto exit_fn;
  921. }
  922. // If there is no custom command table but mciSendString had a parsing
  923. // error then close the device and report the error now
  924. } else if (lpOpenInfo->wParsingError != 0)
  925. {
  926. mciCloseDevice (wID, 0L, NULL, FALSE);
  927. wErr = lpOpenInfo->wParsingError;
  928. goto exit_fn;
  929. }
  930. /* Send MCI_OPEN_DRIVER command to device */
  931. wErr = LOWORD(mciSendCommand (wID, MCI_OPEN_DRIVER,
  932. dwFlags, (DWORD)lpOpen));
  933. // If the OPEN failed then close the device (don't send a CLOSE though)
  934. if (wErr != 0)
  935. mciCloseDevice (wID, 0L, NULL, FALSE);
  936. else
  937. // Set default break key
  938. mciSetBreakKey (nodeWorking->wDeviceID, VK_CANCEL, NULL);
  939. // If we replaced the open parms here then free them
  940. if (lpOriginalOpenParms != lpOpen && lpOpen != NULL)
  941. mciFree (lpOpen);
  942. exit_fn:
  943. if (lpstrCopy != NULL)
  944. mciFree (lpstrCopy);
  945. return wErr;
  946. }
  947. /*
  948. * @doc INTERNAL MCI
  949. * @func BOOL | mciExtractDeviceType | If the given device name ends with
  950. * a file extension (.???) then try to get a typename from the
  951. * [mci extensions] section of WIN.INI
  952. *
  953. * @parm LPCSTR | lpstrDeviceName | The name to get the type from
  954. *
  955. * @parm LPSTR | lpstrDeviceType | The device type, returned to caller.
  956. *
  957. * @parm UINT | wBufLen | The length of the output buffer
  958. *
  959. * @rdesc TRUE if the type was found, FALSE otherwise
  960. *
  961. */
  962. BOOL PASCAL NEAR mciExtractDeviceType (
  963. LPCSTR lpstrDeviceName,
  964. LPSTR lpstrDeviceType,
  965. UINT wBufLen)
  966. {
  967. LPCSTR lpstrExt = lpstrDeviceName;
  968. int i;
  969. // Goto end of string
  970. while (*lpstrExt != '\0')
  971. {
  972. // '!' case is handled elsewhere
  973. if (*lpstrExt == '!')
  974. return FALSE;
  975. ++lpstrExt;
  976. }
  977. // Must be at least 2 characters in string
  978. if (lpstrExt - lpstrDeviceName < 2)
  979. return FALSE;
  980. lpstrExt -= 1;
  981. // Does not count if last character is '.'
  982. if (*lpstrExt == '.')
  983. return FALSE;
  984. lpstrExt -= 1;
  985. // Now looking at second to the last character. Check this and the two
  986. // previous characters for a '.'
  987. for (i=1; i<=3; ++i)
  988. {
  989. // Cannot have path separator here
  990. if (*lpstrExt == '/' || *lpstrExt == '\\')
  991. return FALSE;
  992. if (*lpstrExt == '.')
  993. {
  994. ++lpstrExt;
  995. if (GetProfileString (MCI_EXTENSIONS, lpstrExt, szNull,
  996. lpstrDeviceType, wBufLen) != 0)
  997. return TRUE;
  998. }
  999. if (lpstrExt == lpstrDeviceName)
  1000. return FALSE;
  1001. --lpstrExt;
  1002. }
  1003. return FALSE;
  1004. }
  1005. // Copy characters up to cSeparater into output which is allocated
  1006. // by this function using mciAlloc. Return the input pointer pointing
  1007. // to the character after cSeparator
  1008. // unless the separator is '\0' in which case it points to the end.
  1009. //
  1010. // Return the allocated pointer
  1011. //
  1012. // If bMustFind then the output string is created only if the token
  1013. // is found and is otherwise NULL. Else the output string is always created.
  1014. //
  1015. // cSeparator is ignored inside matching quotes ("abd"), the quotes
  1016. // are not coppied and doubled
  1017. // quotes inside are compressed to one. There must be a terminating quote.
  1018. // Quotes are treated normally unless the first character is a quote
  1019. //
  1020. // Function return value is 0 or an MCIERR code. A missing separator does
  1021. // not cause an error return.
  1022. UINT PASCAL NEAR mciEatToken (LPCSTR FAR *lplpstrInput, char cSeparater,
  1023. LPSTR FAR *lplpstrOutput, BOOL bMustFind)
  1024. {
  1025. LPCSTR lpstrEnd = *lplpstrInput, lpstrCounter;
  1026. LPSTR lpstrOutput;
  1027. UINT wLen;
  1028. BOOL bInQuotes = FALSE, bParseQuotes = TRUE, bQuoted = FALSE;
  1029. // Clear output
  1030. *lplpstrOutput = NULL;
  1031. // Scan for token or end of string
  1032. while ((*lpstrEnd != cSeparater || bInQuotes) && *lpstrEnd != '\0')
  1033. {
  1034. // If quote
  1035. if (*lpstrEnd == '"' && bParseQuotes)
  1036. {
  1037. // If inside quotes
  1038. if (bInQuotes)
  1039. {
  1040. // If next character is a quote also
  1041. if (*(lpstrEnd + 1) == '"')
  1042. // Skip it
  1043. ++lpstrEnd;
  1044. else
  1045. bInQuotes = FALSE;
  1046. } else
  1047. {
  1048. bInQuotes = TRUE;
  1049. bQuoted = TRUE;
  1050. }
  1051. } else if (!bInQuotes)
  1052. {
  1053. if (bQuoted)
  1054. return MCIERR_EXTRA_CHARACTERS;
  1055. // A non-quote was read first so treat any quotes as normal characters
  1056. bParseQuotes = FALSE;
  1057. }
  1058. ++lpstrEnd;
  1059. }
  1060. if (bInQuotes)
  1061. return MCIERR_NO_CLOSING_QUOTE;
  1062. // Fail if the token was not found and bMustFind is TRUE
  1063. if (*lpstrEnd != cSeparater && bMustFind)
  1064. return 0;
  1065. // Length of new string (INCLUDES QUOTES NOT COPIED)
  1066. wLen = lpstrEnd - *lplpstrInput + 1;
  1067. if ((*lplpstrOutput = mciAlloc (wLen)) == NULL)
  1068. return MCIERR_OUT_OF_MEMORY;
  1069. // Copy into allocated space
  1070. lpstrCounter = *lplpstrInput;
  1071. lpstrOutput = *lplpstrOutput;
  1072. bInQuotes = FALSE;
  1073. while (lpstrCounter != lpstrEnd)
  1074. {
  1075. if (*lpstrCounter == '"' && bParseQuotes)
  1076. {
  1077. if (bInQuotes)
  1078. {
  1079. // If this is a doubled quote
  1080. if (*(lpstrCounter + 1) == '"')
  1081. // Copy it
  1082. *lpstrOutput++ = *lpstrCounter++;
  1083. else
  1084. bInQuotes = FALSE;
  1085. } else
  1086. bInQuotes = TRUE;
  1087. // Skip the quote
  1088. ++lpstrCounter;
  1089. } else
  1090. *lpstrOutput++ = *lpstrCounter++;
  1091. }
  1092. *lpstrOutput = '\0';
  1093. if (*lpstrEnd == '\0')
  1094. *lplpstrInput = lpstrEnd;
  1095. else
  1096. *lplpstrInput = lpstrEnd + 1;
  1097. return 0;
  1098. }
  1099. // Take the type number from the open parameters and return
  1100. // it as a string in lplpstrType which must be free'd with mciFree
  1101. // Returns 0 or an MCI error code
  1102. UINT PASCAL NEAR mciExtractTypeFromID (
  1103. LPMCI_OPEN_PARMS lpOpen)
  1104. {
  1105. int nSize;
  1106. LPSTR lpstrType;
  1107. if ((lpstrType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH)) == NULL)
  1108. return MCIERR_OUT_OF_MEMORY;
  1109. // Load the type string corresponding to the ID
  1110. if ((nSize = LoadString (ghInst,
  1111. LOWORD ((DWORD)lpOpen->lpstrDeviceType),
  1112. lpstrType, MCI_MAX_DEVICE_TYPE_LENGTH)) == 0)
  1113. return MCIERR_EXTENSION_NOT_FOUND;
  1114. // Add ordinal (if any) onto the end of the device type name
  1115. if (HIWORD (lpOpen->lpstrDeviceType) != 0)
  1116. {
  1117. if (nSize > MCI_MAX_DEVICE_TYPE_LENGTH - 11)
  1118. {
  1119. DOUT ("mciExtractTypeFromID: type + ordinal too long\r\n");
  1120. return MCIERR_DEVICE_ORD_LENGTH;
  1121. }
  1122. wsprintf (lpstrType + nSize, szUnsignedFormat,
  1123. HIWORD ((DWORD)lpOpen->lpstrDeviceType));
  1124. }
  1125. lpOpen->lpstrDeviceType = lpstrType;
  1126. return 0;
  1127. }
  1128. /*
  1129. * @doc INTERNAL MCI
  1130. * @func UINT | mciOpenDevice | Open an MCI device for access.
  1131. * Used in processing the MCI_OPEN message.
  1132. *
  1133. * @parm DWORD | dwFlags | Open Flags
  1134. * @parm LPMCI_OPEN_PARMS | lpOpen | Description of device
  1135. *
  1136. * @rdesc 0 if successful or an error code
  1137. * @flag MCIERR_INVALID_DEVICE_NAME | Name not known
  1138. * @flag MCIERR_DEVICE_OPEN | Device is already open and is not sharable
  1139. *
  1140. * @comm This function does the following:
  1141. * 1) Check to see if device is already open. If so, return an error
  1142. *
  1143. * 2) Locate the device name in the SYSTEM.INI file and load
  1144. * the corresponding device driver DLL
  1145. *
  1146. * 3) Allocate and initialize a new device description block
  1147. *
  1148. */
  1149. UINT NEAR PASCAL mciOpenDevice (
  1150. DWORD dwStartingFlags,
  1151. LPMCI_OPEN_PARMS lpOpen,
  1152. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
  1153. {
  1154. LPSTR lpstrNewType = NULL;
  1155. UINT wID, wReturn;
  1156. LPCSTR lpstrDeviceName;
  1157. LPSTR lpstrNewElement = NULL;
  1158. BOOL bFromTypeID = FALSE;
  1159. LPCSTR lpstrOriginalType;
  1160. LPCSTR lpstrOriginalElement;
  1161. LPCSTR lpstrOriginalAlias;
  1162. DWORD dwFlags = dwStartingFlags;
  1163. BOOL bDefaultAlias = FALSE;
  1164. // Initialize
  1165. if (lpOpen == NULL)
  1166. return MCIERR_NULL_PARAMETER_BLOCK;
  1167. lpstrOriginalType = lpOpen->lpstrDeviceType;
  1168. lpstrOriginalElement = lpOpen->lpstrElementName;
  1169. lpstrOriginalAlias = lpOpen->lpstrAlias;
  1170. // The type number is given explicitly, convert it to a type name
  1171. if (dwFlags & MCI_OPEN_TYPE_ID)
  1172. if ((wReturn = mciExtractTypeFromID (lpOpen)) != 0)
  1173. return wReturn;
  1174. else
  1175. bFromTypeID = TRUE;
  1176. // The device name is the device type of a simple device or the device
  1177. // element of a compound device
  1178. if (dwFlags & MCI_OPEN_ELEMENT)
  1179. lpstrDeviceName = lpstrOriginalElement;
  1180. else if (dwFlags & MCI_OPEN_TYPE)
  1181. lpstrDeviceName = lpOpen->lpstrDeviceType;
  1182. else
  1183. return MCIERR_MISSING_PARAMETER;
  1184. if (lpstrDeviceName == NULL)
  1185. {
  1186. DOUT ("mciOpenDevice: Device name is NULL\r\n");
  1187. return MCIERR_INVALID_DEVICE_NAME;
  1188. }
  1189. // Is the device already open?
  1190. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  1191. wID = mciGetDeviceIDFromElementID ((DWORD)lpstrDeviceName,
  1192. lpOpen->lpstrDeviceType);
  1193. else
  1194. wID = mciGetDeviceIDInternalEx ((dwFlags & MCI_OPEN_ALIAS ?
  1195. lpOpen->lpstrAlias : lpstrDeviceName),
  1196. lpOpenInfo->hCallingTask);
  1197. // If the device is open already then return an error
  1198. if (wID != 0)
  1199. return dwFlags & MCI_OPEN_ALIAS ? MCIERR_DUPLICATE_ALIAS :
  1200. MCIERR_DEVICE_OPEN;
  1201. // The device is not already open in that task by the name
  1202. // If the type was derived then skip all this crap
  1203. if (bFromTypeID)
  1204. goto load_device;
  1205. // If an element name is given but no type name (only via mciSendCommand)
  1206. if (dwFlags & MCI_OPEN_ELEMENT && !(dwFlags & MCI_OPEN_TYPE))
  1207. {
  1208. lpstrNewType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH);
  1209. if (lpstrNewType == NULL)
  1210. return MCIERR_OUT_OF_MEMORY;
  1211. // Try to get the device type from the element name via a file extension
  1212. if (mciExtractDeviceType (lpstrOriginalElement,
  1213. lpstrNewType, MCI_MAX_DEVICE_TYPE_LENGTH))
  1214. {
  1215. lpOpen->lpstrDeviceType = lpstrNewType;
  1216. dwFlags |= MCI_OPEN_TYPE;
  1217. } else
  1218. {
  1219. mciFree (lpstrNewType);
  1220. return MCIERR_EXTENSION_NOT_FOUND;
  1221. }
  1222. } else if (dwFlags & MCI_OPEN_TYPE && !(dwFlags & MCI_OPEN_ELEMENT))
  1223. // A type name is given but no element
  1224. {
  1225. // Try to extract a device type from the given device name via a file extension
  1226. lpstrNewType = mciAlloc (MCI_MAX_DEVICE_TYPE_LENGTH);
  1227. if (lpstrNewType == NULL)
  1228. return MCIERR_OUT_OF_MEMORY;
  1229. if (mciExtractDeviceType (lpOpen->lpstrDeviceType, lpstrNewType,
  1230. MCI_MAX_DEVICE_TYPE_LENGTH))
  1231. {
  1232. // Fix up the type and element names
  1233. dwFlags |= MCI_OPEN_ELEMENT;
  1234. lpOpen->lpstrElementName = lpOpen->lpstrDeviceType;
  1235. lpOpen->lpstrDeviceType = lpstrNewType;
  1236. } else
  1237. // Failed to extract type so...
  1238. // Try to get a compound element name ('!' separator)
  1239. {
  1240. LPCSTR lpstrTemp = lpOpen->lpstrDeviceType;
  1241. mciFree (lpstrNewType);
  1242. lpstrNewType = NULL;
  1243. if ((wReturn = mciEatToken (&lpstrTemp, '!', &lpstrNewType, TRUE))
  1244. != 0)
  1245. goto cleanup;
  1246. else if (lpstrNewType != NULL)
  1247. {
  1248. if ((wReturn = mciEatToken (&lpstrTemp, '\0',
  1249. &lpstrNewElement, TRUE))
  1250. != 0)
  1251. goto cleanup;
  1252. else if (lpstrNewElement != NULL &&
  1253. *lpstrNewElement != '\0')
  1254. {
  1255. // See if this element name is in use
  1256. if (!(dwFlags & MCI_OPEN_ALIAS))
  1257. if (mciGetDeviceIDInternalEx (lpstrNewElement,
  1258. lpOpenInfo->hCallingTask))
  1259. {
  1260. wReturn = MCIERR_DEVICE_OPEN;
  1261. goto cleanup;
  1262. }
  1263. // Swap type and element for new ones
  1264. lpOpen->lpstrElementName = lpstrNewElement;
  1265. lpOpen->lpstrDeviceType = lpstrNewType;
  1266. dwFlags |= MCI_OPEN_ELEMENT;
  1267. }
  1268. }
  1269. }
  1270. } else
  1271. lpstrNewType = NULL;
  1272. // Tack on a default alias if none is given
  1273. if (! (dwFlags & MCI_OPEN_ALIAS))
  1274. {
  1275. LPCSTR lpstrAlias;
  1276. // If an element name exists then the alias is the element name
  1277. if (dwFlags & MCI_OPEN_ELEMENT)
  1278. {
  1279. // If a device ID was specified then there is no alias
  1280. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  1281. lpstrAlias = NULL;
  1282. else
  1283. lpstrAlias = lpOpen->lpstrElementName;
  1284. // Otherwise the alias is the device type
  1285. } else
  1286. lpstrAlias = lpOpen->lpstrDeviceType;
  1287. if (lpstrAlias != NULL)
  1288. {
  1289. lpOpen->lpstrAlias = lpstrAlias;
  1290. dwFlags |= MCI_OPEN_ALIAS;
  1291. bDefaultAlias = TRUE;
  1292. }
  1293. }
  1294. load_device:;
  1295. wReturn = mciLoadDevice (dwFlags, lpOpen, lpOpenInfo, bDefaultAlias);
  1296. cleanup:
  1297. if (lpstrNewElement != NULL)
  1298. mciFree (lpstrNewElement);
  1299. if (lpstrNewType != NULL)
  1300. mciFree (lpstrNewType);
  1301. if (bFromTypeID)
  1302. mciFree ((LPSTR)lpOpen->lpstrDeviceType);
  1303. // Replace original items
  1304. lpOpen->lpstrDeviceType = lpstrOriginalType;
  1305. lpOpen->lpstrElementName = lpstrOriginalElement;
  1306. lpOpen->lpstrAlias = lpstrOriginalAlias;
  1307. return wReturn;
  1308. }
  1309. STATICFN void PASCAL NEAR
  1310. mciFreeDevice(
  1311. LPMCI_DEVICE_NODE nodeWorking
  1312. )
  1313. {
  1314. UINT wID = nodeWorking->wDeviceID;
  1315. mciMessage(THUNK_MCI_FREE_NODE, (DWORD) nodeWorking->wDeviceID, 0L, 0L, 0L);
  1316. if (nodeWorking->lpstrName != NULL)
  1317. mciFree (nodeWorking->lpstrName);
  1318. if (nodeWorking->lpstrInstallName != NULL)
  1319. mciFree (nodeWorking->lpstrInstallName);
  1320. mciFree(MCI_lpDeviceList[wID]);
  1321. MCI_lpDeviceList[wID] = NULL;
  1322. /* If this was the last device in the list, decrement next ID value */
  1323. if (wID + 1 == MCI_wNextDeviceID) {
  1324. --MCI_wNextDeviceID;
  1325. }
  1326. }
  1327. /*
  1328. * @doc INTERNAL MCI
  1329. * @func UINT | mciCloseDevice | Close an MCI device. Used in
  1330. * processing the MCI_CLOSE message.
  1331. *
  1332. * @parm UINT | wID | The ID of the device to close
  1333. * @parm DWORD | dwFlags | Close Flags
  1334. * @parm LPMCI_GENERIC_PARMS | lpClose | Generic parameters
  1335. * @parm BOOL | bCloseDriver | TRUE if the CLOSE command should be sent
  1336. * on to the driver.
  1337. *
  1338. * @rdesc 0 if successful or an error code
  1339. *
  1340. * @comm This function sends an MCI_CLOSE_DRIVER message to the corresponding
  1341. * driver if the use count is zero and then unloads the driver DLL
  1342. *
  1343. */
  1344. UINT NEAR PASCAL mciCloseDevice (
  1345. UINT wID,
  1346. DWORD dwFlags,
  1347. LPMCI_GENERIC_PARMS lpGeneric,
  1348. BOOL bCloseDriver)
  1349. {
  1350. LPMCI_DEVICE_NODE nodeWorking;
  1351. UINT wErr, wTable;
  1352. nodeWorking = MCI_lpDeviceList[wID];
  1353. if (nodeWorking == NULL)
  1354. {
  1355. DOUT ("mciCloseDevice: NULL node from device ID--error if not auto-close\r\n");
  1356. return 0;
  1357. }
  1358. // If a close is in progress (usually this message comes from a Yield
  1359. // after a mciDriverNotify actuated by the active close) then exit
  1360. if (nodeWorking->dwMCIFlags & MCINODE_ISCLOSING)
  1361. return 0;
  1362. nodeWorking->dwMCIFlags |= MCINODE_ISCLOSING;
  1363. if (bCloseDriver)
  1364. {
  1365. MCI_GENERIC_PARMS GenericParms;
  1366. // Make fake generic params if close came internally
  1367. if (lpGeneric == NULL)
  1368. lpGeneric = &GenericParms;
  1369. wErr = LOWORD(mciSendCommand (wID, MCI_CLOSE_DRIVER, dwFlags,
  1370. (DWORD)lpGeneric));
  1371. }
  1372. else
  1373. wErr = 0;
  1374. // Must zero this to allow the table to be freed by the driver
  1375. nodeWorking->wCustomCommandTable = 0;
  1376. wTable = nodeWorking->wCommandTable;
  1377. // Must zero this to allow the table to be freed
  1378. nodeWorking->wCommandTable = 0;
  1379. mciFreeCommandResource (wTable);
  1380. CloseDriver (nodeWorking->hDrvDriver, 0L, 0L);
  1381. mciFreeDevice (nodeWorking);
  1382. return wErr;
  1383. }
  1384. /*
  1385. * @doc INTERNAL MCI DDK
  1386. * @api DWORD | mciGetDriverData | Returns a pointer to the instance
  1387. * data associated with an MCI device
  1388. *
  1389. * @parm UINT | wDeviceID | The MCI device ID
  1390. *
  1391. * @rdesc The driver instance data. On error, returns 0 but since
  1392. * the driver data might be zero, this cannot be verified by the caller
  1393. * unless the instance data is known to be non-zero (e.g. a pointer)
  1394. *
  1395. */
  1396. DWORD WINAPI mciGetDriverData (
  1397. UINT wDeviceID)
  1398. {
  1399. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  1400. {
  1401. DOUT ("mciGetDriverData: invalid device ID\r\n");
  1402. return 0;
  1403. }
  1404. return MCI_lpDeviceList[wDeviceID]->lpDriverData;
  1405. }
  1406. /*
  1407. * @doc INTERNAL MCI DDK
  1408. * @func BOOL | mciSetDriverData | Sets the instance
  1409. * data associated with an MCI device
  1410. *
  1411. * @parm UINT | wDeviceID | The MCI device ID
  1412. *
  1413. * @parm DWORD | dwData | Driver data to set
  1414. *
  1415. * @rdesc FALSE if the device ID is not known or there is insufficient
  1416. * memory to load the device description, else TRUE.
  1417. *
  1418. */
  1419. BOOL WINAPI mciSetDriverData (
  1420. UINT wDeviceID,
  1421. DWORD dwData)
  1422. {
  1423. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  1424. {
  1425. DOUT ("mciSetDriverData: invalid device ID\r\n");
  1426. return FALSE;
  1427. }
  1428. MCI_lpDeviceList[wDeviceID]->lpDriverData = dwData;
  1429. return TRUE;
  1430. }
  1431. /*
  1432. * @doc INTERNAL MCI DDK
  1433. * @api UINT | mciDriverYield | Used in a driver's idle loop
  1434. * to yield to Windows
  1435. *
  1436. * @parm UINT | wDeviceID | Device ID that is yielding.
  1437. *
  1438. * @rdesc Non-zero if the driver should abort the operation.
  1439. *
  1440. */
  1441. UINT WINAPI mciDriverYield (
  1442. UINT wDeviceID)
  1443. {
  1444. if (MCI_VALID_DEVICE_ID(wDeviceID))
  1445. {
  1446. LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
  1447. if (node->fpYieldProc != NULL)
  1448. return (node->fpYieldProc)(wDeviceID, node->dwYieldData);
  1449. }
  1450. Yield();
  1451. return 0;
  1452. }
  1453. /*
  1454. * @doc EXTERNAL MCI
  1455. * @api BOOL | mciSetYieldProc | This function sets the address
  1456. * of a callback procedure to be called periodically when an MCI device
  1457. * is completing a command specified with the WAIT flag.
  1458. *
  1459. * @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
  1460. * which the yield procedure is to be assigned.
  1461. *
  1462. * @parm YIELDPROC | fpYieldProc | Specifies the callback procedure
  1463. * to be called when the given device is yielding. Specify a NULL value
  1464. * to disable any existing yield procedure.
  1465. *
  1466. * @parm DWORD | dwYieldData | Specifies the data sent to the yield procedure
  1467. * when it is called for the given device.
  1468. *
  1469. * @rdesc Returns TRUE if successful. Returns FALSE for an invalid device ID.
  1470. *
  1471. * @cb int CALLBACK | YieldProc | <f YieldProc> is a placeholder for
  1472. * the application-supplied function name. Export the actual name
  1473. * by including it in the EXPORTS statement in your module-definition
  1474. * file.
  1475. *
  1476. * @parm UINT | wDeviceID | Specifies the device ID of the MCI device.
  1477. *
  1478. * @parm DWORD | dwData | Specifies the application-supplied yield data
  1479. * originally supplied in the <p dwYieldData> parameter.
  1480. *
  1481. * @rdesc Return zero to continue the operation. To cancel the operation,
  1482. * return a nonzero value.
  1483. *
  1484. * @comm This call overrides any previous yield procedure for this device.
  1485. *
  1486. */
  1487. BOOL WINAPI mciSetYieldProc (
  1488. UINT wDeviceID,
  1489. YIELDPROC fpYieldProc,
  1490. DWORD dwYieldData)
  1491. {
  1492. V_CALLBACK((FARPROC)fpYieldProc, FALSE);
  1493. if (Is16bitDrv(wDeviceID)) {
  1494. LPMCI_DEVICE_NODE node = MCI_lpDeviceList[wDeviceID];
  1495. node->fpYieldProc = fpYieldProc;
  1496. node->dwYieldData = dwYieldData;
  1497. return TRUE;
  1498. }
  1499. return (BOOL)mciMessage( THUNK_MCI_SETYIELDPROC, (DWORD)wDeviceID,
  1500. (DWORD)fpYieldProc, dwYieldData, 0L );
  1501. }
  1502. /*
  1503. * @doc EXTERNAL MCI
  1504. * @api YIELDPROC | mciGetYieldProc | This function gets the address
  1505. * of the callback procedure to be called periodically when an MCI device
  1506. * is completing a command specified with the WAIT flag.
  1507. *
  1508. * @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
  1509. * which the yield procedure is to be retrieved from.
  1510. *
  1511. * @parm LPDWORD | lpdwYieldData | Optionally specifies a buffer to place
  1512. * the yield data passed to the function in. If the parameter is NULL, it
  1513. * is ignored.
  1514. *
  1515. * @rdesc Returns the current yield proc if any, else returns NULL for an
  1516. * invalid device ID.
  1517. *
  1518. */
  1519. YIELDPROC WINAPI mciGetYieldProc (
  1520. UINT wDeviceID,
  1521. LPDWORD lpdwYieldData)
  1522. {
  1523. /*
  1524. ** Is this a 16 bit device ID ?
  1525. */
  1526. if (Is16bitDrv(wDeviceID)) {
  1527. if (lpdwYieldData != NULL) {
  1528. V_WPOINTER(lpdwYieldData, sizeof(DWORD), NULL);
  1529. *lpdwYieldData = MCI_lpDeviceList[wDeviceID]->dwYieldData;
  1530. }
  1531. return MCI_lpDeviceList[wDeviceID]->fpYieldProc;
  1532. }
  1533. /*
  1534. ** No, so pass it on to the 32 bit code.
  1535. */
  1536. return (YIELDPROC)mciMessage( THUNK_MCI_GETYIELDPROC, (DWORD)wDeviceID,
  1537. (DWORD)lpdwYieldData, 0L, 0L );
  1538. }
  1539. /*
  1540. * @doc INTERNAL MCI
  1541. * @api int | mciBreakKeyYieldProc | Procedure called to check a
  1542. * key state for the given device
  1543. *
  1544. * @parm UINT | wDeviceID | Device ID which is yielding
  1545. *
  1546. * @parm DWORD | dwYieldData | Data for this device's yield proc
  1547. *
  1548. * @rdesc Non-zero if the driver should abort the operation. Currently
  1549. * always returns 0.
  1550. *
  1551. */
  1552. int CALLBACK mciBreakKeyYieldProc (
  1553. UINT wDeviceID,
  1554. DWORD dwYieldData)
  1555. {
  1556. HWND hwndCheck;
  1557. int nState;
  1558. hwndCheck = (HWND)HIWORD (dwYieldData);
  1559. if (hwndCheck == NULL || hwndCheck == GetActiveWindow())
  1560. {
  1561. nState = GetAsyncKeyState (LOWORD(dwYieldData));
  1562. // Break if key is down or has been down
  1563. if (nState & 1)
  1564. {
  1565. MSG msg;
  1566. while (PeekMessage (&msg, hwndCheck, WM_KEYFIRST, WM_KEYLAST,
  1567. PM_REMOVE));
  1568. return -1;
  1569. }
  1570. }
  1571. Yield();
  1572. return 0;
  1573. }
  1574. /*
  1575. * @doc INTERNAL MCI
  1576. * @func UINT | mciSetBreakKey | Set a key which will break a wait loop
  1577. * for a given driver
  1578. *
  1579. * @parm UINT | wDeviceID | The device ID to assign a break key to
  1580. *
  1581. * @parm int | nVirtKey | Virtual key code to trap
  1582. *
  1583. * @parm HWND | hwndTrap | The handle to a window that must be active
  1584. * for the key to be trapped. If NULL then all windows will be checked
  1585. *
  1586. * @rdesc TRUE if successful, FALSE if invalid device ID
  1587. *
  1588. */
  1589. UINT PASCAL NEAR mciSetBreakKey (
  1590. UINT wDeviceID,
  1591. int nVirtKey,
  1592. HWND hwndTrap)
  1593. {
  1594. return mciSetYieldProc (wDeviceID, mciBreakKeyYieldProc,
  1595. MAKELONG (nVirtKey, hwndTrap));
  1596. }