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.

2232 lines
65 KiB

  1. /******************************************************************************
  2. * Module Name: mci.c
  3. *
  4. * Media Control Architecture Driver Interface
  5. *
  6. * Contents: MCI external message API's mciSendString and mciSendCommand
  7. * Author: DLL (DavidLe)
  8. * Created: 2/13/90
  9. *
  10. * Copyright (c) 1990 Microsoft Corporation
  11. *
  12. \******************************************************************************/
  13. #ifdef DEBUG
  14. #ifndef DEBUG_RETAIL
  15. #define DEBUG_RETAIL
  16. #endif
  17. #endif
  18. #include <windows.h>
  19. #include <string.h>
  20. #define MMNOSEQ
  21. #define MMNOJOY
  22. #define MMNOWAVE
  23. #define MMNOMIDI
  24. #include "mmsystem.h"
  25. #define NOMIDIDEV
  26. #define NOWAVEDEV
  27. #define NOTIMERDEV
  28. #define NOJOYDEV
  29. #define NOSEQDEV
  30. #define NOTASKDEV
  31. #include "mmddk.h"
  32. #include "mmsysi.h"
  33. #include "thunks.h"
  34. #ifndef STATICFN
  35. #define STATICFN
  36. #endif
  37. /* -------------------------------------------------------------------------
  38. ** Thunking stuff
  39. ** -------------------------------------------------------------------------
  40. */
  41. LPMCIMESSAGE PASCAL mci32Message;
  42. DWORD WINAPI mciSendCommand16(
  43. UINT wDeviceID,
  44. UINT wMessage,
  45. DWORD dwParam1,
  46. DWORD dwParam2
  47. );
  48. //
  49. // Define the init code for this file.
  50. //
  51. #pragma alloc_text( INIT, MCITerminate )
  52. #ifdef DEBUG_RETAIL
  53. int DebugmciSendCommand;
  54. #endif
  55. #ifdef DEBUG
  56. void PASCAL NEAR mciCheckLocks(void);
  57. #endif
  58. STATICFN UINT PASCAL NEAR
  59. mciConvertReturnValue(
  60. UINT wType,
  61. UINT wErr,
  62. UINT wDeviceID,
  63. LPDWORD dwParams,
  64. LPSTR lpstrReturnString,
  65. UINT wReturnLength
  66. );
  67. STATICFN DWORD NEAR PASCAL
  68. mciSendStringInternal(
  69. LPCSTR lpstrCommand,
  70. LPSTR lpstrReturnString,
  71. UINT wReturnLength,
  72. HWND hwndCallback,
  73. LPMCI_SYSTEM_MESSAGE lpMessage
  74. );
  75. STATICFN DWORD NEAR PASCAL
  76. mciSendSystemString(
  77. LPCSTR lpstrCommand,
  78. LPSTR lpstrReturnString,
  79. UINT wReturnLength
  80. );
  81. extern int FAR PASCAL
  82. mciBreakKeyYieldProc(
  83. UINT wDeviceID,
  84. DWORD dwYieldData
  85. );
  86. // From dosa.asm
  87. extern int FAR PASCAL DosChangeDir(LPCSTR lpszPath);
  88. extern WORD FAR PASCAL DosGetCurrentDrive(void);
  89. extern BOOL FAR PASCAL DosSetCurrentDrive(WORD wDrive);
  90. extern WORD FAR PASCAL DosGetCurrentDir(WORD wCurdrive, LPSTR lpszBuf);
  91. #define MAX_PATHNAME 144
  92. // This macro defines the list of messages for which mciSendString
  93. // will not try to auto-open
  94. #define MCI_CANNOT_AUTO_OPEN(wMessage) \
  95. (wMessage == MCI_OPEN || wMessage == MCI_SYSINFO \
  96. || wMessage == MCI_SOUND || wMessage == MCI_CLOSE \
  97. || wMessage == MCI_BREAK)
  98. // This macro devices the list of message which do not require an open
  99. // device. It is a subset of MCI_CANNOT_AUTO_OPEN
  100. #define MCI_DO_NOT_NEED_OPEN(wMessage) \
  101. (wMessage == MCI_OPEN || wMessage == MCI_SOUND || wMessage == MCI_SYSINFO)
  102. // Strings used in mciAutoOpenDevice
  103. SZCODE szOpen[] = "open";
  104. static SZCODE szClose[] = "close";
  105. static SZCODE szNotify[] = "notify";
  106. static SZCODE szWait[] = "wait";
  107. static SZCODE szCmdFormat[] = "%ls %ls";
  108. static SZCODE szLongFormat[] = "%ld";
  109. static SZCODE szRectFormat[] = "%d %d %d %d";
  110. extern char far szSystemDefault[];
  111. // Special device name
  112. static SZCODE szNew[] = "new";
  113. /******************************Public*Routine******************************\
  114. * mciAppExit
  115. *
  116. * Notify the 32 bit code that a 16 bit app has died.
  117. *
  118. * History:
  119. * dd-mm-94 - StephenE - Created
  120. *
  121. \**************************************************************************/
  122. DWORD
  123. mciAppExit(
  124. HTASK hTask
  125. )
  126. {
  127. return mciMessage( THUNK_APP_EXIT, (DWORD)hTask,
  128. 0L, 0L, 0L );
  129. }
  130. /*****************************************************************************
  131. * @doc INTERNAL
  132. *
  133. * @api void | MciNotify | called by mmWndProc when it recives a
  134. * MM_MCINOTIFY message
  135. * @rdesc None.
  136. *
  137. ****************************************************************************/
  138. void FAR PASCAL
  139. MciNotify(
  140. WPARAM wParam,
  141. LPARAM lParam
  142. )
  143. {
  144. //
  145. // wParam is the notify status
  146. // lParam is the MCI device id
  147. //
  148. if (MCI_VALID_DEVICE_ID(LOWORD(lParam)) &&
  149. !(MCI_lpDeviceList[LOWORD(lParam)]->dwMCIFlags & MCINODE_ISCLOSING)) {
  150. MCI_lpDeviceList[LOWORD(lParam)]->dwMCIFlags |= MCINODE_ISAUTOCLOSING;
  151. mciCloseDevice (LOWORD(lParam), 0L, NULL, TRUE);
  152. }
  153. }
  154. STATICFN void NEAR PASCAL
  155. HandleNotify(
  156. UINT wErr,
  157. UINT wDeviceID,
  158. DWORD dwFlags,
  159. DWORD dwParam2
  160. )
  161. {
  162. LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)dwParam2;
  163. HWND hwndCallback;
  164. if (wErr == 0 && dwFlags & MCI_NOTIFY && lpGeneric != NULL &&
  165. (hwndCallback = (HWND)(UINT)lpGeneric->dwCallback) != NULL)
  166. mciDriverNotify (hwndCallback, wDeviceID, MCI_NOTIFY_SUCCESSFUL);
  167. }
  168. #ifdef DEBUG_RETAIL
  169. //
  170. // Dump the string form of an MCI command
  171. //
  172. UINT PASCAL NEAR
  173. mciDebugOut(
  174. UINT wDeviceID,
  175. UINT wMessage,
  176. DWORD dwFlags,
  177. DWORD dwParam2,
  178. LPMCI_DEVICE_NODE nodeWorking
  179. )
  180. {
  181. LPSTR lpCommand, lpFirstParameter, lpPrevious, lszDebugOut;
  182. char strTemp[256];
  183. UINT wID, wOffset, wOffsetFirstParameter, wReturnType;
  184. DWORD dwValue;
  185. DWORD dwMask;
  186. UINT wTable;
  187. // Find the command table for the given command message ID
  188. lpCommand = FindCommandItem( wDeviceID, NULL,
  189. (LPSTR)MAKELONG (wMessage, 0),
  190. NULL, &wTable);
  191. if (lpCommand == NULL)
  192. {
  193. if (wMessage != MCI_OPEN_DRIVER && wMessage != MCI_CLOSE_DRIVER)
  194. ROUT ("MMSYSTEM: mciDebugOut: Command table not found");
  195. return 0;
  196. }
  197. lszDebugOut = mciAlloc(512);
  198. if (!lszDebugOut) {
  199. ROUT("MMSYSTEM: Not enough memory to display command");
  200. return 0;
  201. }
  202. // Dump the command name
  203. wsprintf(lszDebugOut, "MMSYSTEM: MCI command: \"%ls", lpCommand);
  204. // Dump the device name
  205. if (wDeviceID == MCI_ALL_DEVICE_ID)
  206. {
  207. lstrcat(lszDebugOut, " all");
  208. }
  209. else if (nodeWorking != NULL)
  210. {
  211. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID)
  212. {
  213. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " Element ID:0x%lx", nodeWorking->dwElementID);
  214. } else if (nodeWorking->lpstrName != NULL)
  215. {
  216. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", nodeWorking->lpstrName);
  217. }
  218. }
  219. // Skip past command entry
  220. lpCommand += mciEatCommandEntry (lpCommand, NULL, NULL);
  221. // Get the next entry
  222. lpFirstParameter = lpCommand;
  223. // Skip past the DWORD return value
  224. wOffsetFirstParameter = 4;
  225. lpCommand += mciEatCommandEntry (lpCommand, &dwValue, &wID);
  226. // If it is a return value, skip it
  227. if (wID == MCI_RETURN)
  228. {
  229. wReturnType = (UINT)dwValue;
  230. lpFirstParameter = lpCommand;
  231. wOffsetFirstParameter += mciGetParamSize (dwValue, wID);
  232. lpCommand += mciEatCommandEntry (lpCommand, &dwValue, &wID);
  233. }
  234. else {
  235. wReturnType = (UINT)0;
  236. }
  237. // Dump device name parameter to OPEN
  238. if (wMessage == MCI_OPEN)
  239. {
  240. LPCSTR lpstrDeviceType =
  241. ((LPMCI_OPEN_PARMS)dwParam2)->lpstrDeviceType;
  242. LPCSTR lpstrElementName =
  243. ((LPMCI_OPEN_PARMS)dwParam2)->lpstrElementName;
  244. // Tack on device type
  245. if (dwFlags & MCI_OPEN_TYPE_ID)
  246. {
  247. LPMCI_OPEN_PARMS lpOpen = (LPMCI_OPEN_PARMS)dwParam2;
  248. DWORD dwOld = (DWORD)lpOpen->lpstrDeviceType;
  249. if (mciExtractTypeFromID ((LPMCI_OPEN_PARMS)dwParam2) != 0)
  250. strTemp[0] = '\0';
  251. lstrcpy (strTemp, lpOpen->lpstrDeviceType);
  252. mciFree ((LPSTR)lpOpen->lpstrDeviceType);
  253. lpOpen->lpstrDeviceType = (LPSTR)dwOld;
  254. } else if (lpstrDeviceType != NULL)
  255. lstrcpy (strTemp, lpstrDeviceType);
  256. else
  257. strTemp[0] = '\0';
  258. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  259. {
  260. // Tack on element ID
  261. lstrcat (strTemp, " Element ID:");
  262. wsprintf (strTemp + lstrlen (strTemp), szLongFormat,
  263. LOWORD ((DWORD)lpstrDeviceType));
  264. } else
  265. {
  266. // Add separator if both type name and element name are present
  267. if (lpstrDeviceType != 0 && lpstrElementName != 0)
  268. lstrcat (strTemp, "!");
  269. if (lpstrElementName != 0 && dwFlags & MCI_OPEN_ELEMENT)
  270. lstrcat (strTemp, lpstrElementName);
  271. }
  272. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", (LPSTR)strTemp);
  273. }
  274. // Walk through each flag
  275. for (dwMask = 1; dwMask;)
  276. {
  277. // Is this bit set?
  278. if ((dwFlags & dwMask) != 0 && !
  279. // The MCI_OPEN_TYPE and MCI_OPEN_ELEMENT flags are taken care of
  280. // above
  281. (wMessage == MCI_OPEN && (dwMask == MCI_OPEN_TYPE
  282. || dwMask == MCI_OPEN_ELEMENT)))
  283. {
  284. lpPrevious = lpCommand = lpFirstParameter;
  285. wOffset = 0;
  286. lpCommand += mciEatCommandEntry (lpCommand, &dwValue, &wID);
  287. // What parameter uses this bit?
  288. while (wID != MCI_END_COMMAND && dwValue != dwMask)
  289. {
  290. wOffset += mciGetParamSize (dwValue, wID);
  291. if (wID == MCI_CONSTANT)
  292. while (wID != MCI_END_CONSTANT)
  293. lpCommand += mciEatCommandEntry (lpCommand,
  294. NULL, &wID);
  295. lpPrevious = lpCommand;
  296. lpCommand += mciEatCommandEntry (lpCommand, &dwValue, &wID);
  297. }
  298. if (wID != MCI_END_COMMAND)
  299. {
  300. // Found the parameter which matches this flag bit
  301. // Print the parameter name
  302. if (*lpPrevious)
  303. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", lpPrevious);
  304. // Print any argument
  305. switch (wID)
  306. {
  307. case MCI_STRING:
  308. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", *(LPSTR FAR *)((LPSTR)dwParam2 + wOffset + wOffsetFirstParameter));
  309. break;
  310. case MCI_CONSTANT:
  311. {
  312. DWORD dwConst = *(LPDWORD)((LPSTR)dwParam2 + wOffset +
  313. wOffsetFirstParameter);
  314. UINT wLen;
  315. BOOL bFound;
  316. for (bFound = FALSE; wID != MCI_END_CONSTANT;)
  317. {
  318. wLen = mciEatCommandEntry (lpCommand,
  319. &dwValue, &wID);
  320. if (dwValue == dwConst)
  321. {
  322. bFound = TRUE;
  323. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", lpCommand);
  324. }
  325. lpCommand += wLen;
  326. }
  327. if (bFound)
  328. break;
  329. // FALL THROUGH
  330. }
  331. case MCI_INTEGER:
  332. wsprintf ((LPSTR)strTemp, szLongFormat,
  333. *(LPDWORD)((LPSTR)dwParam2 + wOffset +
  334. wOffsetFirstParameter));
  335. wsprintf(lszDebugOut + lstrlen(lszDebugOut), " %ls", (LPSTR)strTemp);
  336. break;
  337. }
  338. }
  339. }
  340. // Go the the next flag
  341. dwMask <<= 1;
  342. }
  343. mciUnlockCommandTable (wTable);
  344. lstrcat(lszDebugOut, "\"");
  345. ROUTS(lszDebugOut);
  346. mciFree(lszDebugOut);
  347. return wReturnType;
  348. }
  349. #endif
  350. STATICFN DWORD PASCAL NEAR
  351. mciBreak(
  352. UINT wDeviceID,
  353. DWORD dwFlags,
  354. LPMCI_BREAK_PARMS lpBreakon
  355. )
  356. {
  357. HWND hwnd;
  358. if (dwFlags & MCI_BREAK_KEY)
  359. {
  360. if (dwFlags & MCI_BREAK_OFF)
  361. return MCIERR_FLAGS_NOT_COMPATIBLE;
  362. if (dwFlags & MCI_BREAK_HWND)
  363. hwnd = lpBreakon->hwndBreak;
  364. else
  365. hwnd = 0;
  366. return mciSetBreakKey (wDeviceID, lpBreakon->nVirtKey,
  367. hwnd)
  368. ? 0 : MMSYSERR_INVALPARAM;
  369. } else if (dwFlags & MCI_BREAK_OFF) {
  370. mciSetYieldProc(wDeviceID, NULL, 0);
  371. return 0;
  372. } else
  373. return MCIERR_MISSING_PARAMETER;
  374. }
  375. // Close the indicated device by sending a message inter-task
  376. STATICFN DWORD PASCAL NEAR
  377. mciAutoCloseDevice(
  378. LPCSTR lpstrDevice
  379. )
  380. {
  381. LPSTR lpstrCommand;
  382. DWORD dwRet;
  383. if ((lpstrCommand =
  384. mciAlloc (sizeof (szClose) + 1 +
  385. lstrlen (lpstrDevice))) == NULL)
  386. return MCIERR_OUT_OF_MEMORY;
  387. wsprintf(lpstrCommand, szCmdFormat, (LPCSTR)szClose, lpstrDevice);
  388. dwRet = mciSendSystemString (lpstrCommand, NULL, 0);
  389. mciFree (lpstrCommand);
  390. return dwRet;
  391. }
  392. //
  393. // Process a single MCI command
  394. //
  395. // Called by mciSendCommandInternal
  396. //
  397. STATICFN DWORD PASCAL NEAR
  398. mciSendSingleCommand(
  399. UINT wDeviceID,
  400. UINT wMessage,
  401. DWORD dwParam1,
  402. DWORD dwParam2,
  403. LPMCI_DEVICE_NODE nodeWorking,
  404. BOOL bTaskSwitch,
  405. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo
  406. )
  407. {
  408. DWORD dwRet;
  409. #ifdef DEBUG_RETAIL
  410. UINT wReturnType;
  411. DWORD dwTime;
  412. #endif
  413. #ifdef DEBUG_RETAIL
  414. if (DebugmciSendCommand)
  415. wReturnType =
  416. mciDebugOut (wDeviceID, wMessage, dwParam1, dwParam2,
  417. nodeWorking);
  418. #endif
  419. switch (wMessage)
  420. {
  421. case MCI_OPEN:
  422. dwRet = mciOpenDevice (dwParam1,
  423. (LPMCI_OPEN_PARMS)dwParam2, lpOpenInfo);
  424. break;
  425. case MCI_CLOSE:
  426. // If this device was auto opened send the command via a task switch
  427. if (bTaskSwitch)
  428. {
  429. if (dwParam1 & MCI_NOTIFY)
  430. return MCIERR_NOTIFY_ON_AUTO_OPEN;
  431. dwRet = mciAutoCloseDevice (nodeWorking->lpstrName);
  432. } else
  433. dwRet =
  434. mciCloseDevice (wDeviceID,
  435. dwParam1,
  436. (LPMCI_GENERIC_PARMS)dwParam2, TRUE);
  437. break;
  438. case MCI_SYSINFO:
  439. dwRet = mciSysinfo (wDeviceID, dwParam1,
  440. (LPMCI_SYSINFO_PARMS)dwParam2);
  441. HandleNotify ((UINT)dwRet, 0, dwParam1, dwParam2);
  442. break;
  443. case MCI_BREAK:
  444. dwRet = mciBreak (wDeviceID, dwParam1,
  445. (LPMCI_BREAK_PARMS)dwParam2);
  446. HandleNotify ((UINT)dwRet, wDeviceID, dwParam1, dwParam2);
  447. break;
  448. case MCI_SOUND:
  449. {
  450. dwRet =
  451. sndPlaySound (MCI_SOUND_NAME & dwParam1 ?
  452. ((LPMCI_SOUND_PARMS)dwParam2)->lpstrSoundName : szSystemDefault,
  453. dwParam1 & MCI_WAIT ?
  454. SND_SYNC : SND_ASYNC)
  455. ? 0 : MCIERR_HARDWARE;
  456. HandleNotify ((UINT)dwRet, wDeviceID, dwParam1, dwParam2);
  457. break;
  458. }
  459. default:
  460. #ifdef DEBUG_RETAIL
  461. if (DebugmciSendCommand)
  462. {
  463. dwTime = timeGetTime();
  464. }
  465. #endif
  466. // Initialize GetAsyncKeyState for break key
  467. if (dwParam1 & MCI_WAIT &&
  468. nodeWorking->fpYieldProc == mciBreakKeyYieldProc)
  469. GetAsyncKeyState (LOWORD(nodeWorking->dwYieldData));
  470. dwRet = (DWORD)SendDriverMessage(nodeWorking->hDrvDriver, wMessage,
  471. (LPARAM)dwParam1, (LPARAM)dwParam2);
  472. #ifdef DEBUG_RETAIL
  473. if (DebugmciSendCommand)
  474. {
  475. dwTime = timeGetTime() - dwTime;
  476. }
  477. #endif
  478. break;
  479. } // switch
  480. #ifdef DEBUG_RETAIL
  481. if (DebugmciSendCommand)
  482. {
  483. if (dwRet & MCI_INTEGER_RETURNED) {
  484. wReturnType = MCI_INTEGER;
  485. }
  486. switch (wReturnType)
  487. {
  488. case MCI_INTEGER:
  489. {
  490. char strTemp[50];
  491. if (wMessage == MCI_OPEN) {
  492. mciConvertReturnValue( wReturnType, HIWORD(dwRet),
  493. wDeviceID, (LPDWORD)dwParam2,
  494. strTemp, sizeof(strTemp));
  495. }
  496. else {
  497. mciConvertReturnValue( wReturnType, HIWORD(dwRet),
  498. wDeviceID, (LPDWORD)dwParam2,
  499. strTemp, sizeof(strTemp));
  500. }
  501. RPRINTF2( "MMSYSTEM: time: %lums returns: \"%ls\"",
  502. dwTime, (LPSTR)strTemp);
  503. break;
  504. }
  505. case MCI_STRING:
  506. RPRINTF2( "MMSYSTEM: time: %lums returns: \"%ls\"",
  507. dwTime, (LPSTR)*(((LPDWORD)dwParam2) + 1));
  508. break;
  509. }
  510. }
  511. #endif
  512. return dwRet;
  513. }
  514. // Internal version of mciSendCommand. Differs ONLY in that the return
  515. // value is a DWORD where the high word has meaning only for mciSendString
  516. STATICFN DWORD NEAR PASCAL
  517. mciSendCommandInternal(
  518. UINT wDeviceID,
  519. UINT wMessage,
  520. DWORD dwParam1,
  521. DWORD dwParam2,
  522. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo
  523. )
  524. {
  525. DWORD dwRetVal;
  526. LPMCI_DEVICE_NODE nodeWorking = NULL;
  527. BOOL bWalkAll;
  528. BOOL bTaskSwitch;
  529. DWORD dwAllError = 0;
  530. HTASK hCurrentTask;
  531. hCurrentTask = GetCurrentTask();
  532. // If the device is "all" and the message is *not*
  533. // "sysinfo" then we must walk all devices
  534. if (wDeviceID == MCI_ALL_DEVICE_ID && wMessage != MCI_SYSINFO && wMessage != MCI_SOUND)
  535. {
  536. if (wMessage == MCI_OPEN)
  537. {
  538. dwRetVal = MCIERR_CANNOT_USE_ALL;
  539. goto exitfn;
  540. }
  541. bWalkAll = TRUE;
  542. // Start at device #1
  543. wDeviceID = 1;
  544. } else
  545. bWalkAll = FALSE;
  546. // Walk through all devices if bWalkAll or just one device if !bWalkAll
  547. do
  548. {
  549. // Initialize
  550. dwRetVal = 0;
  551. bTaskSwitch = FALSE;
  552. // Validate the device ID if single device
  553. if (!bWalkAll)
  554. {
  555. if (!MCI_DO_NOT_NEED_OPEN(wMessage))
  556. {
  557. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  558. {
  559. dwRetVal = MCIERR_INVALID_DEVICE_ID;
  560. goto exitfn;
  561. }
  562. nodeWorking = MCI_lpDeviceList[wDeviceID];
  563. }
  564. } else if (wMessage != MCI_SYSINFO)
  565. nodeWorking = MCI_lpDeviceList[wDeviceID];
  566. // Skip if walking the device list and the
  567. // device is not part of the current task
  568. if (bWalkAll)
  569. {
  570. if (nodeWorking == NULL ||
  571. nodeWorking->hOpeningTask != hCurrentTask)
  572. goto no_send;
  573. }
  574. // If the device is in the process of closing and the message
  575. // is not MCI_CLOSE_DRIVER then return an error
  576. if (nodeWorking != NULL &&
  577. (nodeWorking->dwMCIFlags & MCINODE_ISCLOSING) &&
  578. wMessage != MCI_CLOSE_DRIVER)
  579. {
  580. dwRetVal = MCIERR_DEVICE_LOCKED;
  581. goto exitfn;
  582. }
  583. // If this message is being sent from the wrong task (the device was auto-
  584. // opened) fail all but the MCI_CLOSE message which gets sent inter-task
  585. if (nodeWorking != NULL &&
  586. nodeWorking->hCreatorTask != hCurrentTask)
  587. if (wMessage != MCI_CLOSE)
  588. return MCIERR_ILLEGAL_FOR_AUTO_OPEN;
  589. else
  590. {
  591. // Don't even allow close from mciSendCommand if auto-open device has a
  592. // pending close
  593. if (nodeWorking->dwMCIFlags & MCINODE_ISAUTOCLOSING)
  594. {
  595. // But at least give the close a chance to take place
  596. //!! Yield();
  597. return MCIERR_DEVICE_LOCKED;
  598. } else
  599. bTaskSwitch = TRUE;
  600. }
  601. dwRetVal = mciSendSingleCommand (wDeviceID, wMessage, dwParam1,
  602. dwParam2, nodeWorking, bTaskSwitch,
  603. lpOpenInfo);
  604. no_send:
  605. // If we are processing multiple devices
  606. if (bWalkAll)
  607. {
  608. // If there was an error for this device
  609. if (dwRetVal != 0)
  610. // If this is not the first error
  611. if (dwAllError != 0)
  612. dwAllError = MCIERR_MULTIPLE;
  613. // Just one error so far
  614. else
  615. dwAllError = dwRetVal;
  616. }
  617. } while (bWalkAll && ++wDeviceID < MCI_wNextDeviceID);
  618. exitfn:
  619. // Return the accumulated error if multiple devices or just the single error
  620. return bWalkAll ? dwAllError : dwRetVal;
  621. }
  622. /*
  623. * @doc EXTERNAL MCI
  624. *
  625. * @api DWORD | mciSendCommand | This function sends a command message to
  626. * the specified MCI device.
  627. *
  628. * @parm UINT | wDeviceID | Specifies the device ID of the MCI device to
  629. * receive the command. This parameter is
  630. * not used with the <m MCI_OPEN> command.
  631. *
  632. * @parm UINT | wMessage | Specifies the command message.
  633. *
  634. * @parm DWORD | dwParam1 | Specifies flags for the command.
  635. *
  636. * @parm DWORD | dwParam2 | Specifies a pointer to a parameter block
  637. * for the command.
  638. *
  639. * @rdesc Returns zero if the function was successful. Otherwise, it returns
  640. * error information. The low-order word
  641. * of the returned DWORD is the error return value. If the error is
  642. * device-specific, the high-order word contains the driver ID; otherwise
  643. * the high-order word is zero.
  644. *
  645. * To get a textual description of <f mciSendCommand> return values,
  646. * pass the return value to <f mciGetErrorString>.
  647. *
  648. * Error values that are returned when a device is being opened
  649. * are listed with the MCI_OPEN message. In addition to the
  650. * MCI_OPEN error returns, this function can
  651. * return the following values:
  652. *
  653. * @flag MCIERR_BAD_TIME_FORMAT | Illegal value for time format.
  654. *
  655. * @flag MCIERR_CANNOT_USE_ALL | The device name "all" is not allowed
  656. * for this command.
  657. *
  658. * @flag MCIERR_CREATEWINDOW | Could not create or use window.
  659. *
  660. * @flag MCIERR_DEVICE_LOCKED | The device is locked until it is
  661. * closed automatically.
  662. *
  663. * @flag MCIERR_DEVICE_NOT_READY | Device not ready.
  664. *
  665. * @flag MCIERR_DEVICE_TYPE_REQUIRED | The device name must be a valid
  666. * device type.
  667. *
  668. * @flag MCIERR_DRIVER | Unspecified device error.
  669. *
  670. * @flag MCIERR_DRIVER_INTERNAL | Internal driver error.
  671. *
  672. * @flag MCIERR_FILE_NOT_FOUND | Requested file not found.
  673. *
  674. * @flag MCIERR_FILE_NOT_SAVED | The file was not saved.
  675. *
  676. * @flag MCIERR_FILE_READ | A read from the file failed.
  677. *
  678. * @flag MCIERR_FILE_WRITE | A write to the file failed.
  679. *
  680. * @flag MCIERR_FLAGS_NOT_COMPATIBLE | Incompatible parameters
  681. * were specified.
  682. *
  683. * @flag MCIERR_HARDWARE | Hardware error on media device.
  684. *
  685. * @flag MCIERR_INTERNAL | mmsystem startup error.
  686. *
  687. * @flag MCIERR_INVALID_DEVICE_ID | Invalid device ID.
  688. *
  689. * @flag MCIERR_INVALID_DEVICE_NAME | The device is not open
  690. * or is not known.
  691. *
  692. * @flag MCIERR_INVALID_FILE | Invalid file format.
  693. *
  694. * @flag MCIERR_MULTIPLE | Errors occurred in more than one device.
  695. *
  696. * @flag MCIERR_NO_WINDOW | There is no display window.
  697. *
  698. * @flag MCIERR_NULL_PARAMETER_BLOCK | Parameter block pointer was NULL.
  699. *
  700. * @flag MCIERR_OUT_OF_MEMORY | Not enough memory for requested operation.
  701. *
  702. * @flag MCIERR_OUTOFRANGE | Parameter value out of range.
  703. *
  704. * @flag MCIERR_UNNAMED_RESOURCE | Attempt to save unnamed file.
  705. *
  706. * @flag MCIERR_UNRECOGNIZED_COMMAND | Unknown command.
  707. *
  708. * @flag MCIERR_UNSUPPORTED_FUNCTION | Action not available for this
  709. * device.
  710. *
  711. * The following additional return values are defined for MCI sequencers:
  712. *
  713. * @flag MCIERR_SEQ_DIV_INCOMPATIBLE | Set Song Pointer incompatible
  714. * with SMPTE files.
  715. *
  716. * @flag MCIERR_SEQ_PORT_INUSE | Specified port is in use.
  717. *
  718. * @flag MCIERR_SEQ_PORT_MAPNODEVICE | Current map uses non-existent
  719. * device.
  720. *
  721. * @flag MCIERR_SEQ_PORT_MISCERROR | Miscellaneous error with
  722. * specified port.
  723. *
  724. * @flag MCIERR_SEQ_PORT_NONEXISTENT | Specified port does not exist.
  725. *
  726. * @flag MCIERR_SEQ_PORTUNSPECIFIED | No current MIDI port.
  727. *
  728. * @flag MCIERR_SEQ_NOMIDIPRESENT | No MIDI ports present.
  729. *
  730. * @flag MCIERR_SEQ_TIMER | Timer error.
  731. *
  732. * The following additional return values are defined for MCI waveform
  733. * audio devices:
  734. *
  735. * @flag MCIERR_WAVE_INPUTSINUSE | No compatible waveform recording
  736. * device is free.
  737. *
  738. * @flag MCIERR_WAVE_INPUTSUNSUITABLE | No compatible waveform
  739. * recording devices.
  740. *
  741. * @flag MCIERR_WAVE_INPUTUNSPECIFIED | Any compatible waveform
  742. * recording device may be used.
  743. *
  744. * @flag MCIERR_WAVE_OUTPUTSINUSE | No compatible waveform playback
  745. * device is free.
  746. *
  747. * @flag MCIERR_WAVE_OUTPUTSUNSUITABLE | No compatible waveform
  748. * playback devices.
  749. *
  750. * @flag MCIERR_WAVE_OUTPUTUNSPECIFIED | Any compatible waveform
  751. * playback device may be used.
  752. *
  753. * @flag MCIERR_WAVE_SETINPUTINUSE | Set waveform recording device
  754. * is in use.
  755. *
  756. * @flag MCIERR_WAVE_SETINPUTUNSUITABLE | Set waveform recording
  757. * device is incompatible with set format.
  758. *
  759. * @flag MCIERR_WAVE_SETOUTPUTINUSE | Set waveform playback device
  760. * is in use.
  761. *
  762. * @flag MCIERR_WAVE_SETOUTPUTUNSUITABLE | Set waveform playback
  763. * device is incompatible with set format.
  764. *
  765. * @comm Use the <m MCI_OPEN> command to obtain the device ID
  766. * specified by <p wDeviceID>.
  767. *
  768. * @xref mciGetErrorString mciSendString
  769. */
  770. /*
  771. * @doc internal
  772. *
  773. * @api DWORD | mciDriverEntry | Actually a callback. The entry point for MCI drivers.
  774. *
  775. * @parm UINT | wMessage | Identifies the requested action to be performed.
  776. *
  777. * @parm DWORD | dwParam1 | Specifies data for this message. Defined separately
  778. * for each message.
  779. *
  780. * @parm DWORD | dwParam2 | Specifies data for this message. Defined separately
  781. * for each message.
  782. *
  783. * @rdesc The return value is defined separately for each message.
  784. */
  785. DWORD WINAPI
  786. mciSendCommand(
  787. UINT wDeviceID,
  788. UINT wMessage,
  789. DWORD dwParam1,
  790. DWORD dwParam2
  791. )
  792. {
  793. // Initialize the 16-bit device list if needed.
  794. if (!MCI_bDeviceListInitialized && !mciInitDeviceList())
  795. return MCIERR_OUT_OF_MEMORY;
  796. // MCI_OPEN_DRIVER & MCI_CLOSE_DRIVER only supported on 16-bit drivers
  797. if ( (wMessage == MCI_OPEN_DRIVER) || (wMessage == MCI_CLOSE_DRIVER) ) {
  798. return mciSendCommand16( wDeviceID, wMessage, dwParam1, dwParam2 );
  799. }
  800. /*
  801. ** If we are opening the device try the 32 bit side first. If this
  802. ** worked (hopefully this is the usual case) we return the given
  803. ** device ID. Otherwise, we try for a 16 bit device.
  804. */
  805. if ( wMessage == MCI_OPEN ) {
  806. DWORD dwErr;
  807. DPRINTF(("mciSendCommand: Got an MCI_OPEN command... "
  808. "trying 32 bits\r\n" ));
  809. dwErr = mciMessage( THUNK_MCI_SENDCOMMAND, (DWORD)wDeviceID,
  810. (DWORD)wMessage, dwParam1, dwParam2 );
  811. if ( dwErr == MMSYSERR_NOERROR ) {
  812. LPMCI_OPEN_PARMS lpOpenParms = (LPMCI_OPEN_PARMS)dwParam2;
  813. DPRINTF(("mciSendCommand: We have a 32 bit driver,"
  814. " devID = 0x%X\r\n", lpOpenParms->wDeviceID ));
  815. return dwErr;
  816. }
  817. else {
  818. /*
  819. ** We could open the device on the 32 bit side so let
  820. ** the 16 bit code have a go (ie. just fall thru to the code below).
  821. */
  822. DPRINTF(("mciSendCommand: Could not find a 32 bit driver, "
  823. "trying for a 16 bit driver\r\n" ));
  824. dwErr = mciSendCommand16( wDeviceID, wMessage, dwParam1, dwParam2 );
  825. if ( dwErr == MMSYSERR_NOERROR ) {
  826. LPMCI_OPEN_PARMS lpOpenParms = (LPMCI_OPEN_PARMS)dwParam2;
  827. DPRINTF(("mciSendCommand: We have a 16 bit driver,"
  828. " devID = 0x%X\r\n", lpOpenParms->wDeviceID ));
  829. }
  830. return dwErr;
  831. }
  832. }
  833. else {
  834. DWORD dwErr16;
  835. DWORD dwErr32;
  836. /*
  837. ** If we have been given the MCI_ALL_DEVICE_ID then we have to
  838. ** send the command to both the 32 and 16 bit side.
  839. **
  840. ** Special care needs to be taken with the MCI_ALL_DEVICE_ID.
  841. ** The message must be passed on to both 32 and 16 bit devices.
  842. */
  843. if (CouldBe16bitDrv(wDeviceID)) {
  844. dwErr16 = mciSendCommand16( wDeviceID, wMessage,
  845. dwParam1, dwParam2 );
  846. if ( wDeviceID != MCI_ALL_DEVICE_ID ) {
  847. return dwErr16;
  848. }
  849. }
  850. dwErr32 = mciMessage( THUNK_MCI_SENDCOMMAND, (DWORD)wDeviceID,
  851. (DWORD)wMessage, dwParam1, dwParam2 );
  852. /*
  853. ** If we have the MCI_ALL_DEVICE_ID device ID we only return
  854. ** an error if both the 16 and 32 bit calls failed. In which
  855. ** case we return the 32 bit error code.
  856. */
  857. if ( wDeviceID == MCI_ALL_DEVICE_ID ) {
  858. if ( (dwErr16 != MMSYSERR_NOERROR)
  859. && (dwErr32 != MMSYSERR_NOERROR) ) {
  860. return dwErr32;
  861. }
  862. else {
  863. return MMSYSERR_NOERROR;
  864. }
  865. }
  866. return dwErr32;
  867. }
  868. }
  869. /*****************************Private*Routine******************************\
  870. * mciSendCommand16
  871. *
  872. * Here is where we execute the real 16 bit mciSendCommand. Hoefully this
  873. * will not get called to often.
  874. *
  875. * History:
  876. * dd-mm-94 - StephenE - Created
  877. *
  878. \**************************************************************************/
  879. DWORD WINAPI
  880. mciSendCommand16(
  881. UINT wDeviceID,
  882. UINT wMessage,
  883. DWORD dwParam1,
  884. DWORD dwParam2
  885. )
  886. {
  887. DWORD dwErr;
  888. MCI_INTERNAL_OPEN_INFO OpenInfo;
  889. //
  890. // Send the command. This shell is responsible for adding the device ID
  891. // to the error code if necessary
  892. //
  893. OpenInfo.hCallingTask = GetCurrentTask();
  894. OpenInfo.lpstrParams = NULL;
  895. OpenInfo.lpstrPointerList = NULL;
  896. OpenInfo.wParsingError = 0;
  897. dwErr = mciSendCommandInternal (wDeviceID, wMessage,
  898. dwParam1, dwParam2, &OpenInfo);
  899. //
  900. // If the return value contains a resource ID then clear
  901. // it from clear the high word
  902. //
  903. if (dwErr & MCI_RESOURCE_RETURNED)
  904. ((LPDWORD)dwParam2)[1] &= 0xFFFF;
  905. dwErr &= 0xFFFF;
  906. //
  907. // If the error message is in a driver, store the driver ID in the high
  908. // word of the error code
  909. //
  910. if ((UINT)dwErr >= MCIERR_CUSTOM_DRIVER_BASE)
  911. dwErr |= ((DWORD)wDeviceID << 16);
  912. #ifdef DEBUG
  913. // Dump the error text if any to the debug terminal
  914. if (dwErr != 0)
  915. {
  916. char strTemp[MAXERRORLENGTH];
  917. if (!mciGetErrorString (dwErr, strTemp, sizeof(strTemp)))
  918. LoadString(ghInst, STR_MCISCERRTXT, strTemp, sizeof(strTemp));
  919. else
  920. DPRINTF(("mciSendCommand: %ls\r\n",(LPSTR)strTemp));
  921. }
  922. #endif
  923. return dwErr;
  924. }
  925. // Grab colonized digit
  926. // Return is number of bytes written to output (NOT including NULL)
  927. // or 0 if out of room in output buffer (but is terminated anyway)
  928. // If there is room then at least two digits are written, padded with '0'
  929. // if necessary. The function assumes that the buffer size is non-zero length,
  930. // as this is checked in the calling function.
  931. STATICFN UINT PASCAL NEAR
  932. mciColonizeDigit(
  933. LPSTR lpstrOutput,
  934. unsigned char cDigit,
  935. UINT wSize
  936. )
  937. {
  938. UINT wCount;
  939. wCount = 2;
  940. // If there is room for at least two digits
  941. if (wSize >= 3)
  942. {
  943. if (cDigit >= 100)
  944. {
  945. wCount = 3;
  946. if (wSize < 4)
  947. goto terminate;
  948. *lpstrOutput++ = (char)((cDigit / 100) % 10 + '0');
  949. cDigit = (char)(cDigit % 100);
  950. }
  951. *lpstrOutput++ = (char)(cDigit / 10 + '0');
  952. *lpstrOutput++ = (char)(cDigit % 10 + '0');
  953. }
  954. terminate:
  955. *lpstrOutput++ = '\0';
  956. // If we ran out of room then return an error
  957. return (wCount >= wSize) ? 0 : wCount;
  958. }
  959. /*
  960. * @doc INTERNAL MCI
  961. * @func BOOL | mciColonize | Convert a colonized dword into a string
  962. * representation
  963. *
  964. * @parm LPSTR | lpstrOutput | Output buffer
  965. *
  966. * @parm UINT | wLength | Size of output buffer
  967. *
  968. * @parm DWORD | dwData | Value to convert
  969. *
  970. * @parm UINT | wType | Either MCI_COLONIZED3_RETURN or
  971. * MCI_COLONIZED4_RETURN is set (HIWORD portion only!)
  972. *
  973. * @comm Example: For C4, 0x01020304 is converted to "04:03:02:01"
  974. * For C3, 0x01020304 is converted to "04:03:02"
  975. *
  976. * @rdesc FALSE if there is not enough room in the output buffer
  977. *
  978. */
  979. STATICFN BOOL PASCAL NEAR mciColonize(
  980. LPSTR lpstrOutput,
  981. UINT wLength,
  982. DWORD dwData,
  983. UINT wType
  984. )
  985. {
  986. LPSTR lpstrInput = (LPSTR)&dwData;
  987. UINT wSize;
  988. int i;
  989. for (i = 1; i <= (wType & HIWORD(MCI_COLONIZED3_RETURN) ? 3 : 4); ++i)
  990. {
  991. wSize = mciColonizeDigit (lpstrOutput,
  992. *lpstrInput++,
  993. wLength);
  994. if (wSize == 0)
  995. return FALSE;
  996. lpstrOutput += wSize;
  997. wLength -= wSize;
  998. if (i < 3 || i < 4 && wType & HIWORD(MCI_COLONIZED4_RETURN))
  999. {
  1000. --wLength;
  1001. if (wLength == 0)
  1002. return FALSE;
  1003. else
  1004. *lpstrOutput++ = ':';
  1005. }
  1006. }
  1007. return TRUE;
  1008. }
  1009. //
  1010. // Convert the return value to a return string
  1011. //
  1012. STATICFN UINT PASCAL NEAR
  1013. mciConvertReturnValue(
  1014. UINT wType,
  1015. UINT wErrCode,
  1016. UINT wDeviceID,
  1017. LPDWORD dwParams,
  1018. LPSTR lpstrReturnString,
  1019. UINT wReturnLength
  1020. )
  1021. {
  1022. UINT wExternalTable;
  1023. if (lpstrReturnString == NULL || wReturnLength == 0)
  1024. return 0;
  1025. switch (wType)
  1026. {
  1027. case MCI_INTEGER:
  1028. // Convert integer or resource return value to string
  1029. if (wErrCode & HIWORD(MCI_RESOURCE_RETURNED))
  1030. {
  1031. int nResId = HIWORD(dwParams[1]);
  1032. LPMCI_DEVICE_NODE nodeWorking;
  1033. HINSTANCE hInstance;
  1034. if ((nodeWorking = MCI_lpDeviceList[wDeviceID])
  1035. == NULL)
  1036. {
  1037. // Return blank string on memory error
  1038. DOUT ("mciConvertReturnValue Warning:NULL device node\r\n");
  1039. break;
  1040. }
  1041. // Return value is a resource
  1042. if (wErrCode & HIWORD(MCI_RESOURCE_DRIVER))
  1043. {
  1044. // Return string ID belongs to driver
  1045. hInstance = nodeWorking->hDriver;
  1046. wExternalTable = nodeWorking->wCustomCommandTable;
  1047. } else
  1048. {
  1049. wExternalTable = nodeWorking->wCommandTable;
  1050. hInstance = ghInst;
  1051. }
  1052. // Try to get string from custom or device specific external table
  1053. if (wExternalTable == -1 ||
  1054. command_tables[wExternalTable].hModule == NULL ||
  1055. LoadString (command_tables[wExternalTable].hModule,
  1056. nResId, lpstrReturnString, wReturnLength)
  1057. == 0)
  1058. {
  1059. // Try to get string from CORE.MCI if it's not from the driver
  1060. if (hInstance != ghInst ||
  1061. command_tables[0].hModule == NULL ||
  1062. LoadString (command_tables[0].hModule,
  1063. nResId, lpstrReturnString, wReturnLength)
  1064. == 0)
  1065. // Get string from custom module or MMSYSTEM.DLL
  1066. LoadString (hInstance, nResId, lpstrReturnString,
  1067. wReturnLength);
  1068. }
  1069. } else if (wErrCode & HIWORD(MCI_COLONIZED3_RETURN) ||
  1070. wErrCode & HIWORD(MCI_COLONIZED4_RETURN))
  1071. {
  1072. if (!mciColonize (lpstrReturnString,
  1073. wReturnLength, dwParams[1], wErrCode))
  1074. return MCIERR_PARAM_OVERFLOW;
  1075. } else
  1076. // Convert integer return value to string
  1077. {
  1078. DWORD dwTemp;
  1079. // Need room for a sign, up to ten digits and a NULL
  1080. if (wReturnLength < 12)
  1081. return MCIERR_PARAM_OVERFLOW;
  1082. if (wType == MCI_STRING ||
  1083. wErrCode == HIWORD(MCI_INTEGER_RETURNED))
  1084. dwTemp = *(LPDWORD)dwParams[1];
  1085. else
  1086. dwTemp = dwParams[1];
  1087. wsprintf(lpstrReturnString, szLongFormat, dwTemp);
  1088. }
  1089. break;
  1090. case MCI_RECT:
  1091. // Need from for 4 times (a sign plus 5 digits) plus three spaces and a NULL
  1092. if (wReturnLength < 4 * 6 + 4)
  1093. return MCIERR_PARAM_OVERFLOW;
  1094. wsprintf (lpstrReturnString, szRectFormat,
  1095. ((LPWORD)dwParams)[2], ((LPWORD)dwParams)[3],
  1096. ((LPWORD)dwParams)[4], ((LPWORD)dwParams)[5]);
  1097. break;
  1098. default:
  1099. // Only support INTEGERs & MIXED
  1100. DOUT ("mciConvertReturnValue Warning: Unknown return type\r\n");
  1101. return MCIERR_PARSER_INTERNAL;
  1102. }
  1103. return 0;
  1104. }
  1105. //
  1106. // Pull off the command name and device name from the command string,
  1107. // leaving *lplpstrCommand pointing past the device name
  1108. //
  1109. // Returns 0 or an error code on failure. If successful, the caller must
  1110. // free the pstrCommandName and pstrDeviceName
  1111. //
  1112. // If bCompound then check for a '!' separator in the extracted device name
  1113. // and return only the element part. This is done so that inter-task
  1114. // commands to auto-opened devices will include the correct device name
  1115. //
  1116. STATICFN DWORD PASCAL NEAR
  1117. mciSeparateCommandParts(
  1118. LPSTR FAR *lplpstrCommand,
  1119. BOOL bCompound,
  1120. LPSTR FAR *lplpstrCommandName,
  1121. LPSTR FAR *lplpstrDeviceName
  1122. )
  1123. {
  1124. LPSTR lpstrCommand;
  1125. UINT wErr;
  1126. // Localize the input
  1127. lpstrCommand = *lplpstrCommand;
  1128. // Remove leading spaces
  1129. while (*lpstrCommand == ' ')
  1130. ++lpstrCommand;
  1131. if (*lpstrCommand == '\0')
  1132. return MCIERR_MISSING_COMMAND_STRING;
  1133. // Pull the command name off of the command string
  1134. if ((wErr = mciEatToken (&lpstrCommand, ' ', lplpstrCommandName, FALSE))
  1135. != 0)
  1136. return wErr;
  1137. // Skip past spaces
  1138. while (*lpstrCommand == ' ')
  1139. ++lpstrCommand;
  1140. // If we're looking for compound elements then yank off any leading
  1141. // device type if it is not the open command
  1142. if (bCompound && lstrcmpi (szOpen, *lplpstrCommandName) != 0)
  1143. {
  1144. LPSTR lpstrTemp = lpstrCommand;
  1145. while (*lpstrTemp != '\0')
  1146. {
  1147. if (*lpstrTemp == '!')
  1148. {
  1149. // A ! was found so skip past it
  1150. lpstrCommand = lpstrTemp + 1;
  1151. break;
  1152. } else
  1153. ++lpstrTemp;
  1154. }
  1155. }
  1156. // Pull the device name off of the command string
  1157. if ((wErr = mciEatToken (&lpstrCommand, ' ', lplpstrDeviceName, FALSE))
  1158. != 0)
  1159. {
  1160. mciFree (*lplpstrCommandName);
  1161. return wErr;
  1162. }
  1163. // Fix up the results
  1164. *lplpstrCommand = lpstrCommand;
  1165. return 0;
  1166. }
  1167. STATICFN DWORD NEAR PASCAL
  1168. mciSendSystemString(
  1169. LPCSTR lpstrCommand,
  1170. LPSTR lpstrReturnString,
  1171. UINT wReturnLength
  1172. )
  1173. {
  1174. DWORD dwRet;
  1175. LPMCI_SYSTEM_MESSAGE lpMessage;
  1176. if (!hwndNotify)
  1177. return MCIERR_INTERNAL;
  1178. if (lpMessage = mciAlloc (sizeof (MCI_SYSTEM_MESSAGE))) {
  1179. LPSTR lpstrPath;
  1180. if (lpstrPath = mciAlloc(MAX_PATHNAME)) {
  1181. if (!(DosGetCurrentDir(0, lpstrPath))) {
  1182. lpMessage->lpstrCommand = (LPSTR)lpstrCommand;
  1183. lpMessage->lpstrReturnString = lpstrReturnString;
  1184. lpMessage->wReturnLength = wReturnLength;
  1185. lpMessage->hCallingTask = GetCurrentTask();
  1186. lpMessage->lpstrNewDirectory = lpstrPath;
  1187. lpMessage->nNewDrive = DosGetCurrentDrive();
  1188. dwRet = (DWORD)SendMessage(hwndNotify, MM_MCISYSTEM_STRING, (WPARAM)0, (LPARAM)lpMessage);
  1189. } else {
  1190. DOUT("mciSendSystemString: cannot get current directory\r\n");
  1191. dwRet = MCIERR_GET_CD;
  1192. }
  1193. mciFree(lpstrPath);
  1194. } else {
  1195. DOUT("mciSendSystemString: cannot allocate new path\r\n");
  1196. dwRet = MCIERR_OUT_OF_MEMORY;
  1197. }
  1198. mciFree(lpMessage);
  1199. } else {
  1200. DOUT("mciSendSystemString: cannot allocate message block\r\n");
  1201. dwRet = MCIERR_OUT_OF_MEMORY;
  1202. }
  1203. return dwRet;
  1204. }
  1205. DWORD FAR PASCAL
  1206. mciRelaySystemString(
  1207. LPMCI_SYSTEM_MESSAGE lpMessage
  1208. )
  1209. {
  1210. DWORD dwRet;
  1211. LPSTR lpstrOldPath;
  1212. if (lpstrOldPath = mciAlloc(MAX_PATHNAME)) {
  1213. if (!(DosGetCurrentDir(0, lpstrOldPath))) {
  1214. int nOldDrive;
  1215. nOldDrive = DosGetCurrentDrive();
  1216. if (DosSetCurrentDrive(lpMessage->nNewDrive)) {
  1217. if (DosChangeDir(lpMessage->lpstrNewDirectory) == 1) {
  1218. dwRet = mciSendStringInternal (NULL, NULL, 0, 0, lpMessage);
  1219. if (!DosSetCurrentDrive(nOldDrive))
  1220. DOUT("mciRelaySystemString: WARNING, cannot restore drive\r\n");
  1221. if (DosChangeDir(lpstrOldPath) != 1)
  1222. DOUT("mciRelaySystemString: WARNING, cannot restore directory\r\n");
  1223. } else {
  1224. DosSetCurrentDrive(nOldDrive);
  1225. DOUT("mciRelaySystemString: cannot change to new directory\r\n");
  1226. dwRet = MCIERR_SET_CD;
  1227. }
  1228. } else {
  1229. DOUT("mciRelaySystemString: cannot change to new drive\r\n");
  1230. dwRet = MCIERR_SET_DRIVE;
  1231. }
  1232. } else {
  1233. DOUT("mciRelaySystemString: cannot get old directory\r\n");
  1234. dwRet = MCIERR_GET_CD;
  1235. }
  1236. mciFree(lpstrOldPath);
  1237. } else {
  1238. DOUT("mciRelaySystemString: cannot allocate old path\r\n");
  1239. dwRet = MCIERR_OUT_OF_MEMORY;
  1240. }
  1241. return dwRet;
  1242. }
  1243. // Returns TRUE if "notify" is contained in string with leading blank
  1244. // and trailing blank or '\0'
  1245. STATICFN BOOL PASCAL NEAR
  1246. mciFindNotify(
  1247. LPSTR lpString
  1248. )
  1249. {
  1250. while (*lpString != '\0')
  1251. {
  1252. // "notify" must be preceded by a blank
  1253. if (*lpString++ == ' ')
  1254. {
  1255. LPSTR lpTemp;
  1256. lpTemp = szNotify;
  1257. while (*lpTemp != '\0' && *lpString != '\0' &&
  1258. *lpTemp == MCI_TOLOWER(*lpString))
  1259. {
  1260. ++lpTemp;
  1261. ++lpString;
  1262. }
  1263. // "notify" must be followed by a blank or a null
  1264. if (*lpTemp == '\0' &&
  1265. (*lpString == '\0' || *lpString == ' '))
  1266. return TRUE;
  1267. }
  1268. }
  1269. return FALSE;
  1270. }
  1271. /*
  1272. * @doc INTERNAL MCI
  1273. *
  1274. * @func UINT | mciAutoOpenDevice | Try to auto-open the given device and
  1275. * then send the given command with notification sent to the system task
  1276. * window proc which sends a close command to the device on receipt
  1277. *
  1278. * @parm LPSTR | lpstrDeviceName | The device name to open
  1279. *
  1280. * @parm LPSTR | lpstrCommand | The full command to send including the
  1281. * device name which must be the same as lpstrDeviceName
  1282. *
  1283. * @parm LPSTR | lpstrReturnString | The caller's return string buffer
  1284. *
  1285. * @parm UINT | wReturnLength | Size of the caller's return string buffer
  1286. *
  1287. * @rdesc The errorcode to return to the user
  1288. */
  1289. STATICFN UINT PASCAL NEAR
  1290. mciAutoOpenDevice(
  1291. LPSTR lpstrDeviceName,
  1292. LPSTR lpstrCommand,
  1293. LPSTR lpstrReturnString,
  1294. UINT wReturnLength
  1295. )
  1296. {
  1297. LPSTR lpstrTempCommand, lpstrTempReturn = NULL;
  1298. UINT wErr;
  1299. // "notify" not allowed. This will be found by the parser but the wrong
  1300. // error message will be returned.
  1301. if (mciFindNotify (lpstrCommand))
  1302. return MCIERR_NOTIFY_ON_AUTO_OPEN;
  1303. // Build the command string "open <device name>"
  1304. // Must be GMEM_SHARE for system task
  1305. // device name + blank + "open"
  1306. if ((lpstrTempCommand = mciAlloc (lstrlen (lpstrDeviceName) + 1 +
  1307. sizeof (szOpen)))
  1308. == NULL)
  1309. return MCIERR_OUT_OF_MEMORY;
  1310. wsprintf(lpstrTempCommand, szCmdFormat, (LPSTR)szOpen, lpstrDeviceName);
  1311. // Get the open string into the system task via a SendMessage() to mmWndProc
  1312. wErr = (UINT)mciSendSystemString (lpstrTempCommand, NULL, NULL);
  1313. mciFree (lpstrTempCommand);
  1314. if (wErr != 0)
  1315. return wErr;
  1316. lpstrTempCommand = NULL;
  1317. // Must make a GMEM_SHARE copy of the return string for system task
  1318. if (lpstrReturnString == NULL ||
  1319. (lpstrTempReturn = mciAlloc (wReturnLength + 1)) != NULL)
  1320. {
  1321. // Build a GMEM_SHARE command string "<user command> <notify>
  1322. // command + blank + "notify"
  1323. if ((lpstrTempCommand = mciAlloc (lstrlen (lpstrCommand) + 1 + sizeof(szNotify))) == NULL)
  1324. mciFree (lpstrTempReturn);
  1325. }
  1326. if (lpstrTempCommand == NULL)
  1327. {
  1328. // Close the device
  1329. mciDriverNotify (hwndNotify, mciGetDeviceID (lpstrDeviceName), 0);
  1330. return MCIERR_OUT_OF_MEMORY;
  1331. }
  1332. wsprintf(lpstrTempCommand, szCmdFormat, lpstrCommand, (LPSTR)szNotify);
  1333. // Get the user command string into the system task via a SendMessage()
  1334. // to mmWndProc
  1335. // The notification handle is also mmWndProc
  1336. wErr = (UINT)mciSendSystemString (lpstrTempCommand, lpstrTempReturn,
  1337. wReturnLength);
  1338. // Copy the return string into the user's buffer
  1339. if (lpstrReturnString != NULL)
  1340. {
  1341. lstrcpy (lpstrReturnString, lpstrTempReturn);
  1342. mciFree (lpstrTempReturn);
  1343. }
  1344. mciFree (lpstrTempCommand);
  1345. // If there was an error we must close the device
  1346. if (wErr != 0)
  1347. mciAutoCloseDevice (lpstrDeviceName);
  1348. return wErr;
  1349. }
  1350. //
  1351. // Identical to mciSendString() but the lpMessage parameter is tacked on
  1352. //
  1353. // lpMessage comes from inter-task mciSendString and includes an
  1354. // hCallingTask item which is sent down the the OPEN command
  1355. //
  1356. STATICFN DWORD NEAR PASCAL
  1357. mciSendStringInternal(
  1358. LPCSTR lpstrCommand,
  1359. LPSTR lpstrReturnString,
  1360. UINT wReturnLength,
  1361. HWND hwndCallback,
  1362. LPMCI_SYSTEM_MESSAGE lpMessage
  1363. )
  1364. {
  1365. UINT wID, wConvertReturnValue, wErr, wMessage;
  1366. UINT wLen;
  1367. UINT wDeviceID;
  1368. LPDWORD lpdwParams = NULL;
  1369. DWORD dwReturn, dwFlags = 0;
  1370. LPSTR lpCommandItem;
  1371. DWORD dwErr, dwRetType;
  1372. UINT wTable = (UINT)-1;
  1373. LPSTR lpstrDeviceName = NULL, lpstrCommandName = NULL;
  1374. LPSTR FAR *lpstrPointerList = NULL;
  1375. LPSTR lpstrCommandStart;
  1376. HTASK hCallingTask;
  1377. UINT wParsingError;
  1378. BOOL bNewDevice;
  1379. LPSTR lpstrInputCopy;
  1380. // Did this call come in from another task
  1381. if (lpMessage != NULL)
  1382. {
  1383. // Yes so restore info
  1384. lpstrCommand = lpMessage->lpstrCommand;
  1385. lpstrReturnString = lpMessage->lpstrReturnString;
  1386. wReturnLength = lpMessage->wReturnLength;
  1387. hwndCallback = hwndNotify;
  1388. hCallingTask = lpMessage->hCallingTask;
  1389. lpstrInputCopy = NULL;
  1390. } else
  1391. {
  1392. BOOL bInQuotes = FALSE;
  1393. // No so set hCallingTask to current
  1394. hCallingTask = GetCurrentTask();
  1395. if (lpstrCommand == NULL)
  1396. return MCIERR_MISSING_COMMAND_STRING;
  1397. // Make a copy of the input string and convert tabs to
  1398. // spaces except those inside quotes
  1399. //
  1400. if ((lpstrInputCopy = mciAlloc (lstrlen (lpstrCommand) + 1)) == NULL)
  1401. return MCIERR_OUT_OF_MEMORY;
  1402. lstrcpy (lpstrInputCopy, lpstrCommand);
  1403. lpstrCommand = lpstrInputCopy;
  1404. lpstrCommandStart = (LPSTR)lpstrCommand;
  1405. while (*lpstrCommandStart != '\0')
  1406. {
  1407. if (*lpstrCommandStart == '"')
  1408. bInQuotes = !bInQuotes;
  1409. else if (!bInQuotes && *lpstrCommandStart == '\t')
  1410. *lpstrCommandStart = ' ';
  1411. ++lpstrCommandStart;
  1412. }
  1413. }
  1414. lpstrCommandStart = (LPSTR)lpstrCommand;
  1415. if (lpstrReturnString == NULL) {
  1416. //
  1417. // As an additional safeguard against the driver writing into the
  1418. // output buffer when the return string pointer is NULL, set its
  1419. // length to 0
  1420. //
  1421. wReturnLength = 0;
  1422. }
  1423. else {
  1424. //
  1425. // Set return to empty string so that it won't print out garbage if not
  1426. // touched again
  1427. //
  1428. *lpstrReturnString = '\0';
  1429. }
  1430. // Pull the command name and device name off the command string
  1431. if ((dwReturn = mciSeparateCommandParts (&lpstrCommand, lpMessage != NULL,
  1432. &lpstrCommandName, &lpstrDeviceName)) != 0)
  1433. goto exitfn;
  1434. // Get the device id (if any) of the given device name
  1435. wDeviceID = mciGetDeviceIDInternal(lpstrDeviceName, hCallingTask);
  1436. // Allow "new" for an empty device name
  1437. if (wDeviceID == 0 && lstrcmpi (lpstrDeviceName, szNew) == 0)
  1438. {
  1439. bNewDevice = TRUE;
  1440. *lpstrDeviceName = '\0';
  1441. } else {
  1442. bNewDevice = FALSE;
  1443. }
  1444. // Look up the command name
  1445. wMessage = mciParseCommand (wDeviceID, lpstrCommandName, lpstrDeviceName,
  1446. &lpCommandItem, &wTable);
  1447. // If the device has a pending auto-close
  1448. if (MCI_VALID_DEVICE_ID(wDeviceID))
  1449. {
  1450. LPMCI_DEVICE_NODE nodeWorking = MCI_lpDeviceList[wDeviceID];
  1451. // Is there a pending auto-close message?
  1452. if (nodeWorking->dwMCIFlags & MCINODE_ISAUTOCLOSING)
  1453. {
  1454. // Let the device close
  1455. //!! Yield();
  1456. // Did the device close?
  1457. //!! wDeviceID = mciGetDeviceIDInternal (lpstrDeviceName, hCallingTask);
  1458. // If not then fail this command
  1459. //!! if (wDeviceID == 0)
  1460. //!! {
  1461. wErr = MCIERR_DEVICE_LOCKED;
  1462. goto cleanup;
  1463. //!! }
  1464. // If the call does not come from another task and is not owned by this task
  1465. // and is not the SYSINFO command
  1466. //
  1467. } else if (lpMessage == NULL &&
  1468. nodeWorking->hOpeningTask != nodeWorking->hCreatorTask &&
  1469. wMessage != MCI_SYSINFO)
  1470. // Send the string inter-task
  1471. {
  1472. if (mciFindNotify (lpstrCommandStart))
  1473. {
  1474. wErr = MCIERR_NOTIFY_ON_AUTO_OPEN;
  1475. goto cleanup;
  1476. } else
  1477. {
  1478. LPSTR lpstrReturnStringCopy;
  1479. mciFree(lpstrCommandName);
  1480. mciFree(lpstrDeviceName);
  1481. mciUnlockCommandTable (wTable);
  1482. if ((lpstrReturnStringCopy = mciAlloc (wReturnLength + 1)) != NULL)
  1483. {
  1484. dwReturn = mciSendSystemString (lpstrCommandStart,
  1485. lpstrReturnStringCopy,
  1486. wReturnLength);
  1487. lstrcpy (lpstrReturnString, lpstrReturnStringCopy);
  1488. mciFree (lpstrReturnStringCopy);
  1489. } else
  1490. dwReturn = MCIERR_OUT_OF_MEMORY;
  1491. goto exitfn;
  1492. }
  1493. }
  1494. }
  1495. // There must be a device name (except for the MCI_SOUND message)
  1496. if (*lpstrDeviceName == '\0' && wMessage != MCI_SOUND && !bNewDevice)
  1497. {
  1498. wErr = MCIERR_MISSING_DEVICE_NAME;
  1499. goto cleanup;
  1500. }
  1501. // The command must appear in the parser tables
  1502. if (wMessage == 0)
  1503. {
  1504. wErr = MCIERR_UNRECOGNIZED_COMMAND;
  1505. goto cleanup;
  1506. }
  1507. // The "new" device name is only legal for the open message
  1508. if (bNewDevice)
  1509. {
  1510. if (wMessage != MCI_OPEN)
  1511. {
  1512. wErr = MCIERR_INVALID_DEVICE_NAME;
  1513. goto cleanup;
  1514. }
  1515. }
  1516. // If there was no device ID
  1517. if (wDeviceID == 0)
  1518. // If auto open is not legal (usually internal commands)
  1519. if (MCI_CANNOT_AUTO_OPEN (wMessage))
  1520. {
  1521. // If the command needs an open device
  1522. if (!MCI_DO_NOT_NEED_OPEN (wMessage))
  1523. {
  1524. wErr = MCIERR_INVALID_DEVICE_NAME;
  1525. goto cleanup;
  1526. }
  1527. } else
  1528. // If auto open is legal try to open the device automatically
  1529. {
  1530. wErr = mciAutoOpenDevice (lpstrDeviceName, lpstrCommandStart,
  1531. lpstrReturnString, wReturnLength);
  1532. goto cleanup;
  1533. }
  1534. //
  1535. // Parse the command parameters
  1536. //
  1537. // Allocate command parameter block
  1538. if ((lpdwParams = (LPDWORD)mciAlloc (sizeof(DWORD) * MCI_MAX_PARAM_SLOTS))
  1539. == NULL)
  1540. {
  1541. wErr = MCIERR_OUT_OF_MEMORY;
  1542. goto cleanup;
  1543. }
  1544. wErr = mciParseParams (lpstrCommand, lpCommandItem,
  1545. &dwFlags,
  1546. (LPSTR)lpdwParams,
  1547. MCI_MAX_PARAM_SLOTS * sizeof(DWORD),
  1548. &lpstrPointerList, &wParsingError);
  1549. if (wErr != 0)
  1550. goto cleanup;
  1551. // The 'new' device keyword requires an alias
  1552. if (bNewDevice && !(dwFlags & MCI_OPEN_ALIAS))
  1553. {
  1554. wErr = MCIERR_NEW_REQUIRES_ALIAS;
  1555. goto cleanup;
  1556. }
  1557. // Parsed OK so execute command
  1558. // Special processing for the MCI_OPEN message's parameters
  1559. if (wMessage == MCI_OPEN)
  1560. {
  1561. // Manually reference the device type and device element
  1562. if (dwFlags & MCI_OPEN_TYPE)
  1563. {
  1564. // The type name was specified explicitly as a parameter
  1565. // so the given device name is the element name
  1566. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrElementName
  1567. = (LPSTR)lpstrDeviceName;
  1568. dwFlags |= MCI_OPEN_ELEMENT;
  1569. } else
  1570. {
  1571. // A type must be explicitly specified when "new" is used
  1572. if (bNewDevice)
  1573. {
  1574. wErr = MCIERR_INVALID_DEVICE_NAME;
  1575. goto cleanup;
  1576. }
  1577. // The device type is the given device name.
  1578. // There is no element name
  1579. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrDeviceType
  1580. = (LPSTR)lpstrDeviceName;
  1581. ((LPMCI_OPEN_PARMS)lpdwParams)->lpstrElementName = NULL;
  1582. dwFlags |= MCI_OPEN_TYPE;
  1583. }
  1584. }
  1585. else if (wMessage == MCI_SOUND && *lpstrDeviceName != '\0')
  1586. {
  1587. // Kludge the sound name for SOUND
  1588. //!! mciToLower (lpstrDeviceName);
  1589. if (lstrcmpi (lpstrDeviceName, szNotify) == 0)
  1590. dwFlags |= MCI_NOTIFY;
  1591. else if (lstrcmpi (lpstrDeviceName, szWait) == 0)
  1592. dwFlags |= MCI_WAIT;
  1593. else
  1594. {
  1595. ((LPMCI_SOUND_PARMS)lpdwParams)->lpstrSoundName = lpstrDeviceName;
  1596. dwFlags |= MCI_SOUND_NAME;
  1597. }
  1598. }
  1599. // Figure out what kind of return value to expect
  1600. // Initialize flag
  1601. wConvertReturnValue = 0;
  1602. // Skip past header
  1603. wLen = mciEatCommandEntry (lpCommandItem, NULL, NULL);
  1604. // Get return value (if any)
  1605. mciEatCommandEntry (lpCommandItem + wLen, &dwRetType, &wID);
  1606. if (wID == MCI_RETURN)
  1607. {
  1608. // There is a return value
  1609. if (wDeviceID == MCI_ALL_DEVICE_ID && wMessage != MCI_SYSINFO)
  1610. {
  1611. wErr = MCIERR_CANNOT_USE_ALL;
  1612. goto cleanup;
  1613. }
  1614. switch ((UINT)dwRetType)
  1615. {
  1616. case MCI_STRING:
  1617. // The return value is a string, point output
  1618. // buffer to user's buffer
  1619. lpdwParams[1] = (DWORD)lpstrReturnString;
  1620. lpdwParams[2] = (DWORD)wReturnLength;
  1621. break;
  1622. case MCI_INTEGER:
  1623. // The return value is an integer, flag to convert it
  1624. // to a string later
  1625. wConvertReturnValue = MCI_INTEGER;
  1626. break;
  1627. case MCI_RECT:
  1628. // The return value is an rect, flag to
  1629. // convert it to a string later
  1630. wConvertReturnValue = MCI_RECT;
  1631. break;
  1632. #ifdef DEBUG
  1633. default:
  1634. DOUT ("mciSendStringInternal: Unknown return type\r\n");
  1635. break;
  1636. #endif
  1637. }
  1638. }
  1639. // We don't need this around anymore
  1640. mciUnlockCommandTable (wTable);
  1641. wTable = (UINT)-1;
  1642. /* Fill the callback entry */
  1643. lpdwParams[0] = (DWORD)(UINT)hwndCallback;
  1644. // Kludge the type number for SYSINFO
  1645. if (wMessage == MCI_SYSINFO)
  1646. ((LPMCI_SYSINFO_PARMS)lpdwParams)->wDeviceType = mciLookUpType(lpstrDeviceName);
  1647. // Now we actually send the command further into the bowels of MCI!
  1648. // The INTERNAL version of mciSendCommand is used in order to get
  1649. // special return description information encoded in the high word
  1650. // of the return value and to get back the list of pointers allocated
  1651. // by any parsing done in the open command
  1652. {
  1653. MCI_INTERNAL_OPEN_INFO OpenInfo;
  1654. OpenInfo.lpstrParams = (LPSTR)lpstrCommand;
  1655. OpenInfo.lpstrPointerList = lpstrPointerList;
  1656. OpenInfo.hCallingTask = hCallingTask;
  1657. OpenInfo.wParsingError = wParsingError;
  1658. dwErr = mciSendCommandInternal (wDeviceID, wMessage, dwFlags,
  1659. (DWORD)(LPDWORD)lpdwParams,
  1660. &OpenInfo);
  1661. // If the command was reparsed there may be a new pointer list
  1662. // and the old one was free'd
  1663. lpstrPointerList = OpenInfo.lpstrPointerList;
  1664. }
  1665. wErr = (UINT)dwErr;
  1666. if (wErr != 0)
  1667. // If command execution error
  1668. goto cleanup;
  1669. // Command executed OK
  1670. // See if a string return came back with an integer instead
  1671. if (dwErr & MCI_INTEGER_RETURNED)
  1672. wConvertReturnValue = MCI_INTEGER;
  1673. // If the return value must be converted
  1674. if (wConvertReturnValue != 0 && wReturnLength != 0)
  1675. wErr = mciConvertReturnValue (wConvertReturnValue, HIWORD(dwErr),
  1676. wDeviceID, lpdwParams,
  1677. lpstrReturnString, wReturnLength);
  1678. cleanup:
  1679. if (wTable != -1)
  1680. mciUnlockCommandTable (wTable);
  1681. mciFree(lpstrCommandName);
  1682. mciFree(lpstrDeviceName);
  1683. if (lpdwParams != NULL)
  1684. mciFree (lpdwParams);
  1685. // Free any memory used by string parameters
  1686. mciParserFree (lpstrPointerList);
  1687. dwReturn = (wErr >= MCIERR_CUSTOM_DRIVER_BASE ?
  1688. (DWORD)wErr | (DWORD)wDeviceID << 16 :
  1689. (DWORD)wErr);
  1690. #ifdef DEBUG
  1691. if (dwReturn != 0)
  1692. {
  1693. char strTemp[MAXERRORLENGTH];
  1694. if (!mciGetErrorString (dwReturn, strTemp, sizeof(strTemp)))
  1695. LoadString(ghInst, STR_MCISSERRTXT, strTemp, sizeof(strTemp));
  1696. else
  1697. DPRINTF(("mciSendString: %ls\r\n",(LPSTR)strTemp));
  1698. }
  1699. #endif
  1700. exitfn:
  1701. if (lpstrInputCopy != NULL)
  1702. mciFree (lpstrInputCopy);
  1703. #ifdef DEBUG
  1704. mciCheckLocks();
  1705. #endif
  1706. return dwReturn;
  1707. }
  1708. /*
  1709. * @doc EXTERNAL MCI
  1710. *
  1711. * @api DWORD | mciSendString | This function sends a command string to an
  1712. * MCI device. The device that the command is sent to is specified in the
  1713. * command string.
  1714. *
  1715. * @parm LPCSTR | lpstrCommand | Specifies an MCI command string.
  1716. *
  1717. * @parm LPSTR | lpstrReturnString | Specifies a buffer for return
  1718. * information. If no return information is needed, you can specify
  1719. * NUL for this parameter.
  1720. *
  1721. * @parm UINT | wReturnLength | Specifies the size of the return buffer
  1722. * specified by <p lpstrReturnString>.
  1723. *
  1724. * @parm HWND | hwndCallback | Specifies a handle to a window to call back
  1725. * if "notify" was specified in the command string.
  1726. *
  1727. * @rdesc Returns zero if the function was successful. Otherwise, it returns
  1728. * error information. The low-order word
  1729. * of the returned DWORD contains the error return value.
  1730. *
  1731. * To get a textual description of <f mciSendString> return values,
  1732. * pass the return value to <f mciGetErrorString>.
  1733. *
  1734. * The error returns listed for <f mciSendCommand> also apply to
  1735. * <f mciSendString>. The following error returns are unique to
  1736. * <f mciSendString>:
  1737. *
  1738. * @flag MCIERR_BAD_CONSTANT | Unknown value for parameter.
  1739. *
  1740. * @flag MCIERR_BAD_INTEGER | Invalid or missing integer in command.
  1741. *
  1742. * @flag MCIERR_DUPLICATE_FLAGS | A flag or value was specified twice.
  1743. *
  1744. * @flag MCIERR_MISSING_COMMAND_STRING | No command was specified.
  1745. *
  1746. * @flag MCIERR_MISSING_DEVICE_NAME | No device name was specified.
  1747. *
  1748. * @flag MCIERR_MISSING_STRING_ARGUMENT | A string value was
  1749. * missing from the command.
  1750. *
  1751. * @flag MCIERR_NEW_REQUIRES_ALIAS | An alias must be used
  1752. * with the "new" device name.
  1753. *
  1754. * @flag MCIERR_NO_CLOSING_QUOTE | A closing quotation mark is missing.
  1755. *
  1756. * @flag MCIERR_NOTIFY_ON_AUTO_OPEN | The "notify" flag is illegal
  1757. * with auto-open.
  1758. *
  1759. * @flag MCIERR_PARAM_OVERFLOW | The output string was not long enough.
  1760. *
  1761. * @flag MCIERR_PARSER_INTERNAL | Internal parser error.
  1762. *
  1763. * @flag MCIERR_UNRECOGNIZED_KEYWORD | Unknown command parameter.
  1764. *
  1765. * @xref mciGetErrorString mciSendCommand
  1766. */
  1767. DWORD WINAPI
  1768. mciSendString(
  1769. LPCSTR lpstrCommand,
  1770. LPSTR lpstrReturnString,
  1771. UINT wReturnLength,
  1772. HWND hwndCallback
  1773. )
  1774. {
  1775. DWORD dwErr32;
  1776. DWORD dwErr16 = MMSYSERR_NOERROR;
  1777. LPSTR lpstr;
  1778. BOOL fHaveAll = FALSE;
  1779. // Initialize the 16-bit device list
  1780. if (!MCI_bDeviceListInitialized && !mciInitDeviceList()) {
  1781. return MCIERR_OUT_OF_MEMORY;
  1782. }
  1783. dwErr32 = mciMessage( THUNK_MCI_SENDSTRING, (DWORD)lpstrCommand,
  1784. (DWORD)lpstrReturnString, (DWORD)wReturnLength,
  1785. (DWORD)hwndCallback );
  1786. /*
  1787. ** Even if the string was processed correctly by the 32 bit side
  1788. ** we might still have to pass it through to the 16 bit side if it
  1789. ** contains the string " all\0" or " all ".
  1790. */
  1791. lpstr = _fstrstr( lpstrCommand, " all" );
  1792. if ( lpstr ) {
  1793. lpstr += 4;
  1794. if ( *lpstr == ' ' || *lpstr == '\0' ) {
  1795. fHaveAll = TRUE;
  1796. }
  1797. }
  1798. /*
  1799. ** If we have the all device or an error from the 32 bit side
  1800. ** we have to try the 16 bit side.
  1801. */
  1802. if ( !fHaveAll && dwErr32 == MMSYSERR_NOERROR ) {
  1803. return dwErr32;
  1804. }
  1805. else {
  1806. dwErr16 = mciSendStringInternal( lpstrCommand, lpstrReturnString,
  1807. wReturnLength, hwndCallback, NULL );
  1808. }
  1809. /*
  1810. ** Special processing of the return code is required if the
  1811. ** MCI_ALL_DEVICE_ID was specified.
  1812. */
  1813. if ( fHaveAll ) {
  1814. if ( (dwErr16 != MMSYSERR_NOERROR)
  1815. && (dwErr32 != MMSYSERR_NOERROR) ) {
  1816. return dwErr32;
  1817. }
  1818. else {
  1819. return MMSYSERR_NOERROR;
  1820. }
  1821. }
  1822. if ( dwErr32 != MCIERR_INVALID_DEVICE_NAME
  1823. && dwErr16 != MMSYSERR_NOERROR ) {
  1824. return dwErr32;
  1825. }
  1826. return dwErr16;
  1827. }
  1828. /*
  1829. * @doc INTERNAL MCI
  1830. *
  1831. * @api BOOL | mciExecute | This function is a simplified version of the
  1832. * <f mciSendString> function. It does not take a buffer for
  1833. * return information, and it displays a message box when errors occur.
  1834. *
  1835. * @parm LPCSTR | lpstrCommand | Specifies an MCI command string.
  1836. *
  1837. * @rdesc TRUE if successful, FALSE if unsuccessful.
  1838. *
  1839. * @comm This function provides a simple interface to MCI from scripting
  1840. * languages.
  1841. *
  1842. * @xref mciSendString
  1843. */
  1844. BOOL WINAPI
  1845. mciExecute(
  1846. LPCSTR lpstrCommand
  1847. )
  1848. {
  1849. char aszError[MAXERRORLENGTH];
  1850. DWORD dwErr;
  1851. LPSTR lpstrName;
  1852. if (LOWORD(dwErr = mciSendString (lpstrCommand, NULL, 0, NULL)) == 0)
  1853. return TRUE;
  1854. if (!mciGetErrorString (dwErr, aszError, sizeof(aszError)))
  1855. LoadString(ghInst, STR_MCIUNKNOWN, aszError, sizeof(aszError));
  1856. else
  1857. if (lpstrCommand != NULL)
  1858. {
  1859. // Skip initial blanks
  1860. while (*lpstrCommand == ' ')
  1861. ++lpstrCommand;
  1862. // Then skip the command
  1863. while (*lpstrCommand != ' ' && *lpstrCommand != '\0')
  1864. ++lpstrCommand;
  1865. // Then blanks before the device name
  1866. while (*lpstrCommand == ' ')
  1867. ++lpstrCommand;
  1868. // Now, get the device name
  1869. if (lpstrCommand != '\0' &&
  1870. mciEatToken (&lpstrCommand, ' ', &lpstrName, FALSE) != 0)
  1871. DOUT ("Could not allocate device name text for error box\r\n");
  1872. } else
  1873. lpstrName = NULL;
  1874. MessageBox (NULL, aszError, lpstrName, MB_ICONHAND | MB_OK);
  1875. if (lpstrName != NULL)
  1876. mciFree(lpstrName);
  1877. return FALSE;
  1878. }
  1879. /*
  1880. * @doc EXTERNAL MCI
  1881. *
  1882. * @api BOOL | mciGetErrorString | This function returns a
  1883. * textual description of the specified MCI error.
  1884. *
  1885. * @parm DWORD | dwError | Specifies the error code returned by
  1886. * <f mciSendCommand> or <f mciSendString>.
  1887. *
  1888. * @parm LPSTR | lpstrBuffer | Specifies a pointer to a buffer that is
  1889. * filled with a textual description of the specified error.
  1890. *
  1891. * @parm UINT | wLength | Specifies the length of the buffer pointed to by
  1892. * <p lpstrBuffer>.
  1893. *
  1894. * @rdesc Returns TRUE if successful. Otherwise, the given error code
  1895. * was not known.
  1896. */
  1897. BOOL WINAPI
  1898. mciGetErrorString (
  1899. DWORD dwError,
  1900. LPSTR lpstrBuffer,
  1901. UINT wLength
  1902. )
  1903. {
  1904. HINSTANCE hInst;
  1905. if (lpstrBuffer == NULL)
  1906. return FALSE;
  1907. if ( mciMessage( THUNK_MCI_GETERRORSTRING, (DWORD)dwError,
  1908. (DWORD)lpstrBuffer, (DWORD)wLength, 0L ) ) {
  1909. return TRUE;
  1910. }
  1911. // If the high bit is set then get the error string from the driver
  1912. // else get it from mmsystem.dll
  1913. if (HIWORD(dwError) != 0)
  1914. {
  1915. if (!MCI_VALID_DEVICE_ID (HIWORD (dwError)) || !(hInst = MCI_lpDeviceList[HIWORD (dwError)]->hDriver))
  1916. {
  1917. hInst = ghInst;
  1918. dwError = MCIERR_DRIVER;
  1919. }
  1920. } else
  1921. hInst = ghInst;
  1922. if (LoadString (hInst, LOWORD(dwError), lpstrBuffer, wLength) == 0)
  1923. {
  1924. // If the string load failed then at least terminate the string
  1925. if (wLength > 0)
  1926. *lpstrBuffer = '\0';
  1927. return FALSE;
  1928. }
  1929. else
  1930. return TRUE;
  1931. }
  1932. #if 0
  1933. /* Return non-zero if load successful */
  1934. BOOL NEAR PASCAL MCIInit(void)
  1935. {
  1936. mci32Message = (LPMCIMESSAGE)GetProcAddress32W( mmwow32Lib,
  1937. "mci32Message" );
  1938. return TRUE;
  1939. }
  1940. #endif
  1941. void NEAR PASCAL
  1942. MCITerminate(
  1943. void
  1944. )
  1945. {
  1946. /*
  1947. We would like to close all open devices here but cannot because of
  1948. unknown WEP order
  1949. */
  1950. if (hMciHeap != NULL)
  1951. HeapDestroy(hMciHeap);
  1952. hMciHeap = NULL;
  1953. }