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.

3078 lines
98 KiB

  1. /*******************************************************************************
  2. *
  3. * Module Name: mci.c
  4. *
  5. * Media Control Architecture Driver Interface
  6. *
  7. * Contents: MCI external message API's mciSendString and mciSendCommand
  8. * Author: DLL (DavidLe)
  9. * Created: 2/13/90
  10. * 5/22/91: Ported to Win32 - NigelT
  11. *
  12. * Copyright (c) 1991-1998 Microsoft Corporation
  13. *
  14. \******************************************************************************/
  15. #define INCL_WINMM
  16. #include "winmmi.h"
  17. #include "mci.h"
  18. #include "wchar.h"
  19. /*
  20. * MCI critical section stuff
  21. */
  22. #if DBG
  23. UINT cmciCritSec = 0; // enter'ed count
  24. UINT uCritSecOwner; // Thread id of critical section owner
  25. #endif
  26. CRITICAL_SECTION mciCritSec; // used to protect process global mci variables
  27. #if DBG
  28. int mciDebugLevel;
  29. #endif
  30. extern DWORD mciWindowThreadId;
  31. #define MCIERR_AUTO_ALREADY_CLOSED ((MCIERROR)0xFF000000) // Secret return code
  32. STATICFN UINT mciConvertReturnValue(
  33. UINT uType, UINT uErr, MCIDEVICEID wDeviceID,
  34. PDWORD_PTR dwParams, LPWSTR lpstrReturnString,
  35. UINT uReturnLength);
  36. STATICFN DWORD mciSendStringInternal(
  37. LPCWSTR lpstrCommand, LPWSTR lpstrReturnString, UINT uReturnLength,
  38. HANDLE hCallback, LPMCI_SYSTEM_MESSAGE lpMessage);
  39. STATICFN DWORD mciSendSystemString(
  40. LPCWSTR lpstrCommand, DWORD dwAdditionalFlags, LPWSTR lpstrReturnString,
  41. UINT uReturnLength);
  42. UINT mciBreakKeyYieldProc ( MCIDEVICEID wDeviceID,
  43. DWORD dwYieldData);
  44. extern UINT FAR mciExtractTypeFromID(
  45. LPMCI_OPEN_PARMSW lpOpen);
  46. // This macro defines the list of messages for which mciSendString
  47. // will not try to auto-open
  48. #define MCI_CANNOT_AUTO_OPEN(wMessage) \
  49. (wMessage == MCI_OPEN || wMessage == MCI_SYSINFO \
  50. || wMessage == MCI_SOUND || wMessage == MCI_CLOSE \
  51. || wMessage == MCI_BREAK)
  52. // This macro devices the list of message which do not require an open
  53. // device. It is a subset of MCI_CANNOT_AUTO_OPEN
  54. #define MCI_DO_NOT_NEED_OPEN(wMessage) \
  55. (wMessage == MCI_OPEN || wMessage == MCI_SOUND || wMessage == MCI_SYSINFO)
  56. // Strings used in mciAutoOpenDevice
  57. WSZCODE wszOpen[] = L"open";
  58. STATICDT WSZCODE wszClose[] = L"close";
  59. STATICDT WSZCODE wszNotify[] = L"notify"; // IMPORTANT: MUST be lowercase
  60. STATICDT WSZCODE wszWait[] = L"wait";
  61. STATICDT WSZCODE szCmdFormat[] = L"%ls %ls";
  62. STATICDT WSZCODE szLongFormat[] = L"%ld";
  63. STATICDT WSZCODE szRectFormat[] = L"%d %d %d %d";
  64. // Special device name
  65. STATICDT WSZCODE wszNew[] = L"new";
  66. /*****************************************************************************
  67. * @doc INTERNAL
  68. *
  69. * @func void | MciNotify | called by mmWndProc when it recives a
  70. * MM_MCINOTIFY message
  71. * @rdesc None.
  72. *
  73. ****************************************************************************/
  74. void MciNotify(
  75. DWORD wParam,
  76. LONG lParam)
  77. {
  78. //
  79. // wParam is the notify status
  80. // lParam is the MCI device id
  81. //
  82. mciEnter("MciNotify");
  83. if (MCI_VALID_DEVICE_ID((UINT)lParam) // If a valid device
  84. && !(ISCLOSING(MCI_lpDeviceList[lParam]))) { // and if not in process of closing
  85. SETAUTOCLOSING(MCI_lpDeviceList[lParam]);
  86. //
  87. // Must not hold MCI critical section when calling mciCloseDevice
  88. // because DrvClose gets the load/unload critical section while
  89. // drivers loading will have the load/unload critical section
  90. // but can call back (eg) to mciRegisterCommandTable causing
  91. // a deadlock.
  92. //
  93. // Even if the incoming notification is ABORTED/SUPERSEDED/FAILED
  94. // we must still close the device. Otherwise devices get left open.
  95. // mciCloseDevice will protect against trying to close a device that
  96. // we do not own.
  97. mciLeave("MciNotify");
  98. mciCloseDevice ((MCIDEVICEID)lParam, 0L, NULL, TRUE);
  99. } else {
  100. mciLeave("MciNotify");
  101. }
  102. }
  103. /*--------------------------------------------------------------------*\
  104. * HandleNotify
  105. *
  106. \*--------------------------------------------------------------------*/
  107. STATICFN void HandleNotify(
  108. DWORD uErr,
  109. MCIDEVICEID wDeviceID,
  110. DWORD dwFlags,
  111. DWORD_PTR dwParam2)
  112. {
  113. LPMCI_GENERIC_PARMS lpGeneric = (LPMCI_GENERIC_PARMS)dwParam2;
  114. HANDLE hCallback;
  115. if (0 == uErr
  116. && dwFlags & MCI_NOTIFY
  117. && lpGeneric != NULL
  118. && (hCallback = (HANDLE)lpGeneric->dwCallback) != NULL)
  119. {
  120. mciDriverNotify (hCallback, wDeviceID, MCI_NOTIFY_SUCCESSFUL);
  121. }
  122. }
  123. #if DBG
  124. /*--------------------------------------------------------------------*\
  125. * mciDebugOut
  126. *
  127. * Dump the string form of an MCI command
  128. \*--------------------------------------------------------------------*/
  129. UINT NEAR mciDebugOut(
  130. MCIDEVICEID wDeviceID,
  131. UINT wMessage,
  132. DWORD_PTR dwFlags,
  133. DWORD_PTR dwParam2,
  134. LPMCI_DEVICE_NODE nodeWorking)
  135. {
  136. LPWSTR lpCommand, lpFirstParameter, lpPrevious, lszDebugOut;
  137. WCHAR strTemp[256];
  138. UINT wID;
  139. UINT wOffset, wOffsetFirstParameter;
  140. UINT uReturnType = 0;
  141. DWORD dwValue;
  142. DWORD dwMask = 1; // used to test each flag bit in turn
  143. UINT wTable;
  144. // Find the command table for the given command message ID
  145. lpCommand = FindCommandItem( wDeviceID, NULL, (LPWSTR)(UINT_PTR)wMessage,
  146. NULL, &wTable );
  147. if (lpCommand == NULL)
  148. {
  149. if (wMessage != MCI_OPEN_DRIVER && wMessage != MCI_CLOSE_DRIVER) {
  150. ROUT(("WINMM: mciDebugOut: Command table not found"));
  151. }
  152. return 0;
  153. }
  154. lszDebugOut = mciAlloc( BYTE_GIVEN_CHAR( 512 ) );
  155. if (!lszDebugOut) {
  156. ROUT(("WINMM: Not enough memory to display command"));
  157. return 0;
  158. }
  159. // Dump the command name into the buffer
  160. wsprintfW( lszDebugOut, L"MCI command: \"%ls", lpCommand );
  161. // Dump the device name
  162. if (wDeviceID == MCI_ALL_DEVICE_ID)
  163. {
  164. wcscat( lszDebugOut, L" all" );
  165. }
  166. else if (nodeWorking != NULL)
  167. {
  168. if (nodeWorking->dwMCIOpenFlags & MCI_OPEN_ELEMENT_ID)
  169. {
  170. wsprintfW( lszDebugOut + wcslen( lszDebugOut ),
  171. L" Element ID:0x%lx", nodeWorking->dwElementID );
  172. }
  173. else if (nodeWorking->lpstrName != NULL)
  174. {
  175. wsprintfW( lszDebugOut + wcslen( lszDebugOut ),
  176. L" %ls", nodeWorking->lpstrName );
  177. }
  178. }
  179. // Skip past command entry
  180. lpCommand = (LPWSTR)((LPBYTE)lpCommand + mciEatCommandEntry( lpCommand, NULL, NULL));
  181. // Get the next entry
  182. lpFirstParameter = lpCommand;
  183. // Skip past the DWORD return value
  184. wOffsetFirstParameter = 4;
  185. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  186. mciEatCommandEntry( lpCommand,
  187. &dwValue, &wID ));
  188. // If it is a return value, skip it
  189. if (wID == MCI_RETURN)
  190. {
  191. uReturnType = (UINT)dwValue;
  192. lpFirstParameter = lpCommand;
  193. wOffsetFirstParameter += mciGetParamSize (dwValue, wID);
  194. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  195. mciEatCommandEntry(lpCommand,
  196. &dwValue, &wID));
  197. }
  198. // Dump device name parameter to OPEN
  199. if (wMessage == MCI_OPEN)
  200. {
  201. LPCWSTR lpstrDeviceType =
  202. ((LPMCI_OPEN_PARMSW)dwParam2)->lpstrDeviceType;
  203. LPCWSTR lpstrElementName =
  204. ((LPMCI_OPEN_PARMSW)dwParam2)->lpstrElementName;
  205. // Tack on device type
  206. if (dwFlags & MCI_OPEN_TYPE_ID)
  207. {
  208. // Warning!: Expanding dwOld to DWORD_PTR may not work on
  209. // on Win64, just to clear out warning. MCI may
  210. // not get ported to Win64.
  211. LPMCI_OPEN_PARMSW lpOpen = (LPMCI_OPEN_PARMSW)dwParam2;
  212. DWORD_PTR dwOld = PtrToUlong(lpOpen->lpstrDeviceType);
  213. if (mciExtractTypeFromID ((LPMCI_OPEN_PARMSW)dwParam2) != 0) {
  214. strTemp[0] = '\0';
  215. }
  216. wcscpy (strTemp, (LPWSTR)lpOpen->lpstrDeviceType);
  217. mciFree ((LPWSTR)lpOpen->lpstrDeviceType);
  218. lpOpen->lpstrDeviceType = (LPWSTR)dwOld;
  219. } else if (lpstrDeviceType != NULL)
  220. wcscpy (strTemp, (LPWSTR)lpstrDeviceType);
  221. else {
  222. strTemp[0] = '\0';
  223. }
  224. if (dwFlags & MCI_OPEN_ELEMENT_ID)
  225. {
  226. // Tack on element ID
  227. wcscat( strTemp, L" Element ID:");
  228. wsprintfW( strTemp + wcslen (strTemp), szLongFormat,
  229. LOWORD (PtrToUlong(lpstrDeviceType)));
  230. } else
  231. {
  232. // Add separator if both type name and element name are present
  233. if (lpstrDeviceType != 0 && lpstrElementName != 0) {
  234. wcscat( strTemp, L"!" );
  235. }
  236. if (lpstrElementName != 0 && dwFlags & MCI_OPEN_ELEMENT) {
  237. wcscat( strTemp, lpstrElementName );
  238. }
  239. }
  240. wsprintfW( lszDebugOut + wcslen(lszDebugOut), L" %ls", strTemp );
  241. }
  242. // Walk through each flag
  243. while (dwMask != 0)
  244. {
  245. // Is this bit set?
  246. if ((dwFlags & dwMask) != 0 && !
  247. // The MCI_OPEN_TYPE and MCI_OPEN_ELEMENT flags are taken care of
  248. // above
  249. (wMessage == MCI_OPEN && (dwMask == MCI_OPEN_TYPE
  250. || dwMask == MCI_OPEN_ELEMENT)))
  251. {
  252. lpPrevious = lpCommand = lpFirstParameter;
  253. wOffset = 0;
  254. lpCommand = (LPWSTR)((LPBYTE)lpCommand
  255. + mciEatCommandEntry( lpCommand, &dwValue, &wID ));
  256. // What parameter uses this bit?
  257. while (wID != MCI_END_COMMAND && dwValue != dwMask)
  258. {
  259. wOffset += mciGetParamSize( dwValue, wID);
  260. if (wID == MCI_CONSTANT) {
  261. while (wID != MCI_END_CONSTANT) {
  262. lpCommand = (LPWSTR)((LPBYTE)lpCommand
  263. + mciEatCommandEntry( lpCommand, NULL, &wID));
  264. }
  265. }
  266. lpPrevious = lpCommand;
  267. lpCommand = (LPWSTR)((LPBYTE)lpCommand
  268. + mciEatCommandEntry( lpCommand, &dwValue, &wID ));
  269. }
  270. if (wID != MCI_END_COMMAND)
  271. {
  272. // Found the parameter which matches this flag bit
  273. // Print the parameter name
  274. if (*lpPrevious) {
  275. wsprintfW( lszDebugOut + wcslen(lszDebugOut),
  276. L" %ls", lpPrevious);
  277. }
  278. // Print any argument
  279. switch (wID)
  280. {
  281. case MCI_STRING:
  282. wsprintfW( lszDebugOut + wcslen(lszDebugOut),
  283. L" %ls", *(LPWSTR *)( (LPBYTE)dwParam2
  284. + wOffset + wOffsetFirstParameter) );
  285. break;
  286. case MCI_CONSTANT:
  287. {
  288. DWORD dwConst = *(LPDWORD)((LPBYTE)dwParam2 + wOffset +
  289. wOffsetFirstParameter);
  290. UINT wLen;
  291. BOOL bFound = FALSE;
  292. while (wID != MCI_END_CONSTANT)
  293. {
  294. wLen = mciEatCommandEntry( lpCommand,
  295. &dwValue, &wID);
  296. if (dwValue == dwConst)
  297. {
  298. bFound = TRUE;
  299. wsprintfW( lszDebugOut + wcslen(lszDebugOut),
  300. L" %ls", lpCommand);
  301. }
  302. lpCommand = (LPWSTR)((LPBYTE)lpCommand + wLen);
  303. }
  304. if (bFound)
  305. break;
  306. // FALL THROUGH
  307. }
  308. case MCI_INTEGER:
  309. case MCI_HWND:
  310. case MCI_HPAL:
  311. case MCI_HDC:
  312. wsprintfW( strTemp, szLongFormat,
  313. *(LPDWORD)((LPBYTE)dwParam2 + wOffset +
  314. wOffsetFirstParameter));
  315. wsprintfW( lszDebugOut + wcslen(lszDebugOut),
  316. L" %ls", strTemp );
  317. break;
  318. }
  319. }
  320. }
  321. // Go to the next flag
  322. dwMask <<= 1;
  323. }
  324. mciUnlockCommandTable( wTable);
  325. wcscat(lszDebugOut, L"\"" );
  326. ROUTSW((lszDebugOut));
  327. mciFree(lszDebugOut);
  328. return uReturnType;
  329. }
  330. #endif
  331. DWORD mciBreak(
  332. MCIDEVICEID wDeviceID,
  333. DWORD dwFlags,
  334. LPMCI_BREAK_PARMS lpBreakon)
  335. {
  336. HWND hwnd;
  337. if (dwFlags & MCI_BREAK_KEY)
  338. {
  339. if (dwFlags & MCI_BREAK_OFF) {
  340. return MCIERR_FLAGS_NOT_COMPATIBLE;
  341. }
  342. if (dwFlags & MCI_BREAK_HWND) {
  343. hwnd = lpBreakon->hwndBreak;
  344. }
  345. else
  346. {
  347. hwnd = NULL;
  348. }
  349. return mciSetBreakKey (wDeviceID, lpBreakon->nVirtKey,
  350. hwnd)
  351. ? 0 : MMSYSERR_INVALPARAM;
  352. } else if (dwFlags & MCI_BREAK_OFF) {
  353. mciSetYieldProc (wDeviceID, NULL, 0);
  354. return 0;
  355. } else {
  356. return MCIERR_MISSING_PARAMETER;
  357. }
  358. }
  359. //***********************************************************************
  360. // mciAutoCloseDevice
  361. //
  362. // Close the indicated device by sending a message inter-task
  363. //***********************************************************************
  364. STATICFN DWORD mciAutoCloseDevice(
  365. LPCWSTR lpstrDevice)
  366. {
  367. LPWSTR lpstrCommand;
  368. DWORD dwRet;
  369. int alloc_len = BYTE_GIVEN_CHAR( wcslen( lpstrDevice) ) +
  370. sizeof(wszClose) + sizeof(WCHAR);
  371. if ((lpstrCommand = mciAlloc ( alloc_len ) ) == NULL)
  372. return MCIERR_OUT_OF_MEMORY;
  373. wsprintfW( lpstrCommand, szCmdFormat, wszClose, lpstrDevice);
  374. dwRet = mciSendSystemString( lpstrCommand, 0L, NULL, 0);
  375. mciFree( lpstrCommand);
  376. return dwRet;
  377. }
  378. //***********************************************************************
  379. // mciSendSingleCommand
  380. //
  381. // Process a single MCI command
  382. // Called by mciSendCommandInternal
  383. //
  384. //***********************************************************************
  385. DWORD NEAR mciSendSingleCommand(
  386. MCIDEVICEID wDeviceID,
  387. UINT wMessage,
  388. DWORD_PTR dwParam1,
  389. DWORD_PTR dwParam2,
  390. LPMCI_DEVICE_NODE nodeWorking,
  391. BOOL bWalkAll,
  392. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
  393. {
  394. DWORD dwRet;
  395. #if DBG
  396. UINT uReturnType;
  397. if (mciDebugLevel != 0)
  398. uReturnType = mciDebugOut( wDeviceID, wMessage, dwParam1, dwParam2,
  399. nodeWorking);
  400. if (nodeWorking == NULL && !MCI_DO_NOT_NEED_OPEN (wMessage))
  401. return MCIERR_INTERNAL;
  402. #endif
  403. switch (wMessage)
  404. {
  405. case MCI_OPEN:
  406. dwRet = mciOpenDevice ((DWORD)dwParam1,
  407. (LPMCI_OPEN_PARMSW)dwParam2, lpOpenInfo);
  408. break;
  409. case MCI_CLOSE:
  410. // If we were walking the device list and this device was auto opened
  411. // send the command via a task switch
  412. // If we just called mciCloseDevice (as sometimes happened before a bug
  413. // was fixed mciCloseDevice will unload the driver but the MCI_CLOSE_DRIVER
  414. // command will not get sent because it will be rejected as coming from the
  415. // wrong task. The result would be (was) that the driver would access violate
  416. // when it next did something.
  417. if (GetCurrentTask() != nodeWorking->hCreatorTask)
  418. {
  419. LPWSTR lpstrCommand;
  420. if (!bWalkAll) {
  421. //
  422. // Only valid to close an auto-opened device if it's
  423. // being close as part of closing MCI_ALL_DEVICE_ID
  424. // We can reach here if an app 'guesses' an MCI device
  425. // id and tries to close it while playing.
  426. //
  427. dwRet = MCIERR_ILLEGAL_FOR_AUTO_OPEN;
  428. break;
  429. }
  430. lpstrCommand = mciAlloc( sizeof(wszClose)+ sizeof(WCHAR) +
  431. BYTE_GIVEN_CHAR( wcslen( nodeWorking->lpstrName ) ) );
  432. if ( lpstrCommand == NULL )
  433. return MCIERR_OUT_OF_MEMORY;
  434. wcscpy( lpstrCommand, wszClose);
  435. wcscat( lpstrCommand, L" ");
  436. wcscat( lpstrCommand, nodeWorking->lpstrName);
  437. dwRet = mciSendSystemString( lpstrCommand, 0L, NULL, 0);
  438. mciFree( lpstrCommand);
  439. } else
  440. dwRet = mciCloseDevice( wDeviceID, (DWORD)dwParam1,
  441. (LPMCI_GENERIC_PARMS)dwParam2, TRUE);
  442. break;
  443. case MCI_SYSINFO:
  444. dwRet = mciSysinfo( wDeviceID, (DWORD)dwParam1,
  445. (LPMCI_SYSINFO_PARMSW)dwParam2);
  446. HandleNotify( dwRet, wDeviceID, (DWORD)dwParam1, dwParam2);
  447. break;
  448. case MCI_BREAK:
  449. dwRet = mciBreak( wDeviceID, (DWORD)dwParam1,
  450. (LPMCI_BREAK_PARMS)dwParam2);
  451. HandleNotify( dwRet, wDeviceID, (DWORD)dwParam1, dwParam2);
  452. break;
  453. case MCI_SOUND:
  454. {
  455. LPMCI_SOUND_PARMSW lpSound = (LPMCI_SOUND_PARMSW)dwParam2;
  456. if ( PlaySoundW( MCI_SOUND_NAME & dwParam1
  457. ? lpSound->lpstrSoundName
  458. : L".Default",
  459. (HANDLE)0,
  460. dwParam1 & MCI_WAIT
  461. ? SND_SYNC | SND_ALIAS
  462. : SND_ASYNC | SND_ALIAS ) )
  463. {
  464. dwRet = 0;
  465. } else {
  466. dwRet = MCIERR_HARDWARE;
  467. }
  468. HandleNotify( dwRet, wDeviceID, (DWORD)dwParam1, dwParam2);
  469. break;
  470. }
  471. default:
  472. #if 0 // don't bother (NigelT)
  473. if (mciDebugLevel > 1)
  474. {
  475. dwStartTime = timeGetTime();
  476. }
  477. #endif
  478. // Initialize GetAsyncKeyState for break key
  479. {
  480. if ((dwParam1 & MCI_WAIT) &&
  481. nodeWorking->fpYieldProc == mciBreakKeyYieldProc)
  482. {
  483. dprintf4(("Getting initial state of Break key"));
  484. GetAsyncKeyState( nodeWorking->dwYieldData);
  485. //GetAsyncKeyState( LOWORD(nodeWorking->dwYieldData));
  486. }
  487. }
  488. dwRet = (DWORD)DrvSendMessage( nodeWorking->hDrvDriver, wMessage,
  489. dwParam1, dwParam2);
  490. break;
  491. } // switch
  492. #if DBG
  493. if (mciDebugLevel != 0)
  494. {
  495. if (dwRet & MCI_INTEGER_RETURNED)
  496. uReturnType = MCI_INTEGER;
  497. switch (uReturnType)
  498. {
  499. case MCI_INTEGER:
  500. {
  501. WCHAR strTemp[50];
  502. mciConvertReturnValue( uReturnType, HIWORD(dwRet), wDeviceID,
  503. (PDWORD_PTR)dwParam2, strTemp,
  504. CHAR_GIVEN_BYTE( sizeof(strTemp) ) );
  505. dprintf2((" returns: %ls", strTemp));
  506. break;
  507. }
  508. case MCI_STRING:
  509. dprintf2((" returns: %ls",(LPWSTR)(1 + (LPDWORD)dwParam2)));
  510. break;
  511. }
  512. }
  513. #endif
  514. return dwRet;
  515. }
  516. //***********************************************************************
  517. // mciSendCommandInternal
  518. //
  519. // Internal version of mciSendCommand. Differs ONLY in that the return
  520. // value is a DWORD where the high word has meaning only for mciSendString
  521. //
  522. //***********************************************************************
  523. STATICFN DWORD mciSendCommandInternal(
  524. MCIDEVICEID wDeviceID,
  525. UINT wMessage,
  526. DWORD_PTR dwParam1,
  527. DWORD_PTR dwParam2,
  528. LPMCI_INTERNAL_OPEN_INFO lpOpenInfo)
  529. {
  530. DWORD dwRetVal;
  531. LPMCI_DEVICE_NODE nodeWorking = NULL;
  532. BOOL bWalkAll;
  533. DWORD dwAllError = 0;
  534. HANDLE hCurrentTask;
  535. hCurrentTask = GetCurrentTask();
  536. // If the device is "all" and the message is *not*
  537. // "sysinfo" then we must walk all devices
  538. if (wDeviceID == MCI_ALL_DEVICE_ID
  539. && (wMessage != MCI_SYSINFO)
  540. && (wMessage != MCI_SOUND))
  541. {
  542. if (wMessage == MCI_OPEN)
  543. {
  544. dwRetVal = MCIERR_CANNOT_USE_ALL;
  545. goto exitfn;
  546. }
  547. bWalkAll = TRUE;
  548. // Start at device #1
  549. wDeviceID = 1;
  550. } else {
  551. bWalkAll = FALSE;
  552. }
  553. mciEnter("mciSendCommandInternal");
  554. // Walk through all devices if bWalkAll or just one device if !bWalkAll
  555. do
  556. {
  557. // Initialize
  558. dwRetVal = 0;
  559. // Validate the device ID if single device
  560. if (!bWalkAll)
  561. {
  562. if (!MCI_DO_NOT_NEED_OPEN(wMessage))
  563. {
  564. if (!MCI_VALID_DEVICE_ID(wDeviceID))
  565. {
  566. dwRetVal = MCIERR_INVALID_DEVICE_ID;
  567. goto exitfn;
  568. }
  569. nodeWorking = MCI_lpDeviceList[wDeviceID];
  570. }
  571. }
  572. else if (wMessage != MCI_SYSINFO)
  573. {
  574. nodeWorking = MCI_lpDeviceList[wDeviceID];
  575. }
  576. // Skip if walking the device list and the
  577. // device is not part of the current task
  578. if (bWalkAll)
  579. {
  580. if (nodeWorking == NULL ||
  581. nodeWorking->hOpeningTask != hCurrentTask)
  582. goto no_send;
  583. }
  584. // If the device is in the process of closing and the message
  585. // is not MCI_CLOSE_DEVICE then return an error
  586. if (nodeWorking != NULL &&
  587. ISCLOSING(nodeWorking) &&
  588. wMessage != MCI_CLOSE_DRIVER)
  589. {
  590. dwRetVal = MCIERR_DEVICE_LOCKED;
  591. goto exitfn;
  592. }
  593. // If this message is being sent from the wrong task (the device was auto-
  594. // opened) fail all but the MCI_CLOSE message which gets sent inter-task
  595. if (nodeWorking != NULL &&
  596. nodeWorking->hCreatorTask != hCurrentTask)
  597. {
  598. if (wMessage != MCI_CLOSE)
  599. {
  600. dwRetVal = MCIERR_ILLEGAL_FOR_AUTO_OPEN;
  601. goto exitfn;
  602. }
  603. else
  604. {
  605. // Don't even allow close from mciSendCommand if auto-open device has a
  606. // pending close
  607. if (ISAUTOCLOSING(nodeWorking))
  608. {
  609. dwRetVal = MCIERR_DEVICE_LOCKED;
  610. goto exitfn;
  611. }
  612. }
  613. }
  614. mciLeave("mciSendCommandInternal");
  615. dwRetVal = mciSendSingleCommand( wDeviceID, wMessage, dwParam1,
  616. dwParam2, nodeWorking, bWalkAll,
  617. lpOpenInfo);
  618. mciEnter("mciSendCommandInternal");
  619. no_send:
  620. // If we are processing multiple devices
  621. if (bWalkAll)
  622. {
  623. // If there was an error for this device
  624. if (dwRetVal != 0)
  625. {
  626. // If this is not the first error
  627. if (dwAllError != 0) {
  628. dwAllError = MCIERR_MULTIPLE;
  629. // Just one error so far
  630. } else {
  631. dwAllError = dwRetVal;
  632. }
  633. }
  634. }
  635. } while (bWalkAll && ++wDeviceID < MCI_wNextDeviceID);
  636. exitfn:;
  637. mciLeave("mciSendCommandInternal");
  638. return dwAllError == MCIERR_MULTIPLE ? dwAllError : dwRetVal;
  639. }
  640. /************************************************************************
  641. * @doc EXTERNAL MCI
  642. *
  643. * @api DWORD | mciSendCommand | This function sends a command message to
  644. * the specified MCI device.
  645. *
  646. * @parm MCIDEVICEID | wDeviceID | Specifies the device ID of the MCI device
  647. * to receive the command. This parameter is
  648. * not used with the <m MCI_OPEN> command.
  649. *
  650. * @parm UINT | wMessage | Specifies the command message.
  651. *
  652. * @parm DWORD | dwParam1 | Specifies flags for the command.
  653. *
  654. * @parm DWORD | dwParam2 | Specifies a pointer to a parameter block
  655. * for the command.
  656. *
  657. * @rdesc Returns zero if the function was successful. Otherwise, it returns
  658. * error information. The low-order word
  659. * of the returned DWORD is the error return value. If the error is
  660. * device-specific, the high-order word contains the driver ID; otherwise
  661. * the high-order word is zero.
  662. *
  663. * To get a textual description of <f mciSendCommand> return values,
  664. * pass the return value to <f mciGetErrorString>.
  665. *
  666. * Error values that are returned when a device is being opened
  667. * are listed with the MCI_OPEN message. In addition to the
  668. * MCI_OPEN error returns, this function can
  669. * return the following values:
  670. *
  671. * @flag MCIERR_BAD_TIME_FORMAT | Illegal value for time format.
  672. *
  673. * @flag MCIERR_CANNOT_USE_ALL | The device name "all" is not allowed
  674. * for this command.
  675. *
  676. * @flag MCIERR_CREATEWINDOW | Could not create or use window.
  677. *
  678. * @flag MCIERR_DEVICE_LOCKED | The device is locked until it is
  679. * closed automatically.
  680. *
  681. * @flag MCIERR_DEVICE_NOT_READY | Device not ready.
  682. *
  683. * @flag MCIERR_DEVICE_TYPE_REQUIRED | The device name must be a valid
  684. * device type.
  685. *
  686. * @flag MCIERR_DRIVER | Unspecified device error.
  687. *
  688. * @flag MCIERR_DRIVER_INTERNAL | Internal driver error.
  689. *
  690. * @flag MCIERR_FILE_NOT_FOUND | Requested file not found.
  691. *
  692. * @flag MCIERR_FILE_NOT_SAVED | The file was not saved.
  693. *
  694. * @flag MCIERR_FILE_READ | A read from the file failed.
  695. *
  696. * @flag MCIERR_FILE_WRITE | A write to the file failed.
  697. *
  698. * @flag MCIERR_FLAGS_NOT_COMPATIBLE | Incompatible parameters
  699. * were specified.
  700. *
  701. * @flag MCIERR_HARDWARE | Hardware error on media device.
  702. *
  703. * @flag MCIERR_INTERNAL | Internal error.
  704. *
  705. * @flag MCIERR_INVALID_DEVICE_ID | Invalid device ID.
  706. *
  707. * @flag MCIERR_INVALID_DEVICE_NAME | The device is not open
  708. * or is not known.
  709. *
  710. * @flag MCIERR_INVALID_FILE | Invalid file format.
  711. *
  712. * @flag MCIERR_MULTIPLE | Errors occurred in more than one device.
  713. *
  714. * @flag MCIERR_NO_WINDOW | There is no display window.
  715. *
  716. * @flag MCIERR_NULL_PARAMETER_BLOCK | Parameter block pointer was NULL.
  717. *
  718. * @flag MCIERR_OUT_OF_MEMORY | Not enough memory for requested operation.
  719. *
  720. * @flag MCIERR_OUTOFRANGE | Parameter value out of range.
  721. *
  722. * @flag MCIERR_UNNAMED_RESOURCE | Attempt to save unnamed file.
  723. *
  724. * @flag MCIERR_UNRECOGNIZED_COMMAND | Unknown command.
  725. *
  726. * @flag MCIERR_UNSUPPORTED_FUNCTION | Action not available for this
  727. * device.
  728. *
  729. * The following additional return values are defined for MCI sequencers:
  730. *
  731. * @flag MCIERR_SEQ_DIV_INCOMPATIBLE | Set Song Pointer incompatible
  732. * with SMPTE files.
  733. *
  734. * @flag MCIERR_SEQ_PORT_INUSE | Specified port is in use.
  735. *
  736. * @flag MCIERR_SEQ_PORT_MAPNODEVICE | Current map uses non-existent
  737. * device.
  738. *
  739. * @flag MCIERR_SEQ_PORT_MISCERROR | Miscellaneous error with
  740. * specified port.
  741. *
  742. * @flag MCIERR_SEQ_PORT_NONEXISTENT | Specified port does not exist.
  743. *
  744. * @flag MCIERR_SEQ_PORTUNSPECIFIED | No current MIDI port.
  745. *
  746. * @flag MCIERR_SEQ_NOMIDIPRESENT | No MIDI ports present.
  747. *
  748. * @flag MCIERR_SEQ_TIMER | Timer error.
  749. *
  750. * The following additional return values are defined for MCI waveform
  751. * audio devices:
  752. *
  753. * @flag MCIERR_WAVE_INPUTSINUSE | No compatible waveform recording
  754. * device is free.
  755. *
  756. * @flag MCIERR_WAVE_INPUTSUNSUITABLE | No compatible waveform
  757. * recording devices.
  758. *
  759. * @flag MCIERR_WAVE_INPUTUNSPECIFIED | Any compatible waveform
  760. * recording device may be used.
  761. *
  762. * @flag MCIERR_WAVE_OUTPUTSINUSE | No compatible waveform playback
  763. * device is free.
  764. *
  765. * @flag MCIERR_WAVE_OUTPUTSUNSUITABLE | No compatible waveform
  766. * playback devices.
  767. *
  768. * @flag MCIERR_WAVE_OUTPUTUNSPECIFIED | Any compatible waveform
  769. * playback device may be used.
  770. *
  771. * @flag MCIERR_WAVE_SETINPUTINUSE | Set waveform recording device
  772. * is in use.
  773. *
  774. * @flag MCIERR_WAVE_SETINPUTUNSUITABLE | Set waveform recording
  775. * device is incompatible with set format.
  776. *
  777. * @flag MCIERR_WAVE_SETOUTPUTINUSE | Set waveform playback device
  778. * is in use.
  779. *
  780. * @flag MCIERR_WAVE_SETOUTPUTUNSUITABLE | Set waveform playback
  781. * device is incompatible with set format.
  782. *
  783. * @comm Use the <m MCI_OPEN> command to obtain the device ID
  784. * specified by <p wDeviceID>.
  785. *
  786. * @xref mciGetErrorString mciSendString
  787. */
  788. /*
  789. * @doc internal
  790. *
  791. * @api DWORD | mciDriverEntry | Actually a callback. The entry point for MCI drivers.
  792. *
  793. * @parm UINT | wMessage | Identifies the requested action to be performed.
  794. *
  795. * @parm DWORD | dwParam1 | Specifies data for this message. Defined separately
  796. * for each message.
  797. *
  798. * @parm DWORD | dwParam2 | Specifies data for this message. Defined separately
  799. * for each message.
  800. *
  801. * @rdesc The return value is defined separately for each message.
  802. */
  803. // WARNING!! Casting all pointer references to wMessage to UINT_PTR to
  804. // clear out warnings. Note: This will NOT WORK on Win64;
  805. // we'll have to change this prototype to get this to work
  806. // on Win64.
  807. DWORD mciSendCommandA(
  808. MCIDEVICEID wDeviceID,
  809. UINT wMessage,
  810. DWORD_PTR dwParam1,
  811. DWORD_PTR dwParam2)
  812. {
  813. LPCSTR lpStr1;
  814. LPCSTR lpStr2;
  815. LPCSTR lpStr3;
  816. DWORD dwRet;
  817. /*
  818. ** If dwParam1 is 0L, we have no information to perform the ascii
  819. ** to unicode thunks from. Therefore, I will pass the call straight
  820. ** thru to mciSendCommandW "as is".
  821. */
  822. if ( dwParam1 == 0L ) {
  823. return mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2 );
  824. }
  825. /*
  826. ** If we are still here we have some thunking to do.
  827. **
  828. **
  829. ** Basically this code is very similiar to the WOW thunk code.
  830. **
  831. ** We have to special case MCI_OPEN and MCI_SYSINFO because the
  832. ** command table is either not available or in an inconsistent state.
  833. **
  834. ** Otherwise, the code is identical to the WOW code. Maybe we could do
  835. ** unicode thunking in the WOW layer and then call mciSendCommandW.
  836. ** It seems bad that we should have to thunk poor old WOW apps twice!!
  837. ** they are slow enough as it is :-)
  838. **
  839. ** We have the advantage that all pointers are already 32 bit.
  840. **
  841. */
  842. switch ( wMessage ) {
  843. case MCI_CLOSE_DRIVER:
  844. dprintf3(( "MCI_CLOSE_DRIVER command" ));
  845. return mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2 );
  846. break;
  847. case MCI_OPEN_DRIVER:
  848. dprintf3(( "MCI_OPEN_DRIVER command" ));
  849. /* fall thru */
  850. case MCI_OPEN:
  851. {
  852. LPMCI_OPEN_PARMSW lpOpenP = (LPMCI_OPEN_PARMSW)dwParam2;
  853. #if DBG
  854. dprintf3(( "MCI_OPEN command" ));
  855. /*
  856. ** As of yet I don't know how to thunk command extensions
  857. ** for the open command.
  858. ** These may well contain strings but we have no way of
  859. ** knowing because we haven't got access to the command table.
  860. */
  861. if ( dwParam1 & 0xFFFF0000 ) {
  862. dprintf1(( "MCI_OPEN called with command extensions !!" ));
  863. }
  864. #endif
  865. /*
  866. ** First save the original ascii string pointers.
  867. ** Note that lpstrDeviceType may be a TYPE_ID
  868. ** Note that lpstrElementName may be a ELEMENT_ID
  869. */
  870. lpStr1 = (LPCSTR)lpOpenP->lpstrDeviceType;
  871. lpStr2 = (LPCSTR)lpOpenP->lpstrElementName;
  872. lpStr3 = (LPCSTR)lpOpenP->lpstrAlias;
  873. /*
  874. ** Now allocate a unicode copy of the ascii, don't try
  875. ** to copy NULL strings, or ID types
  876. **
  877. ** The first string to be copied is lpstrDeviceType.
  878. ** This pointer is only valid if the MCI_OPEN_TYPE bit
  879. ** is set and MCI_OPEN_TYPE_ID is not set. If either
  880. ** bit is set and lpstrDeviceType is NULL it is an
  881. ** error that will be picked up later.
  882. **
  883. ** The second string is lpstrElementName which is valid
  884. ** only with MCI_OPEN_ELEMENT set and MCI_OPEN_ELEMENT_ID
  885. ** not set. As in the case above it is an error if
  886. ** either bit is set but the pointer itself is NULL.
  887. **
  888. ** The third string is lpstrAlias which is valid only
  889. ** with MCI_OPEN_ALIAS set. In this case when this bit
  890. ** is set there is no modifying bit that changes the
  891. ** meaning of the pointer.
  892. **
  893. ** If an unicode string is not allocated the internal
  894. ** pointer is set to NULL. This value can be checked
  895. ** after the mciSendCommand call to see if the string
  896. ** has to be freed and the original pointer restored.
  897. */
  898. if ( lpStr1 ) {
  899. if ((dwParam1 & MCI_OPEN_TYPE)
  900. && !(dwParam1 & MCI_OPEN_TYPE_ID) ) {
  901. lpOpenP->lpstrDeviceType = AllocUnicodeStr( (LPSTR)lpStr1 );
  902. if ( lpOpenP->lpstrDeviceType == NULL ) {
  903. dwRet = MCIERR_OUT_OF_MEMORY;
  904. goto err1;
  905. }
  906. } else lpStr1 = NULL; // Nothing allocated, will free nothing
  907. }
  908. if ( lpStr2 ) {
  909. if ((dwParam1 & MCI_OPEN_ELEMENT)
  910. && !(dwParam1 & MCI_OPEN_ELEMENT_ID) ) {
  911. lpOpenP->lpstrElementName = AllocUnicodeStr( (LPSTR)lpStr2 );
  912. if ( lpOpenP->lpstrElementName == NULL ) {
  913. dwRet = MCIERR_OUT_OF_MEMORY;
  914. goto err2;
  915. }
  916. } else lpStr2 = NULL; // Nothing allocated, will free nothing
  917. }
  918. if ( lpStr3 ) {
  919. if (dwParam1 & MCI_OPEN_ALIAS) {
  920. lpOpenP->lpstrAlias = AllocUnicodeStr( (LPSTR)lpStr3 );
  921. if ( lpOpenP->lpstrAlias == NULL ) {
  922. dwRet = MCIERR_OUT_OF_MEMORY;
  923. goto err3;
  924. }
  925. } else lpStr3 = NULL; // Nothing allocated, will free nothing
  926. }
  927. /*
  928. ** Now call the unicode version
  929. */
  930. dwRet = mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2 );
  931. /*
  932. ** Free the unicode strings.
  933. ** and restore the original string pointers
  934. */
  935. if ( lpStr3 ) {
  936. FreeUnicodeStr( (LPWSTR)lpOpenP->lpstrAlias );
  937. err3: lpOpenP->lpstrAlias = (LPCWSTR)lpStr3;
  938. }
  939. if ( lpStr2 ) {
  940. FreeUnicodeStr( (LPWSTR)lpOpenP->lpstrElementName );
  941. err2: lpOpenP->lpstrElementName = (LPCWSTR)lpStr2;
  942. }
  943. if ( lpStr1 ) {
  944. FreeUnicodeStr( (LPWSTR)lpOpenP->lpstrDeviceType );
  945. err1: lpOpenP->lpstrDeviceType = (LPCWSTR)lpStr1;
  946. }
  947. return dwRet;
  948. }
  949. case MCI_SYSINFO:
  950. dprintf3(( "MCI_SYSINFO command" ));
  951. /*
  952. ** If we are returning a number forget about UNICODE,
  953. ** applies when (dwParam1 & MCI_SYSINFO_QUANTITY) is TRUE.
  954. */
  955. if ( dwParam1 & MCI_SYSINFO_QUANTITY ) {
  956. return mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2 );
  957. }
  958. else {
  959. LPMCI_SYSINFO_PARMSW lpInfoP = (LPMCI_SYSINFO_PARMSW)dwParam2;
  960. DWORD len = BYTE_GIVEN_CHAR( lpInfoP->dwRetSize );
  961. /*
  962. ** First save the original ascii string pointers.
  963. */
  964. lpStr1 = (LPSTR)lpInfoP->lpstrReturn;
  965. /*
  966. ** If there is somewhere to store the result then we
  967. ** must allocate temporary space (for Unicode result)
  968. ** and on return from mciSendCommandW translate the
  969. ** string to Ascii.
  970. */
  971. if (len) {
  972. if ( lpStr1 ) {
  973. lpInfoP->lpstrReturn = mciAlloc( len );
  974. if ( lpInfoP->lpstrReturn == NULL ) {
  975. lpInfoP->lpstrReturn = (LPWSTR)lpStr1;
  976. return MCIERR_OUT_OF_MEMORY;
  977. }
  978. lpStr2 = mciAlloc( len );
  979. if ( lpStr2 == NULL ) {
  980. mciFree( (LPWSTR)lpInfoP->lpstrReturn );
  981. lpInfoP->lpstrReturn = (LPWSTR)lpStr1;
  982. return MCIERR_OUT_OF_MEMORY;
  983. }
  984. }
  985. } else {
  986. /*
  987. ** Should we ZERO the string pointers in the parameter block?
  988. ** Yes, belts and braces !!
  989. */
  990. lpInfoP->lpstrReturn = NULL;
  991. }
  992. /*
  993. ** Now call the unicode version
  994. */
  995. dwRet = mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2 );
  996. /*
  997. ** Copy the unicode return string into ascii, if the
  998. ** user provided a return string
  999. */
  1000. if (len && lpStr1) {
  1001. if ((MMSYSERR_NOERROR == dwRet) && len) {
  1002. UnicodeStrToAsciiStr( (PBYTE)lpStr2,
  1003. (PBYTE)lpStr2 + len,
  1004. lpInfoP->lpstrReturn );
  1005. /* On return from mciSendCommandW lpInfoP->dwRetSize is
  1006. ** equal to the number of characters copied to
  1007. ** lpInfoP->lpstrReturn less the NULL terminator.
  1008. ** So add one to lpInfoP->dwRetSize to include the NULL
  1009. ** in the strncpy below.
  1010. **
  1011. ** But ONLY if the original buffer was large enough.
  1012. */
  1013. //#ifdef DBCS
  1014. //fix kksuzuka: #3642
  1015. //have to copy byte length into ASCII buffer..
  1016. strncpy( (LPSTR)lpStr1, lpStr2,
  1017. min(BYTE_GIVEN_CHAR(lpInfoP->dwRetSize+1), CHAR_GIVEN_BYTE(len)));
  1018. //#else
  1019. // strncpy( (LPSTR)lpStr1, lpStr2,
  1020. // min((UINT)lpInfoP->dwRetSize + 1, CHAR_GIVEN_BYTE(len)) );
  1021. //#endif
  1022. #if DBG
  1023. dprintf3(( "Return param (UNICODE)= %ls", lpInfoP->lpstrReturn ));
  1024. dprintf3(( "Return param (ASCII) = %s", lpStr1 ));
  1025. #endif
  1026. }
  1027. /*
  1028. ** Free temp storage and restore the original strings
  1029. */
  1030. mciFree( lpInfoP->lpstrReturn );
  1031. lpInfoP->lpstrReturn = (LPWSTR)lpStr1;
  1032. mciFree( lpStr2 );
  1033. }
  1034. return dwRet;
  1035. }
  1036. default:
  1037. {
  1038. /*
  1039. ** NewParms is allocated off the stack in order to minimize
  1040. ** the number of calls to mciAlloc, and it means we do not
  1041. ** have to remember to free it.
  1042. */
  1043. DWORD_PTR NewParms[MCI_MAX_PARAM_SLOTS];
  1044. /*
  1045. ** dwStrMask is used to store a bitmap representation of which
  1046. ** offsets into dwParam2 contain strings. ie. bit 4 set
  1047. ** means that dwParam2[4] is a string.
  1048. */
  1049. DWORD dwStrMask = 0L;
  1050. /*
  1051. ** fStrReturn is used as a reminder of whether a string return
  1052. ** is expected or not. If the return type is not a string
  1053. ** we just copy the bytes back as is. uReturnLength is the
  1054. ** number of bytes to copy back. dwParm2 is used to ease some
  1055. ** of the addressing used to access the dwParam2 array.
  1056. */
  1057. BOOL fStrReturn = FALSE;
  1058. UINT uReturnLength = 0;
  1059. PDWORD_PTR dwParm2 = (PDWORD_PTR)dwParam2;
  1060. /*
  1061. ** The remaining variables are used as we scan our way thru the
  1062. ** command table.
  1063. */
  1064. LPWSTR lpCommand, lpFirstParameter;
  1065. LPSTR lpReturnStrTemp;
  1066. UINT wID;
  1067. DWORD dwValue;
  1068. UINT wOffset32, wOffset1stParm32, uTable, uStrlenBytes;
  1069. PDWORD_PTR pdwParm32;
  1070. DWORD dwMask = 1;
  1071. if (!dwParam2) {
  1072. return mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2);
  1073. }
  1074. /*
  1075. ** Find the command table for the given command ID.
  1076. ** If the command table is not there we have probably been
  1077. ** given a duff device ID. Anyway exit with an internal
  1078. ** error.
  1079. */
  1080. lpCommand = FindCommandItem( wDeviceID, NULL, (LPWSTR)(UINT_PTR)wMessage,
  1081. NULL, &uTable );
  1082. if ( lpCommand == NULL ) {
  1083. return MCIERR_UNSUPPORTED_FUNCTION;
  1084. }
  1085. #if DBG
  1086. ZeroMemory(NewParms, sizeof(NewParms));
  1087. #endif
  1088. /*
  1089. ** Copy callback field.
  1090. */
  1091. if ( dwParam1 & MCI_NOTIFY ) {
  1092. NewParms[0] = dwParm2[0];
  1093. }
  1094. /*
  1095. ** Skip past command entry
  1096. */
  1097. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  1098. mciEatCommandEntry( lpCommand, NULL, NULL ));
  1099. /*
  1100. ** Get and remember the first parameter
  1101. */
  1102. lpFirstParameter = lpCommand;
  1103. /*
  1104. ** Skip past the DWORD callback field
  1105. */
  1106. wOffset1stParm32 = 4;
  1107. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  1108. mciEatCommandEntry( lpCommand, &dwValue, &wID ));
  1109. /*
  1110. ** If the first parameter is a return value, we have some
  1111. ** special processing
  1112. */
  1113. if ( wID == MCI_RETURN ) {
  1114. /*
  1115. ** String return types are a special case.
  1116. */
  1117. if ( dwValue == MCI_STRING ) {
  1118. dprintf3(( "Found a return string" ));
  1119. /*
  1120. ** Get unicode string length in bytes and allocate
  1121. ** some storage, but only if a valid length has been
  1122. ** given. Otherwise set this field to NULL, we must
  1123. ** use 0 here otherwise the MIPS compiler goes
  1124. ** ape Xxxx. We set a flag to remind us to unthunk
  1125. ** the return string later.
  1126. **
  1127. ** Note that we are actually allocating lots of equally
  1128. ** sized storage here. This saves on the number of times
  1129. ** that we call mciAlloc.
  1130. */
  1131. if ( uStrlenBytes = (UINT)BYTE_GIVEN_CHAR( dwParm2[2] ) ) {
  1132. NewParms[1] = (DWORD_PTR)mciAlloc( uStrlenBytes * 2 );
  1133. dprintf4(( "Allocated %d bytes for the return string at %x", uStrlenBytes, NewParms[1] ));
  1134. if ( NewParms[1] == 0 ) {
  1135. mciUnlockCommandTable( uTable );
  1136. return MCIERR_OUT_OF_MEMORY;
  1137. }
  1138. lpReturnStrTemp = (LPSTR)(NewParms[1] + uStrlenBytes);
  1139. fStrReturn = TRUE;
  1140. }
  1141. else {
  1142. NewParms[1] = (DWORD)0;
  1143. }
  1144. /*
  1145. ** Copy string length.
  1146. */
  1147. NewParms[2] = dwParm2[2];
  1148. }
  1149. /*
  1150. ** Adjust the offset of the first parameter.
  1151. */
  1152. uReturnLength = mciGetParamSize( dwValue, wID );
  1153. wOffset1stParm32 += uReturnLength;
  1154. /*
  1155. ** Save the new first parameter pointer
  1156. */
  1157. lpFirstParameter = lpCommand;
  1158. }
  1159. /*
  1160. ** Walk through each flag
  1161. */
  1162. while ( dwMask != 0 ) {
  1163. /*
  1164. ** Is this bit set?
  1165. */
  1166. if ( (dwParam1 & dwMask) != 0 ) {
  1167. wOffset32 = wOffset1stParm32;
  1168. lpCommand = (LPWSTR)((LPBYTE)lpFirstParameter +
  1169. mciEatCommandEntry( lpFirstParameter,
  1170. &dwValue, &wID ));
  1171. /*
  1172. ** What parameter uses this bit?
  1173. */
  1174. while ( wID != MCI_END_COMMAND && dwValue != dwMask ) {
  1175. wOffset32 += mciGetParamSize( dwValue, wID );
  1176. if ( wID == MCI_CONSTANT ) {
  1177. while ( wID != MCI_END_CONSTANT ) {
  1178. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  1179. mciEatCommandEntry( lpCommand,
  1180. NULL, &wID ));
  1181. }
  1182. }
  1183. lpCommand = (LPWSTR)((LPBYTE)lpCommand +
  1184. mciEatCommandEntry( lpCommand,
  1185. &dwValue, &wID ));
  1186. }
  1187. if ( wID != MCI_END_COMMAND ) {
  1188. pdwParm32 = (PDWORD_PTR)((LPBYTE)NewParms + wOffset32);
  1189. if ( wID == MCI_STRING ) {
  1190. /*
  1191. ** Allocate a unicode string for this parameter
  1192. ** and set the flag.
  1193. */
  1194. *pdwParm32 = (DWORD_PTR)AllocUnicodeStr(
  1195. (LPSTR)*(PDWORD_PTR)((LPBYTE)dwParm2 +
  1196. wOffset32) );
  1197. //
  1198. // Turn wOffset32 into a bit mask.
  1199. // wOffset32 is the slot number offset in bytes
  1200. dwStrMask |= 1 << ((wOffset32 >> 2) - 1);
  1201. // Calculate the slot position (offset / 4)
  1202. // decrement to get the number of bits to shift
  1203. // shift 1 that number of bits left
  1204. // and OR into the existing dwStrMask.
  1205. #if DBG
  1206. dprintf3(( "String at %x (Addr %x) (UNICODE)= %ls", wOffset32/4, *pdwParm32 , *pdwParm32 ));
  1207. dprintf3(( "String at %x (Addr %x) (ASCII) = %s", wOffset32/4, *pdwParm32 , (LPSTR)*(PDWORD_PTR)((LPBYTE)dwParm2 + wOffset32) ));
  1208. #endif
  1209. }
  1210. else { // not a string
  1211. /*
  1212. ** Otherwise copy the parameter as is, if
  1213. ** there is anything to copy...
  1214. */
  1215. wID = mciGetParamSize( dwValue, wID);
  1216. switch (wID) {
  1217. case 4:
  1218. *pdwParm32 = *(LPDWORD)((LPBYTE)dwParm2 + wOffset32);
  1219. break;
  1220. case 0:
  1221. break;
  1222. default:
  1223. // This will be sizeof(MCI_RECT) as of today (Jan 93)
  1224. CopyMemory(pdwParm32, (LPBYTE)dwParm2 + wOffset32, wID);
  1225. }
  1226. }
  1227. }
  1228. }
  1229. /*
  1230. ** Go to the next flag
  1231. */
  1232. dwMask <<= 1;
  1233. }
  1234. // If no strings needed converting. Use the original parameter block
  1235. if ( !(dwStrMask | fStrReturn)) {
  1236. // No strings in parameters. Use original parameter pointer
  1237. dprintf3(( "NO strings for command %4X", wMessage ));
  1238. dwRet = mciSendCommandW( wDeviceID, wMessage, dwParam1, dwParam2);
  1239. uReturnLength = 0; // We will not need to copy anything back
  1240. } else {
  1241. dprintf3(( "The unicode string mask is %8X fStrReturn %x", dwStrMask, fStrReturn ));
  1242. dwRet = (DWORD)mciSendCommandW( wDeviceID, wMessage, dwParam1, (DWORD_PTR)NewParms );
  1243. }
  1244. /*
  1245. ** If there is a string return field we unthunk it here.
  1246. */
  1247. if ( fStrReturn && uStrlenBytes ) {
  1248. /*
  1249. ** If mciSendCommand worked then we need to convert the
  1250. ** return string from unicode to ascii.
  1251. */
  1252. if ( MMSYSERR_NOERROR == dwRet ) {
  1253. UnicodeStrToAsciiStr( (PBYTE)lpReturnStrTemp,
  1254. (PBYTE)lpReturnStrTemp + uStrlenBytes,
  1255. (LPWSTR)NewParms[1] );
  1256. /*
  1257. ** Copy back the return string size.
  1258. */
  1259. dwParm2[2] = NewParms[2];
  1260. /* On return from mciSendCommandW the dwRetSize field is
  1261. ** equal to the number of characters copied to
  1262. ** lpInfoP->lpstrReturn less the NULL terminator.
  1263. ** So add one to lpInfoP->dwRetSize to include the NULL in
  1264. ** the strncpy below.
  1265. **
  1266. ** But ONLY if the original buffer was large enough.
  1267. */
  1268. //#ifdef DBCS
  1269. //fix kksuzuka: #3642
  1270. //have to copy byte length into ASCII buffer..
  1271. strncpy( (LPSTR)dwParm2[1], lpReturnStrTemp,
  1272. min( (size_t)(BYTE_GIVEN_CHAR(NewParms[2]+1)),
  1273. (size_t)(CHAR_GIVEN_BYTE(uStrlenBytes))) );
  1274. //#else
  1275. // strncpy( (LPSTR)dwParm2[1], lpReturnStrTemp,
  1276. // min( (UINT)NewParms[2] + 1,
  1277. // CHAR_GIVEN_BYTE(uStrlenBytes)) );
  1278. //#endif
  1279. #if DBG
  1280. dprintf3(( "Returned string (UNICODE)= %ls", NewParms[1] ));
  1281. dprintf3(( "Returned string (ASCII) = %s", dwParm2[1] ));
  1282. #endif
  1283. }
  1284. /*
  1285. ** We need to free the string storage whether mciSendCommand
  1286. ** worked or not.
  1287. */
  1288. dprintf4(( "Freeing returned string at %x", NewParms[1] ));
  1289. mciFree( NewParms[1] );
  1290. }
  1291. /*
  1292. ** Else if there is any other sort of return field unthunk
  1293. ** it by copying across the bytes as is.
  1294. */
  1295. else if ( uReturnLength ) {
  1296. dprintf3(( "Copying back %d returned bytes", uReturnLength ));
  1297. CopyMemory( (LPDWORD)dwParam2 + 1, NewParms + 1, uReturnLength );
  1298. }
  1299. /*
  1300. ** Now go through the dwStrMask and free each field as indicated
  1301. ** by the set bits in the mask. We start at 1 because the
  1302. ** zero'th field is known to be a window handle.
  1303. */
  1304. wOffset32 = 1;
  1305. for ( ; dwStrMask != 0; dwStrMask >>= 1, wOffset32++ ) {
  1306. if ( dwStrMask & 1 ) {
  1307. /*
  1308. ** There is a string at NewParms[ wOffset32 ]
  1309. */
  1310. dprintf3(( "Freeing string at %d (%x) (UNICODE) = %ls", wOffset32, NewParms[ wOffset32 ], (LPWSTR)NewParms[ wOffset32 ] ));
  1311. FreeUnicodeStr( (LPWSTR)NewParms[ wOffset32 ] );
  1312. }
  1313. }
  1314. dprintf4(( "Unlocking command table" ));
  1315. mciUnlockCommandTable( uTable );
  1316. }
  1317. }
  1318. return dwRet;
  1319. }
  1320. DWORD mciSendCommandW(
  1321. MCIDEVICEID wDeviceID,
  1322. UINT wMessage,
  1323. DWORD_PTR dwParam1,
  1324. DWORD_PTR dwParam2)
  1325. {
  1326. UINT wRet;
  1327. DWORD dwErr;
  1328. MCI_INTERNAL_OPEN_INFO OpenInfo;
  1329. // Initialize the device list
  1330. if (!MCI_bDeviceListInitialized && !mciInitDeviceList())
  1331. return MCIERR_OUT_OF_MEMORY;
  1332. dprintf3(("mciSendCommand, command=%x Device=%x",wMessage, wDeviceID));
  1333. //
  1334. // Send the command. This shell is responsible for adding the device ID
  1335. // to the error code if necessary
  1336. //
  1337. OpenInfo.hCallingTask = GetCurrentTask();
  1338. OpenInfo.lpstrParams = NULL;
  1339. OpenInfo.lpstrPointerList = NULL;
  1340. OpenInfo.wParsingError = 0;
  1341. dwErr = mciSendCommandInternal( wDeviceID, wMessage,
  1342. dwParam1, dwParam2, &OpenInfo);
  1343. wRet = LOWORD(dwErr);
  1344. dprintf4(("Return value from mciSendCommandInternal %x", wRet));
  1345. // If the return value contains a resource ID then clear it from the high word
  1346. // Note that for IA64 the first element of the structure pointed to by
  1347. // dwParam2 is always a DWORD_PTR. However, the second element is not
  1348. // currently always a DWORD_PTR, some of the structures currenly have only
  1349. // a DWORD element in the second field. Accordingly, we make sure to only
  1350. // update the first DWORD of the second field in the structure, that works
  1351. // in every case because none of the bits in 32 - 63 are ever set by existing
  1352. // code.
  1353. if (dwErr & MCI_RESOURCE_RETURNED) {
  1354. *(LPDWORD)((PDWORD_PTR)dwParam2+1) &= 0xFFFF;
  1355. }
  1356. // If the error message is in a driver, store the driver ID in the high
  1357. // word of the error code
  1358. if (wRet >= MCIERR_CUSTOM_DRIVER_BASE) {
  1359. dwErr = (DWORD)wRet | ((DWORD)wDeviceID << 16);
  1360. } else {
  1361. dwErr = (DWORD)wRet;
  1362. }
  1363. #if DBG
  1364. // Dump the error text if any to the debug terminal
  1365. // Note that dwErr != 0 is a VALID return for driver messages. Only
  1366. // trap MCI messages
  1367. if ((dwErr != 0) && (wMessage>=MCI_FIRST))
  1368. {
  1369. WCHAR strTemp[MAXERRORLENGTH];
  1370. if (!mciGetErrorStringW( dwErr,
  1371. strTemp,
  1372. MAXERRORLENGTH ) ) {
  1373. LoadStringW( ghInst, STR_MCISCERRTXT, strTemp,
  1374. MAXERRORLENGTH );
  1375. }
  1376. dprintf1(("mciSendCommand: %ls", strTemp));
  1377. }
  1378. #endif
  1379. //
  1380. // Somehow since 3.51 the priorities of threads in WOW have
  1381. // changed and now the application thread is running at a
  1382. // higher priority than that of regular threads (i.e. mciavi's
  1383. // worker thread). Many applications that use MCI tend to
  1384. // poll the status of the MCI device that is playing. This
  1385. // polling is causing the other threads in WOW to be starved
  1386. // and brings the playback of AVIs to a crawl. This sleep
  1387. // will keep the application thread from buring so much of
  1388. // the CPU and allow other threads, for example MCIAVI, to
  1389. // do it's work.
  1390. //
  1391. if ( WinmmRunningInWOW )
  1392. {
  1393. Sleep(0);
  1394. }
  1395. return dwErr;
  1396. }
  1397. //***************************************************************************
  1398. // mciColonizeDigit
  1399. //
  1400. // Grab colonized digit
  1401. // Return is number of bytes written to output (NOT including NULL)
  1402. // or 0 if out of room in output buffer (but is terminated anyway)
  1403. // If there is room then at least two digits are written, padded with '0'
  1404. // if necessary. The function assumes that the buffer size is non-zero length,
  1405. // as this is checked in the function that calls the function that calls us.
  1406. //
  1407. //***************************************************************************
  1408. STATICFN UINT NEAR mciColonizeDigit(
  1409. LPWSTR lpstrOutput,
  1410. CHAR cDigit,
  1411. UINT uSize)
  1412. {
  1413. UINT uCount = 0;
  1414. #if DBG
  1415. // There is room for terminating NULL
  1416. if (uSize == 0) {
  1417. dprintf(("MCI: Internal error!!"));
  1418. return 0;
  1419. }
  1420. #endif
  1421. uCount = 2;
  1422. // If there is room for at least two digits
  1423. if (uSize >= 3)
  1424. {
  1425. if (cDigit >= 100)
  1426. {
  1427. uCount = 3;
  1428. if (uSize < 4)
  1429. goto terminate;
  1430. *lpstrOutput++ = (WCHAR)((cDigit / 100) % 10 + '0');
  1431. cDigit = (CHAR)(cDigit % 100);
  1432. }
  1433. *lpstrOutput++ = (WCHAR)(cDigit / 10 + '0');
  1434. *lpstrOutput++ = (WCHAR)(cDigit % 10 + '0');
  1435. }
  1436. terminate:;
  1437. *lpstrOutput++ = '\0';
  1438. // If we ran out of room then return an error
  1439. return (uCount >= uSize) ? 0 : uCount;
  1440. }
  1441. /*
  1442. * @doc INTERNAL MCI
  1443. * @func BOOL | mciColonize | Convert a colonized dword into a string
  1444. * representation
  1445. *
  1446. * @parm LPWSTR | lpstrOutput | Output buffer
  1447. *
  1448. * @parm UINT | uLength | Size of output buffer
  1449. *
  1450. * @parm DWORD | dwData | Value to convert
  1451. *
  1452. * @parm UINT | uType | Either MCI_COLONIZED3_RETURN or
  1453. * MCI_COLONIZED4_RETURN is set (HIWORD portion only!)
  1454. *
  1455. * @comm Example: For C4, 0x01020304 is converted to "04:03:02:01"
  1456. * For C3, 0x01020304 is converted to "04:03:02"
  1457. *
  1458. * @rdesc FALSE if there is not enough room in the output buffer
  1459. *
  1460. */
  1461. STATICFN BOOL NEAR mciColonize(
  1462. LPWSTR lpstrOutput,
  1463. UINT uLength,
  1464. DWORD dwData,
  1465. UINT uType)
  1466. {
  1467. LPSTR lpstrInput = (LPSTR)&dwData; // For stepping over each byte of input
  1468. UINT uSize;
  1469. int i;
  1470. for (i = 1; i <= (uType & HIWORD(MCI_COLONIZED3_RETURN) ? 3 : 4); ++i)
  1471. {
  1472. uSize = mciColonizeDigit( lpstrOutput, *lpstrInput++, uLength);
  1473. if (uSize == 0)
  1474. return FALSE;
  1475. lpstrOutput += uSize;
  1476. uLength -= uSize;
  1477. if (i < 3 || i < 4 && uType & HIWORD(MCI_COLONIZED4_RETURN))
  1478. {
  1479. --uLength;
  1480. if (uLength == 0)
  1481. return FALSE;
  1482. else
  1483. *lpstrOutput++ = ':';
  1484. }
  1485. }
  1486. return TRUE;
  1487. }
  1488. //***********************************************************************
  1489. // mciConvertReturnValue
  1490. //
  1491. // Convert the return value to a return string
  1492. //
  1493. //***********************************************************************
  1494. UINT mciConvertReturnValue(
  1495. UINT uType,
  1496. UINT uErrCode,
  1497. MCIDEVICEID wDeviceID,
  1498. PDWORD_PTR dwParams,
  1499. LPWSTR lpstrReturnString,
  1500. UINT uReturnLength ) // This is a character length
  1501. {
  1502. UINT wExternalTable;
  1503. if (lpstrReturnString == NULL || uReturnLength == 0)
  1504. return 0;
  1505. switch (uType)
  1506. {
  1507. case MCI_INTEGER:
  1508. case MCI_HWND:
  1509. case MCI_HPAL:
  1510. case MCI_HDC:
  1511. // Convert integer or resource return value to string
  1512. if (uErrCode & HIWORD(MCI_RESOURCE_RETURNED))
  1513. {
  1514. int nResId = HIWORD(dwParams[1]);
  1515. LPMCI_DEVICE_NODE nodeWorking;
  1516. HANDLE hInstance;
  1517. mciEnter("mciConvertReturnValue");
  1518. nodeWorking = MCI_lpDeviceList[wDeviceID];
  1519. mciLeave("mciConvertReturnValue");
  1520. if (nodeWorking == NULL)
  1521. {
  1522. // Return blank string on memory error
  1523. dprintf1(("mciConvertReturnValue Warning:NULL device node"));
  1524. break;
  1525. }
  1526. // Return value is a resource
  1527. if (uErrCode & HIWORD(MCI_RESOURCE_DRIVER))
  1528. {
  1529. // Return string ID belongs to driver
  1530. hInstance = nodeWorking->hDriver;
  1531. // WAS hInstance = nodeWorking->hCreatorTask;
  1532. wExternalTable = nodeWorking->wCustomCommandTable;
  1533. } else
  1534. {
  1535. wExternalTable = nodeWorking->wCommandTable;
  1536. hInstance = ghInst;
  1537. }
  1538. // Try to get string from custom or device specific external table
  1539. if ( wExternalTable == MCI_TABLE_NOT_PRESENT ||
  1540. command_tables[wExternalTable].hModule == NULL ||
  1541. LoadStringW( command_tables[wExternalTable].hModule,
  1542. nResId,
  1543. lpstrReturnString,
  1544. uReturnLength ) == 0 )
  1545. {
  1546. // Try to get string from CORE.MCI if it's not from the driver
  1547. if (hInstance != ghInst ||
  1548. command_tables[0].hModule == NULL ||
  1549. LoadStringW( command_tables[0].hModule, nResId,
  1550. lpstrReturnString,
  1551. uReturnLength ) == 0) {
  1552. // Get string from custom module or WINMM.DLL
  1553. LoadStringW( hInstance, nResId, lpstrReturnString,
  1554. uReturnLength);
  1555. }
  1556. }
  1557. } else if (uErrCode & HIWORD(MCI_COLONIZED3_RETURN) ||
  1558. uErrCode & HIWORD(MCI_COLONIZED4_RETURN))
  1559. {
  1560. if (!mciColonize (lpstrReturnString,
  1561. uReturnLength, (DWORD)dwParams[1], uErrCode))
  1562. return MCIERR_PARAM_OVERFLOW;
  1563. } else
  1564. // Convert integer return value to string
  1565. // NEED BETTER ERROR CHECKING !!LATER!!
  1566. // MUST FIND A VERSION OF THIS WHICH WON'T OVERFLOW OUTPUT BUFFER
  1567. {
  1568. DWORD dwTemp;
  1569. // Need room for a sign, up to ten digits and a NULL
  1570. if (uReturnLength < 12)
  1571. return MCIERR_PARAM_OVERFLOW;
  1572. if (uType == MCI_STRING ||
  1573. uErrCode == HIWORD(MCI_INTEGER_RETURNED))
  1574. dwTemp = *(LPDWORD)dwParams[1];
  1575. else
  1576. dwTemp = (DWORD)dwParams[1];
  1577. wsprintfW(lpstrReturnString, szLongFormat, dwTemp);
  1578. }
  1579. break;
  1580. case MCI_RECT:
  1581. // Need from for 4 times (a sign plus 5 digits) plus three spaces and a NULL
  1582. if (uReturnLength < 4 * 6 + 4)
  1583. return MCIERR_PARAM_OVERFLOW;
  1584. wsprintfW (lpstrReturnString, szRectFormat,
  1585. ((PMCI_ANIM_RECT_PARMS)dwParams)->rc.left,
  1586. ((PMCI_ANIM_RECT_PARMS)dwParams)->rc.top,
  1587. ((PMCI_ANIM_RECT_PARMS)dwParams)->rc.right,
  1588. ((PMCI_ANIM_RECT_PARMS)dwParams)->rc.bottom);
  1589. break;
  1590. default:
  1591. // Only support INTEGERs & MIXED
  1592. dprintf1(("mciConvertReturnValue Warning: Unknown return type"));
  1593. return MCIERR_PARSER_INTERNAL;
  1594. }
  1595. return 0;
  1596. }
  1597. //***********************************************************************
  1598. // mciSeparateCommandParts
  1599. //
  1600. // Pull off the command name and device name from the command string,
  1601. // leaving *lplpstrCommand pointing past the device name
  1602. //
  1603. // Returns 0 or an error code on failure. If successful, the caller must
  1604. // free the pstrCommandName and pstrDeviceName
  1605. //
  1606. // If bCompound then check for a '!' separator in the extracted device name
  1607. // and return only the element part. This is done so that inter-task
  1608. // commands to auto-opened devices will include the correct device name
  1609. //
  1610. //***********************************************************************
  1611. STATICFN DWORD NEAR mciSeparateCommandParts(
  1612. LPCWSTR FAR *lplpstrCommand,
  1613. BOOL bCompound,
  1614. LPWSTR FAR *lplpstrCommandName,
  1615. LPWSTR FAR *lplpstrDeviceName)
  1616. {
  1617. LPWSTR lpstrCommand;
  1618. UINT uErr;
  1619. // Localize the input
  1620. lpstrCommand = (LPWSTR)*lplpstrCommand;
  1621. // Remove leading spaces
  1622. while (*lpstrCommand == ' ') {
  1623. ++lpstrCommand;
  1624. }
  1625. if (*lpstrCommand == '\0') {
  1626. return MCIERR_MISSING_COMMAND_STRING;
  1627. }
  1628. // Pull the command name off the front of the command string
  1629. if ((uErr = mciEatToken ( (LPCWSTR *)&lpstrCommand, ' ', lplpstrCommandName, FALSE))
  1630. != 0) {
  1631. return uErr;
  1632. }
  1633. // Skip past spaces
  1634. while (*lpstrCommand == ' ') {
  1635. ++lpstrCommand;
  1636. }
  1637. // If we're looking for compound elements then yank off any leading
  1638. // device type if it is not the open command
  1639. if (bCompound && lstrcmpiW( wszOpen, *lplpstrCommandName) != 0)
  1640. {
  1641. LPWSTR lpstrTemp = lpstrCommand;
  1642. while (*lpstrTemp != '\0')
  1643. {
  1644. if (*lpstrTemp == '!')
  1645. {
  1646. // A ! was found so skip past it
  1647. lpstrCommand = lpstrTemp + 1;
  1648. break;
  1649. } else
  1650. ++lpstrTemp;
  1651. }
  1652. }
  1653. // Pull the device name off of the command string
  1654. if ((uErr = mciEatToken( (LPCWSTR *)&lpstrCommand, ' ', lplpstrDeviceName, FALSE))
  1655. != 0)
  1656. {
  1657. mciFree (*lplpstrCommandName);
  1658. return uErr;
  1659. }
  1660. // Fix up the results
  1661. *lplpstrCommand = lpstrCommand;
  1662. return 0;
  1663. }
  1664. /*--------------------------------------------------------------------*\
  1665. * mciSendSystemString
  1666. *
  1667. \*--------------------------------------------------------------------*/
  1668. STATICFN DWORD mciSendSystemString(
  1669. LPCWSTR lpstrCommand,
  1670. DWORD dwAdditionalFlags,
  1671. LPWSTR lpstrReturnString,
  1672. UINT uReturnLength)
  1673. {
  1674. DWORD dwRet;
  1675. LPMCI_SYSTEM_MESSAGE lpMessage;
  1676. DWORD CurDirSize;
  1677. dprintf2(("\nmciSendSystemString(%ls)", lpstrCommand));
  1678. if (!CreatehwndNotify()) {
  1679. dprintf1(("NULL notification window handle"));
  1680. return MCIERR_INTERNAL;
  1681. }
  1682. // Get a buffer to hold the current path PLUS an MCI_SYSTEM_MESSAGE structure
  1683. CurDirSize = GetCurrentDirectoryW( 0, NULL ); // Get size required.
  1684. // Remember the NULL is not included
  1685. if ( !CurDirSize ) { // Add 1 for the terminator
  1686. dprintf1(("NULL current path"));
  1687. return MCIERR_GET_CD;
  1688. }
  1689. CurDirSize++;
  1690. if (NULL != (lpMessage = mciAlloc( sizeof(MCI_SYSTEM_MESSAGE)
  1691. + BYTE_GIVEN_CHAR( CurDirSize ) ))) {
  1692. LPWSTR lpstrPath = (LPWSTR)( (LPBYTE)lpMessage
  1693. + sizeof( MCI_SYSTEM_MESSAGE ) );
  1694. if ( GetCurrentDirectoryW( CurDirSize, lpstrPath ) ) {
  1695. lpMessage->lpstrCommand = (LPWSTR)lpstrCommand;
  1696. lpMessage->dwAdditionalFlags = dwAdditionalFlags;
  1697. lpMessage->lpstrReturnString = lpstrReturnString;
  1698. lpMessage->uReturnLength = uReturnLength;
  1699. #if DBG
  1700. if ((0 == uReturnLength) && (0 != lpstrReturnString)) {
  1701. dprintf1((" ******** Return length 0, non 0 return address"));
  1702. }
  1703. #endif
  1704. lpMessage->hCallingTask = GetCurrentTask();
  1705. lpMessage->lpstrNewDirectory = lpstrPath;
  1706. // This is where we need to do some thread stuff
  1707. dwRet = (DWORD)SendMessage(hwndNotify, MM_MCISYSTEM_STRING, 0, (LPARAM)lpMessage);
  1708. //dwRet = mciSendStringInternal (NULL, NULL, 0, NULL, lpMessage);
  1709. } else {
  1710. dprintf1(("mciSendSystemString: cannot get current directory\n"));
  1711. dwRet = MCIERR_GET_CD;
  1712. }
  1713. mciFree(lpMessage);
  1714. } else {
  1715. dprintf1(("mciSendSystemString: cannot allocate message block\n"));
  1716. dwRet = MCIERR_OUT_OF_MEMORY;
  1717. }
  1718. return dwRet;
  1719. }
  1720. /*--------------------------------------------------------------------*\
  1721. * mciRelaySystemString
  1722. *
  1723. * Internal:
  1724. *
  1725. \*--------------------------------------------------------------------*/
  1726. DWORD mciRelaySystemString(
  1727. LPMCI_SYSTEM_MESSAGE lpMessage)
  1728. {
  1729. DWORD dwRet;
  1730. LPWSTR lpstrOldPath;
  1731. DWORD CurDirSize;
  1732. lpstrOldPath = 0; // Initialise to remove warning message
  1733. #if DBG
  1734. dprintf2(("mciRelaySystemString(%ls)", lpMessage->lpstrCommand));
  1735. #endif
  1736. // Get a buffer to hold the current path
  1737. CurDirSize = GetCurrentDirectoryW(0, lpstrOldPath); // Get size required.
  1738. // Remember the NULL is not included
  1739. if (!CurDirSize) { // Add 1 for the terminator AFTER testing
  1740. dprintf1(("NULL current path")); // for 0 from GetCurrentDirectory
  1741. return MCIERR_INTERNAL;
  1742. }
  1743. CurDirSize++;
  1744. /*
  1745. * Allocate space to hold the current path
  1746. * Fill the allocated space with the current path
  1747. * Set the new current directory to that in the message
  1748. * Execute the MCI command via SentStringInternal
  1749. * Reset to old current directory
  1750. *
  1751. * This code is not reentrant on the same PROCESS!!
  1752. */
  1753. if (NULL != (lpstrOldPath = mciAlloc( BYTE_GIVEN_CHAR(CurDirSize) ))) {
  1754. if (GetCurrentDirectoryW(CurDirSize, lpstrOldPath)) {
  1755. if (SetCurrentDirectoryW(lpMessage->lpstrNewDirectory)) {
  1756. dwRet = mciSendStringInternal (NULL, NULL, 0, NULL, lpMessage);
  1757. if (!SetCurrentDirectoryW(lpstrOldPath)) {
  1758. dprintf1(("mciRelaySystemString: WARNING, cannot restore path\n"));
  1759. }
  1760. } else {
  1761. dprintf1(("mciRelaySystemString: cannot set new path\n"));
  1762. dwRet = MCIERR_SET_CD;
  1763. }
  1764. } else {
  1765. dprintf1(("mciRelaySystemString: cannot get old path\n"));
  1766. dwRet = MCIERR_GET_CD;
  1767. }
  1768. mciFree(lpstrOldPath);
  1769. } else {
  1770. dprintf1(("mciRelaySystemString: cannot allocate old path\n"));
  1771. dwRet = MCIERR_OUT_OF_MEMORY;
  1772. }
  1773. return dwRet;
  1774. }
  1775. //***********************************************************************
  1776. // mciFindNotify
  1777. //
  1778. // Returns TRUE if "notify" is contained in string with leading blank
  1779. // and trailing blank or '\0'
  1780. //***********************************************************************
  1781. STATICFN BOOL mciFindNotify(
  1782. LPWSTR lpString)
  1783. {
  1784. while (*lpString != '\0')
  1785. {
  1786. // "notify" must be preceded by a blank
  1787. if (*lpString++ == ' ')
  1788. {
  1789. LPWSTR lpTemp;
  1790. lpTemp = wszNotify;
  1791. while (*lpTemp != '\0' && *lpString != '\0' &&
  1792. *lpTemp == MCI_TOLOWER(*lpString))
  1793. {
  1794. ++lpTemp;
  1795. ++lpString;
  1796. }
  1797. // "notify" must be followed by a blank or a null
  1798. if (*lpTemp == '\0' && // implies that wszNotify was found
  1799. (*lpString == '\0' || *lpString == ' '))
  1800. return TRUE;
  1801. }
  1802. }
  1803. return FALSE;
  1804. }
  1805. /*
  1806. * @doc INTERNAL MCI
  1807. *
  1808. * @func UINT | mciAutoOpenDevice | Try to auto-open the given device and
  1809. * then send the given command with notification sent to the system task
  1810. * window proc which sends a close command to the device on receipt
  1811. *
  1812. * @parm LPWSTR | lpstrDeviceName | The device name to open
  1813. *
  1814. * @parm LPWSTR | lpstrCommand | The full command to send including the
  1815. * device name which must be the same as lpstrDeviceName
  1816. *
  1817. * @parm LPWSTR | lpstrReturnString | The caller's return string buffer
  1818. *
  1819. * @parm UINT | uReturnLength | Size of the caller's return string buffer
  1820. *
  1821. * @rdesc The errorcode to return to the user
  1822. */
  1823. STATICFN UINT NEAR mciAutoOpenDevice(
  1824. LPWSTR lpstrDeviceName,
  1825. LPWSTR lpstrCommand,
  1826. LPWSTR lpstrReturnString,
  1827. UINT uReturnLength)
  1828. {
  1829. LPWSTR lpstrTempCommand, lpstrTempReturn = NULL;
  1830. UINT uErr;
  1831. dprintf2(("mciAutoOpenDevice(%ls, %ls)", lpstrDeviceName, lpstrCommand));
  1832. //
  1833. // Don't allow recursive auto opens on the mciWindow thread!
  1834. // This can happen when the device auto closes between a command (eg
  1835. // status) being issued on the client thread and executed on the
  1836. // mciWindow thread.
  1837. //
  1838. // mciSendStringW will detect this return code and try again - probably
  1839. // causing the device to be auto-opened on the caller's thread.
  1840. //
  1841. if (PtrToUlong(GetCurrentTask()) == mciWindowThreadId) {
  1842. return MCIERR_AUTO_ALREADY_CLOSED;
  1843. }
  1844. // "notify" not allowed. This will be found by the parser but the wrong
  1845. // error message will be returned.
  1846. if (mciFindNotify (lpstrCommand)) {
  1847. return MCIERR_NOTIFY_ON_AUTO_OPEN;
  1848. }
  1849. // Build the command string "open <device name>"
  1850. // Must be GMEM_SHARE for system task
  1851. // "open" + blank + device name + NULL
  1852. if ( (lpstrTempCommand = mciAlloc(
  1853. BYTE_GIVEN_CHAR( wcslen(lpstrDeviceName) +
  1854. /* Sizeof(wszOpen) == OPEN+NULL */
  1855. sizeof( wszOpen ) +
  1856. sizeof( WCHAR ) ) ) ) == NULL) {
  1857. return MCIERR_OUT_OF_MEMORY;
  1858. }
  1859. #ifdef WHICH_IS_BEST
  1860. wcscpy (lpstrTempCommand, wszOpen);
  1861. wcscat (lpstrTempCommand, L" ");
  1862. wcscat (lpstrTempCommand, lpstrDeviceName);
  1863. #else
  1864. wsprintfW(lpstrTempCommand, szCmdFormat, wszOpen, lpstrDeviceName);
  1865. #endif
  1866. // Get the open string into the system task via a SendMessage() to mmWndProc
  1867. uErr = (UINT)mciSendSystemString (lpstrTempCommand, 0L, NULL, 0);
  1868. mciFree (lpstrTempCommand);
  1869. if (uErr != 0) {
  1870. return uErr;
  1871. }
  1872. lpstrTempCommand = NULL;
  1873. // Must make a GMEM_SHARE copy of the return string for system task
  1874. if ( lpstrReturnString != NULL ) {
  1875. if ((lpstrTempReturn = mciAlloc(
  1876. BYTE_GIVEN_CHAR(uReturnLength + 1) )) == NULL )
  1877. {
  1878. // Close the device
  1879. mciDriverNotify (hwndNotify, mciGetDeviceIDW( lpstrDeviceName), 0);
  1880. return MCIERR_OUT_OF_MEMORY;
  1881. }
  1882. #if DBG
  1883. *lpstrTempReturn = 0;
  1884. #endif
  1885. }
  1886. // Get the user command string into the system task via a SendMessage()
  1887. // to mmWndProc
  1888. // The notification handle is also mmWndProc
  1889. uErr = (UINT)mciSendSystemString( lpstrCommand, MCI_NOTIFY, lpstrTempReturn,
  1890. uReturnLength);
  1891. // Copy the return string into the user's buffer
  1892. if (lpstrReturnString != NULL) {
  1893. if (uErr == 0) {
  1894. wcscpy( lpstrReturnString, lpstrTempReturn);
  1895. } else { // ERROR and no string to be copied
  1896. WinAssert(!*lpstrTempReturn);
  1897. }
  1898. mciFree( lpstrTempReturn);
  1899. }
  1900. // If there was an error we must close the device
  1901. if (uErr != 0)
  1902. {
  1903. mciAutoCloseDevice( lpstrDeviceName);
  1904. }
  1905. return uErr;
  1906. }
  1907. //*************************************************************************
  1908. // mciSendStringInternal
  1909. //
  1910. // Identical to mciSendString() but the lpMessage parameter is tacked on
  1911. //
  1912. // lpMessage comes from inter-task mciSendString and includes an
  1913. // hCallingTask item which is sent down the the OPEN command
  1914. //
  1915. //*************************************************************************
  1916. STATICFN DWORD mciSendStringInternal(
  1917. LPCWSTR lpstrCommand,
  1918. LPWSTR lpstrReturnString,
  1919. UINT uReturnLength, // This is a character length - NOT bytes
  1920. HANDLE hCallback,
  1921. LPMCI_SYSTEM_MESSAGE lpMessage)
  1922. {
  1923. UINT wID;
  1924. UINT uLen;
  1925. UINT uErr = 0;
  1926. UINT uConvertReturnValue;
  1927. UINT wMessage;
  1928. MCIDEVICEID wDeviceID;
  1929. PDWORD_PTR lpdwParams = NULL;
  1930. DWORD dwReturn, dwFlags = 0, dwAdditionalFlags = 0;
  1931. LPWSTR lpCommandItem;
  1932. DWORD dwErr = 0, dwRetType;
  1933. UINT wTable = (UINT)MCI_TABLE_NOT_PRESENT;
  1934. LPWSTR lpstrDeviceName = NULL;
  1935. LPWSTR lpstrCommandName = NULL;
  1936. LPWSTR FAR *lpstrPointerList = NULL;
  1937. LPWSTR lpstrCommandStart;
  1938. HANDLE hCallingTask;
  1939. UINT wParsingError;
  1940. BOOL bNewDevice;
  1941. LPWSTR lpstrInputCopy = NULL;
  1942. // Did this call come in from another task
  1943. if (lpMessage != NULL)
  1944. {
  1945. dprintf3(("mciSendStringInternal: remote task call"));
  1946. // Yes so restore info
  1947. lpstrCommand = lpMessage->lpstrCommand;
  1948. dwAdditionalFlags = lpMessage->dwAdditionalFlags;
  1949. lpstrReturnString = lpMessage->lpstrReturnString;
  1950. uReturnLength = lpMessage->uReturnLength;
  1951. #if DBG
  1952. if ((0 == uReturnLength) && (0 != lpstrReturnString)) {
  1953. dprintf((" -------- Return length 0, non 0 return address"));
  1954. }
  1955. #endif
  1956. hCallback = hwndNotify;
  1957. hCallingTask = lpMessage->hCallingTask;
  1958. lpstrInputCopy = NULL;
  1959. } else
  1960. {
  1961. BOOL bInQuotes = FALSE;
  1962. // No, so set hCallingTask to current thread
  1963. hCallingTask = GetCurrentTask();
  1964. if (lpstrCommand == NULL) {
  1965. return MCIERR_MISSING_COMMAND_STRING;
  1966. }
  1967. dprintf2(("mciSendString command ->%ls<-",lpstrCommand));
  1968. // Make a copy of the input string and convert tabs to spaces except
  1969. // when inside a quoted string
  1970. if ( (lpstrInputCopy = mciAlloc(
  1971. BYTE_GIVEN_CHAR( wcslen(lpstrCommand) + 1 ) ) ) == NULL ) {
  1972. return MCIERR_OUT_OF_MEMORY;
  1973. }
  1974. wcscpy(lpstrInputCopy, lpstrCommand); // Copies to the allocated area
  1975. lpstrCommand = lpstrInputCopy; // Reset string pointer to copy
  1976. lpstrCommandStart = (LPWSTR)lpstrCommand;
  1977. while (*lpstrCommandStart != '\0')
  1978. {
  1979. if (*lpstrCommandStart == '"') {
  1980. bInQuotes = !bInQuotes;
  1981. }
  1982. else if (!bInQuotes && *lpstrCommandStart == '\t') {
  1983. *lpstrCommandStart = ' ';
  1984. }
  1985. ++lpstrCommandStart;
  1986. }
  1987. }
  1988. lpstrCommandStart = (LPWSTR)lpstrCommand;
  1989. if (lpstrReturnString == NULL) {
  1990. // As an additional safeguard against writing into
  1991. // the output buffer when the return string pointer is NULL,
  1992. // set its length to 0
  1993. uReturnLength = 0;
  1994. } else {
  1995. #if DBG
  1996. if (0 == uReturnLength) {
  1997. dprintf(("Return length of zero, but now writing to return string"));
  1998. }
  1999. #endif
  2000. // Set return to empty string so that it won't print out garbage if not
  2001. // touched again
  2002. *lpstrReturnString = '\0';
  2003. }
  2004. // Pull the command name and device name off the command string
  2005. if ((dwReturn = mciSeparateCommandParts( (LPCWSTR FAR *)&lpstrCommand,
  2006. lpMessage != NULL,
  2007. &lpstrCommandName,
  2008. &lpstrDeviceName)) != 0)
  2009. goto exitfn;
  2010. // Get the device id (if any) of the given device name
  2011. wDeviceID = mciGetDeviceIDW(lpstrDeviceName);
  2012. // Allow "new" for an empty device name
  2013. if (wDeviceID == 0 && lstrcmpiW (lpstrDeviceName, wszNew) == 0)
  2014. {
  2015. bNewDevice = TRUE;
  2016. *lpstrDeviceName = '\0';
  2017. } else {
  2018. bNewDevice = FALSE;
  2019. }
  2020. // // If the call does not come from another task
  2021. // if (MCI_VALID_DEVICE_ID(wDeviceID) && hCallingTask == GetCurrentTask())
  2022. // {
  2023. // LPMCI_DEVICE_NODE nodeWorking = MCI_lpDeviceList[wDeviceID];
  2024. // if (nodeWorking == NULL)
  2025. // {
  2026. // uErr = MCIERR_INTERNAL;
  2027. // goto cleanup;
  2028. // }
  2029. // // Was the device opened by this task
  2030. // if (nodeWorking->hOpeningTask != nodeWorking->hCreatorTask)
  2031. // // No so send the string inter-task
  2032. // {
  2033. // mciFree(lpstrCommandName);
  2034. // mciFree(lpstrDeviceName);
  2035. // dwReturn = mciSendSystemString (lpstrCommandStart, lpstrReturnString,
  2036. // uReturnLength);
  2037. // goto exitfn;
  2038. // }
  2039. // }
  2040. // Look up the command name
  2041. wMessage = mciParseCommand( wDeviceID, lpstrCommandName, lpstrDeviceName,
  2042. &lpCommandItem, &wTable);
  2043. // If the device was auto-opened the request will go to the auto thread.
  2044. // We do not hang around to find out what happens. (The device could
  2045. // close at any time.)
  2046. mciEnter("mciSendStringInternal");
  2047. if (MCI_VALID_DEVICE_ID(wDeviceID))
  2048. {
  2049. LPMCI_DEVICE_NODE nodeWorking;
  2050. nodeWorking = MCI_lpDeviceList[wDeviceID];
  2051. // Is there a pending auto-close message?
  2052. if (ISAUTOCLOSING(nodeWorking))
  2053. {
  2054. uErr = MCIERR_DEVICE_LOCKED;
  2055. mciLeave("mciSendStringInternal");
  2056. goto cleanup;
  2057. // If the call does not come from another task and is not owned by this task
  2058. // and is not the SYSINFO command
  2059. } else if (lpMessage == NULL &&
  2060. nodeWorking->hOpeningTask != nodeWorking->hCreatorTask &&
  2061. wMessage != MCI_SYSINFO)
  2062. // Send the string inter-task
  2063. {
  2064. if ( mciFindNotify( lpstrCommandStart) )
  2065. {
  2066. uErr = MCIERR_NOTIFY_ON_AUTO_OPEN;
  2067. mciLeave("mciSendStringInternal");
  2068. goto cleanup;
  2069. }
  2070. else
  2071. {
  2072. LPWSTR lpstrReturnStringCopy;
  2073. mciFree(lpstrCommandName);
  2074. mciFree(lpstrDeviceName);
  2075. mciUnlockCommandTable (wTable);
  2076. if (uReturnLength) {
  2077. lpstrReturnStringCopy = mciAlloc (
  2078. BYTE_GIVEN_CHAR(uReturnLength + 1) );
  2079. } else {
  2080. lpstrReturnStringCopy = NULL;
  2081. }
  2082. mciLeave("mciSendStringInternal");
  2083. // If we failed to allocate a return string we return
  2084. // an error. Note: return strings are optional
  2085. if ((uReturnLength==0) || (lpstrReturnStringCopy != NULL) )
  2086. {
  2087. dwReturn = mciSendSystemString( lpstrCommandStart,
  2088. 0L,
  2089. lpstrReturnStringCopy,
  2090. uReturnLength);
  2091. if (uReturnLength) {
  2092. wcscpy( lpstrReturnString, lpstrReturnStringCopy);
  2093. mciFree( lpstrReturnStringCopy);
  2094. }
  2095. } else {
  2096. dwReturn = MCIERR_OUT_OF_MEMORY;
  2097. }
  2098. goto exitfn;
  2099. }
  2100. } else {
  2101. mciLeave("mciSendStringInternal");
  2102. }
  2103. }
  2104. else {
  2105. mciLeave("mciSendStringInternal");
  2106. }
  2107. // There must be a device name (except for the MCI_SOUND message)
  2108. if (*lpstrDeviceName == '\0' && wMessage != MCI_SOUND && !bNewDevice)
  2109. {
  2110. uErr = MCIERR_MISSING_DEVICE_NAME;
  2111. goto cleanup;
  2112. }
  2113. // The command must appear in the parser tables
  2114. if (wMessage == 0)
  2115. {
  2116. uErr = MCIERR_UNRECOGNIZED_COMMAND;
  2117. goto cleanup;
  2118. }
  2119. // The "new" device name is only legal for the open message
  2120. if (bNewDevice)
  2121. {
  2122. if (wMessage != MCI_OPEN)
  2123. {
  2124. uErr = MCIERR_INVALID_DEVICE_NAME;
  2125. goto cleanup;
  2126. }
  2127. }
  2128. // If there was no device ID
  2129. if (wDeviceID == 0)
  2130. {
  2131. // If auto open is not legal (usually internal commands)
  2132. if (MCI_CANNOT_AUTO_OPEN (wMessage))
  2133. {
  2134. // If the command needs an open device
  2135. if (!MCI_DO_NOT_NEED_OPEN (wMessage))
  2136. {
  2137. dprintf1(("mciSendStringInternal: device needs open"));
  2138. uErr = MCIERR_INVALID_DEVICE_NAME;
  2139. goto cleanup;
  2140. }
  2141. } else {
  2142. // If auto open is legal try to open the device automatically
  2143. uErr = mciAutoOpenDevice( lpstrDeviceName, lpstrCommandStart,
  2144. lpstrReturnString, uReturnLength);
  2145. // wDeviceID = MCI_ALL_DEVICE_ID;
  2146. goto cleanup;
  2147. }
  2148. }
  2149. //
  2150. // Parse the command parameters
  2151. //
  2152. if ((lpdwParams = (PDWORD_PTR)mciAlloc( sizeof(DWORD_PTR) * MCI_MAX_PARAM_SLOTS))
  2153. == NULL)
  2154. {
  2155. uErr = MCIERR_OUT_OF_MEMORY;
  2156. goto cleanup;
  2157. }
  2158. uErr = mciParseParams( wMessage, lpstrCommand, lpCommandItem, &dwFlags,
  2159. (LPWSTR)lpdwParams,
  2160. MCI_MAX_PARAM_SLOTS * sizeof(DWORD_PTR),
  2161. &lpstrPointerList, &wParsingError);
  2162. if (uErr != 0) {
  2163. goto cleanup;
  2164. }
  2165. // The 'new' device keyword requires an alias
  2166. if (bNewDevice && !(dwFlags & MCI_OPEN_ALIAS))
  2167. {
  2168. uErr = MCIERR_NEW_REQUIRES_ALIAS;
  2169. goto cleanup;
  2170. }
  2171. // Parsed OK so execute command
  2172. // Special processing for the MCI_OPEN message's parameters
  2173. if (wMessage == MCI_OPEN)
  2174. {
  2175. // Manually reference the device type and device element
  2176. if (dwFlags & MCI_OPEN_TYPE)
  2177. {
  2178. // The type name was specified explicitly as a parameter
  2179. // so the given device name is the element name
  2180. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrElementName = lpstrDeviceName;
  2181. dwFlags |= MCI_OPEN_ELEMENT;
  2182. } else
  2183. {
  2184. // A type must be explicitly specified when "new" is used
  2185. if (bNewDevice)
  2186. {
  2187. uErr = MCIERR_INVALID_DEVICE_NAME;
  2188. goto cleanup;
  2189. }
  2190. // The device type is the given device name. There is no element name
  2191. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrDeviceType = lpstrDeviceName;
  2192. ((LPMCI_OPEN_PARMSW)lpdwParams)->lpstrElementName = NULL;
  2193. dwFlags |= MCI_OPEN_TYPE;
  2194. }
  2195. }
  2196. else if (wMessage == MCI_SOUND && *lpstrDeviceName != '\0')
  2197. {
  2198. // Kludge the sound name for SOUND
  2199. // mciToLower (lpstrDeviceName);
  2200. if (lstrcmpiW(lpstrDeviceName, wszNotify) == 0)
  2201. {
  2202. *lpstrDeviceName = '\0';
  2203. dwFlags |= MCI_NOTIFY;
  2204. }
  2205. else if ( lstrcmpiW( lpstrDeviceName, wszWait ) == 0)
  2206. {
  2207. *lpstrDeviceName = '\0';
  2208. dwFlags |= MCI_WAIT;
  2209. }
  2210. else
  2211. {
  2212. ((LPMCI_SOUND_PARMSW)lpdwParams)->lpstrSoundName = lpstrDeviceName;
  2213. dwFlags |= MCI_SOUND_NAME;
  2214. }
  2215. }
  2216. // Figure out what kind of return value to expect
  2217. // Initialize flag
  2218. uConvertReturnValue = 0;
  2219. // Skip past header
  2220. uLen = mciEatCommandEntry (lpCommandItem, NULL, NULL);
  2221. // Get return value (if any)
  2222. mciEatCommandEntry ( (LPWSTR)((LPBYTE)lpCommandItem + uLen),
  2223. &dwRetType, &wID);
  2224. if (wID == MCI_RETURN)
  2225. {
  2226. // There is a return value
  2227. if (wDeviceID == MCI_ALL_DEVICE_ID && wMessage != MCI_SYSINFO)
  2228. {
  2229. uErr = MCIERR_CANNOT_USE_ALL;
  2230. goto cleanup;
  2231. }
  2232. switch (dwRetType)
  2233. {
  2234. case MCI_STRING:
  2235. // The return value is a string, point output buffer to user's buffer
  2236. lpdwParams[1] = (DWORD_PTR)lpstrReturnString;
  2237. lpdwParams[2] = (DWORD_PTR)uReturnLength;
  2238. break;
  2239. case MCI_INTEGER:
  2240. case MCI_HWND:
  2241. case MCI_HPAL:
  2242. case MCI_HDC:
  2243. // The return value is an integer, flag to convert it to a string later
  2244. // new uConvertReturnValue = MCI_INTEGER;
  2245. // new break;
  2246. case MCI_RECT:
  2247. // The return value is an rect, flag to convert it to a string later
  2248. // new uConvertReturnValue = MCI_RECT;
  2249. /* NEW */ uConvertReturnValue = (UINT)dwRetType;
  2250. break;
  2251. #if DBG
  2252. default:
  2253. dprintf1(("mciSendStringInternal: Unknown return type %d",dwRetType));
  2254. break;
  2255. #endif
  2256. }
  2257. }
  2258. // We don't need this around anymore
  2259. mciUnlockCommandTable (wTable);
  2260. wTable = (UINT)MCI_TABLE_NOT_PRESENT;
  2261. /* Fill the callback entry */
  2262. lpdwParams[0] = (DWORD_PTR)hCallback;
  2263. // Kludge the type number for SYSINFO
  2264. if (wMessage == MCI_SYSINFO) {
  2265. ((LPMCI_SYSINFO_PARMS)lpdwParams)->wDeviceType =
  2266. mciLookUpType(lpstrDeviceName);
  2267. }
  2268. // Now we actually send the command further into the bowels of MCI!
  2269. // The INTERNAL version of mciSendCommand is used in order to get
  2270. // special return description information encoded in the high word
  2271. // of the return value and to get back the list of pointers allocated
  2272. // by any parsing done in the open command
  2273. {
  2274. MCI_INTERNAL_OPEN_INFO OpenInfo;
  2275. OpenInfo.lpstrParams = (LPWSTR)lpstrCommand;
  2276. OpenInfo.lpstrPointerList = lpstrPointerList;
  2277. OpenInfo.hCallingTask = hCallingTask;
  2278. OpenInfo.wParsingError = wParsingError;
  2279. dwErr = mciSendCommandInternal (wDeviceID, wMessage,
  2280. dwFlags | dwAdditionalFlags,
  2281. (DWORD_PTR)(LPDWORD)lpdwParams,
  2282. &OpenInfo);
  2283. // If the command was reparsed there may be a new pointer list
  2284. // and the old one was free'd
  2285. lpstrPointerList = OpenInfo.lpstrPointerList;
  2286. }
  2287. uErr = LOWORD(dwErr);
  2288. if (uErr != 0) {
  2289. // If command execution error
  2290. goto cleanup;
  2291. }
  2292. // Command executed OK
  2293. // See if a string return came back with an integer instead
  2294. if (dwErr & MCI_INTEGER_RETURNED) {
  2295. uConvertReturnValue = MCI_INTEGER;
  2296. }
  2297. // If the return value must be converted
  2298. if (uConvertReturnValue != 0 && uReturnLength != 0) {
  2299. uErr = mciConvertReturnValue( uConvertReturnValue, HIWORD(dwErr),
  2300. wDeviceID, lpdwParams,
  2301. lpstrReturnString, uReturnLength);
  2302. }
  2303. cleanup:;
  2304. if (wTable != MCI_TABLE_NOT_PRESENT) {
  2305. mciUnlockCommandTable (wTable);
  2306. }
  2307. mciFree(lpstrCommandName);
  2308. mciFree(lpstrDeviceName);
  2309. if (lpdwParams != NULL) {
  2310. mciFree (lpdwParams);
  2311. }
  2312. // Free any memory used by string parameters
  2313. mciParserFree (lpstrPointerList);
  2314. dwReturn = (uErr >= MCIERR_CUSTOM_DRIVER_BASE ?
  2315. (DWORD)uErr | (DWORD)wDeviceID << 16 :
  2316. (DWORD)uErr);
  2317. #if DBG
  2318. if (dwReturn != 0)
  2319. {
  2320. WCHAR strTemp[MAXERRORLENGTH];
  2321. if (!mciGetErrorStringW( dwReturn, strTemp,
  2322. sizeof(strTemp) / sizeof(WCHAR) ) ) {
  2323. LoadStringW( ghInst, STR_MCISSERRTXT, strTemp,
  2324. sizeof(strTemp) / sizeof(WCHAR) );
  2325. }
  2326. else {
  2327. dprintf1(( "mciSendString: %ls", strTemp ));
  2328. }
  2329. }
  2330. #endif
  2331. exitfn:
  2332. if (lpstrInputCopy != NULL) {
  2333. mciFree (lpstrInputCopy);
  2334. }
  2335. #if DBG
  2336. mciCheckLocks();
  2337. #endif
  2338. return dwReturn;
  2339. }
  2340. /*
  2341. * @doc EXTERNAL MCI
  2342. *
  2343. * @api DWORD | mciSendString | This function sends a command string to an
  2344. * MCI device. The device that the command is sent to is specified in the
  2345. * command string.
  2346. *
  2347. * @parm LPCTSTR | lpstrCommand | Points to an MCI command string of the form:
  2348. * [command] [device] [parameters].
  2349. *
  2350. * @parm LPTSTR | lpstrReturnString | Specifies a buffer for return
  2351. * information. If no return information is needed, you can specify
  2352. * NULL for this parameter.
  2353. *
  2354. * @parm UINT | uReturnLength | Specifies the size of the return buffer
  2355. * specified by <p lpstrReturnString>.
  2356. *
  2357. * @parm HANDLE | hCallback | Specifies a handle to a window to call back
  2358. * if "notify" was specified in the command string.
  2359. *
  2360. * @rdesc Returns zero if the function was successful. Otherwise, it returns
  2361. * error information. The low-order word
  2362. * of the returned DWORD contains the error return value.
  2363. *
  2364. * To get a textual description of <f mciSendString> return values,
  2365. * pass the return value to <f mciGetErrorString>.
  2366. *
  2367. * The error returns listed for <f mciSendCommand> also apply to
  2368. * <f mciSendString>. The following error returns are unique to
  2369. * <f mciSendString>:
  2370. *
  2371. * @flag MCIERR_BAD_CONSTANT | Unknown value for parameter.
  2372. *
  2373. * @flag MCIERR_BAD_INTEGER | Invalid or missing integer in command.
  2374. *
  2375. * @flag MCIERR_DUPLICATE_FLAGS | A flag or value was specified twice.
  2376. *
  2377. * @flag MCIERR_MISSING_COMMAND_STRING | No command was specified.
  2378. *
  2379. * @flag MCIERR_MISSING_DEVICE_NAME | No device name was specified.
  2380. *
  2381. * @flag MCIERR_MISSING_STRING_ARGUMENT | A string value was
  2382. * missing from the command.
  2383. *
  2384. * @flag MCIERR_NEW_REQUIRES_ALIAS | An alias must be used
  2385. * with the "new" device name.
  2386. *
  2387. * @flag MCIERR_NO_CLOSING_QUOTE | A closing quotation mark is missing.
  2388. *
  2389. * @flag MCIERR_NOTIFY_ON_AUTO_OPEN | The "notify" flag is illegal
  2390. * with auto-open.
  2391. *
  2392. * @flag MCIERR_PARAM_OVERFLOW | The output string was not long enough.
  2393. *
  2394. * @flag MCIERR_PARSER_INTERNAL | Internal parser error.
  2395. *
  2396. * @flag MCIERR_UNRECOGNIZED_KEYWORD | Unknown command parameter.
  2397. *
  2398. * @xref mciGetErrorString mciSendCommand
  2399. */
  2400. MCIERROR APIENTRY mciSendStringA(
  2401. LPCSTR lpstrCommand,
  2402. LPSTR lpstrReturnString,
  2403. UINT uReturnLength,
  2404. HWND hwndCallback)
  2405. {
  2406. MCIERROR mciErr;
  2407. LPWSTR lpwstrCom;
  2408. LPWSTR lpwstrRet;
  2409. LPSTR lpstrTmp;
  2410. UINT len;
  2411. #ifdef DBG
  2412. dprintf4(( "Entered mciSendString ASCII" ));
  2413. #endif
  2414. // uReturnLength is a character count
  2415. // len is now in bytes
  2416. // WARNING: The length field might only be valid if a return
  2417. // address is given. If NO return address is specified, then
  2418. // we do not want to waste time allocating anything.
  2419. if (!lpstrReturnString) {
  2420. uReturnLength = 0;
  2421. }
  2422. len = BYTE_GIVEN_CHAR( uReturnLength );
  2423. // We could make the following code slightly more efficient by
  2424. // allocating a single area of size uReturnLength*2 bytes.
  2425. if (len) {
  2426. lpstrTmp = (LPSTR)mciAlloc( len );
  2427. if ( lpstrTmp == (LPSTR)NULL ) {
  2428. return MCIERR_OUT_OF_MEMORY;
  2429. }
  2430. lpwstrRet = (LPWSTR)mciAlloc( len );
  2431. if ( lpwstrRet == (LPWSTR)NULL ) {
  2432. mciFree( lpstrTmp );
  2433. return MCIERR_OUT_OF_MEMORY;
  2434. }
  2435. } else {
  2436. lpstrTmp = NULL;
  2437. lpwstrRet = NULL;
  2438. }
  2439. lpwstrCom = AllocUnicodeStr( (LPSTR)lpstrCommand );
  2440. if ( lpwstrCom == NULL ) {
  2441. if (len) {
  2442. mciFree( lpstrTmp );
  2443. mciFree( lpwstrRet );
  2444. }
  2445. return MCIERR_OUT_OF_MEMORY;
  2446. }
  2447. #ifdef DBG
  2448. dprintf4(( "Unicode Command = %ls", lpwstrCom ));
  2449. dprintf4(( "Ascii command = %s", lpstrCommand ));
  2450. #endif
  2451. mciErr = mciSendStringW( lpwstrCom, lpwstrRet, uReturnLength, hwndCallback );
  2452. dprintf4(( "mciSendStringW returned %d", mciErr ));
  2453. if (len) {
  2454. dprintf4(( "Copying Unicode string to Ascii: %ls", lpwstrRet));
  2455. UnicodeStrToAsciiStr( (PBYTE)lpstrTmp, (PBYTE)lpstrTmp + len, lpwstrRet );
  2456. strncpy( lpstrReturnString, lpstrTmp, uReturnLength );
  2457. dprintf4(( "........done: %s", lpstrReturnString));
  2458. mciFree( lpstrTmp );
  2459. mciFree( lpwstrRet );
  2460. }
  2461. FreeUnicodeStr( lpwstrCom );
  2462. return mciErr;
  2463. }
  2464. MCIERROR APIENTRY mciSendStringW(
  2465. LPCWSTR lpstrCommand,
  2466. LPWSTR lpstrReturnString,
  2467. UINT uReturnLength,
  2468. HWND hwndCallback)
  2469. {
  2470. MCIERROR wRet;
  2471. // Initialize the device list
  2472. if (!MCI_bDeviceListInitialized && !mciInitDeviceList()) {
  2473. return MCIERR_OUT_OF_MEMORY;
  2474. }
  2475. //
  2476. // We can get return code MCIERR_AUTO_ALREADY_CLOSED if the device is
  2477. // auto-open and appears to be open but when we get to the mciWindow
  2478. // thread it has already closed. In this case we try again.
  2479. //
  2480. do {
  2481. wRet = mciSendStringInternal (lpstrCommand, lpstrReturnString,
  2482. uReturnLength, hwndCallback, NULL);
  2483. } while (wRet == MCIERR_AUTO_ALREADY_CLOSED);
  2484. return wRet;
  2485. }
  2486. /*
  2487. * @doc INTERNAL MCI
  2488. *
  2489. * @api BOOL | mciExecute | This function is a simplified version of the
  2490. * <f mciSendString> function. It does not take a buffer for
  2491. * return information, and displays a dialog box when
  2492. * errors occur.
  2493. *
  2494. * @parm LPCSTR | lpstrCommand | Points to an MCI command string of the form:
  2495. * [command] [device] [parameters].
  2496. *
  2497. * @rdesc TRUE if successful, FALSE if unsuccessful.
  2498. *
  2499. * @comm This function provides a simple interface to MCI from scripting
  2500. * languages. For debugging, set the "mciexecute" entry in the
  2501. * [mmdebug] section of WIN.INI to 1 and detailed error information will
  2502. * be displayed in a dialog box. If "mmcmd" is set to 0, only user-correctable
  2503. * error information will be displayed.
  2504. * THIS FUNCTION IS NOW OBSOLETE AND IS ONLY PRESENT FOR 16BIT COMPATIBILITY
  2505. * HENCE NO UNICODE VERSION IS PROVIDED
  2506. *
  2507. * @xref mciSendString
  2508. */
  2509. BOOL APIENTRY mciExecute(
  2510. LPCSTR lpstrCommand)
  2511. {
  2512. WCHAR aszError[MAXERRORLENGTH];
  2513. DWORD dwErr;
  2514. HANDLE hName = 0;
  2515. LPWSTR lpstrName = NULL;
  2516. LPWSTR lpwstrCom;
  2517. lpwstrCom = AllocUnicodeStr( (LPSTR)lpstrCommand );
  2518. if ( lpwstrCom == NULL ) {
  2519. return FALSE;
  2520. }
  2521. dwErr = mciSendStringW(lpwstrCom, NULL, 0, NULL);
  2522. FreeUnicodeStr( lpwstrCom );
  2523. if (LOWORD(dwErr) == 0) {
  2524. return TRUE;
  2525. }
  2526. if (!mciGetErrorStringW( dwErr, aszError, MAXERRORLENGTH )) {
  2527. LoadStringW( ghInst, STR_MCIUNKNOWN, aszError, MAXERRORLENGTH );
  2528. } else {
  2529. if (lpwstrCom != NULL)
  2530. {
  2531. // Skip initial blanks
  2532. while (*lpwstrCom == ' ') {
  2533. ++lpwstrCom;
  2534. }
  2535. // Then skip the command
  2536. while (*lpwstrCom != ' ' && *lpwstrCom != '\0') {
  2537. ++lpwstrCom;
  2538. }
  2539. // Then blanks before the device name
  2540. while (*lpwstrCom == ' ') ++lpwstrCom;
  2541. // Now, get the device name
  2542. if ( *lpwstrCom != '\0' &&
  2543. mciEatToken ((LPCWSTR *)&lpwstrCom, ' ', &lpstrName, FALSE)
  2544. != 0
  2545. ) {
  2546. dprintf1(("Could not allocate device name text for error box"));
  2547. }
  2548. }
  2549. }
  2550. MessageBoxW( NULL, aszError, lpstrName, MB_ICONHAND | MB_OK);
  2551. if (lpstrName != NULL) {
  2552. mciFree(lpstrName);
  2553. }
  2554. return FALSE;
  2555. }
  2556. /*
  2557. * @doc EXTERNAL MCI
  2558. *
  2559. * @api BOOL | mciGetErrorString | This function returns a
  2560. * textual description of the specified MCI error.
  2561. *
  2562. * @parm DWORD | dwError | Specifies the error code returned by
  2563. * <f mciSendCommand> or <f mciSendString>.
  2564. *
  2565. * @parm LPTSTR | lpstrBuffer | Specifies a pointer to a buffer that is
  2566. * filled with a textual description of the specified error.
  2567. *
  2568. * @parm UINT | uLength | Specifies the length of the buffer pointed to by
  2569. * <p lpstrBuffer>.
  2570. *
  2571. * @rdesc Returns TRUE if successful. Otherwise, the given error code
  2572. * was not known.
  2573. */
  2574. BOOL APIENTRY mciGetErrorStringA(
  2575. DWORD dwError,
  2576. LPSTR lpstrBuffer,
  2577. UINT uLength)
  2578. {
  2579. HANDLE hInst = 0;
  2580. if (lpstrBuffer == NULL) {
  2581. return FALSE;
  2582. }
  2583. // If the high bit is set then get the error string from the driver
  2584. // otherwise get it from mmsystem.dll
  2585. if (HIWORD(dwError) != 0) {
  2586. mciEnter("mciGetErrorStringA");
  2587. if (MCI_VALID_DEVICE_ID ((UINT)HIWORD(dwError))) {
  2588. hInst = MCI_lpDeviceList[HIWORD (dwError)]->hDriver;
  2589. }
  2590. mciLeave("mciGetErrorStringA");
  2591. if (hInst == 0) {
  2592. hInst = ghInst;
  2593. dwError = MCIERR_DRIVER;
  2594. }
  2595. } else {
  2596. hInst = ghInst;
  2597. }
  2598. if (LoadStringA(hInst, LOWORD(dwError), lpstrBuffer, uLength ) == 0)
  2599. {
  2600. // If the string load failed then at least terminate the string
  2601. if (uLength > 0) {
  2602. *lpstrBuffer = '\0';
  2603. dprintf1(("Failed to load resource string"));
  2604. }
  2605. return FALSE;
  2606. }
  2607. else
  2608. {
  2609. return TRUE;
  2610. }
  2611. }
  2612. BOOL APIENTRY mciGetErrorStringW(
  2613. DWORD dwError,
  2614. LPWSTR lpstrBuffer,
  2615. UINT uLength)
  2616. {
  2617. HANDLE hInst = 0;
  2618. if (lpstrBuffer == NULL) {
  2619. return FALSE;
  2620. }
  2621. // If the high bit is set then get the error string from the driver
  2622. // otherwise get it from mmsystem.dll
  2623. if (HIWORD(dwError) != 0) {
  2624. mciEnter("mciGetErrorStringW");
  2625. if (MCI_VALID_DEVICE_ID ((UINT)HIWORD(dwError))) {
  2626. hInst = MCI_lpDeviceList[HIWORD (dwError)]->hDriver;
  2627. }
  2628. mciLeave("mciGetErrorStringW");
  2629. if (hInst == 0) {
  2630. hInst = ghInst;
  2631. dwError = MCIERR_DRIVER;
  2632. }
  2633. } else {
  2634. hInst = ghInst;
  2635. }
  2636. if (LoadStringW(hInst, LOWORD(dwError), lpstrBuffer, uLength ) == 0)
  2637. {
  2638. // If the string load failed then at least terminate the string
  2639. if (uLength > 0) {
  2640. *lpstrBuffer = '\0';
  2641. dprintf1(("Failed to load resource string"));
  2642. }
  2643. return FALSE;
  2644. }
  2645. else
  2646. {
  2647. return TRUE;
  2648. }
  2649. }
  2650. #if 0
  2651. /*
  2652. * Return non-zero if load successful
  2653. */
  2654. BOOL MCIInit()
  2655. {
  2656. return TRUE;
  2657. }
  2658. /*
  2659. * Return non-zero if load successful
  2660. */
  2661. void MCITerminate()
  2662. {
  2663. /*
  2664. We would like to close all open devices here but cannot because of
  2665. unknown WEP order
  2666. */
  2667. if (hMciHeap != NULL) {
  2668. HeapDestroy(hMciHeap);
  2669. }
  2670. hMciHeap = NULL;
  2671. }
  2672. #endif