Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3060 lines
97 KiB

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