Leaked source code of windows server 2003
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.

1955 lines
60 KiB

  1. /*******************************Module*Header*********************************\
  2. * Module Name: mciparse.c
  3. *
  4. * Media Control Architecture Command Parser
  5. *
  6. * Created: 3/2/90
  7. * Author: DLL (DavidLe)
  8. *
  9. * History:
  10. * 5/22/91: Ported to Win32 - NigelT
  11. * 4 Mar 1992: SteveDav - much work for NT. Bring up to Win 3.1 level
  12. *
  13. * Copyright (c) 1991-1998 Microsoft Corporation
  14. *
  15. \******************************************************************************/
  16. //****************************************************************************
  17. //
  18. // This has to be defined in order to pick up the
  19. // correct version of MAKEINTRESOURCE
  20. //
  21. //****************************************************************************
  22. #define UNICODE
  23. /*****************************************************************************
  24. * Notes: *
  25. * *
  26. * MCI command tables are (normally) loaded from resource type files. The *
  27. * format of a command table is shown below. Note that because the table *
  28. * contains string data, the binary values are UNALIGNED. This causes *
  29. * specific problems on MIPS machines. *
  30. * *
  31. * Because of compatibility with Windows 3.1 the binary data is WORD size *
  32. * *
  33. * Table format: *
  34. * *
  35. * verb\0 MCI_MESSAGE,0 MCI_command_type *
  36. * *
  37. * e.g. *
  38. * 'o' 'p' 'e' 'n' 03 08 00 00 00 'n' 'o' 't' 'i' 'f' 'y' 00 *
  39. * 01 00 00 00 00 05 00 *
  40. * *
  41. * which is "open" MCI_OPEN,0, MCI_COMMAND_HEAD *
  42. * "notify" MCI_NOTIFY,0 MCI_FLAG *
  43. * *
  44. * beware of the byte ordering! *
  45. * *
  46. ****************************************************************************/
  47. #include "winmmi.h"
  48. #include "mci.h"
  49. #include "wchar.h"
  50. #include <digitalv.h>
  51. #define _INC_WOW_CONVERSIONS
  52. #include "mmwow32.h"
  53. extern WSZCODE wszOpen[]; // in MCI.C
  54. STATICFN UINT mciRegisterCommandTable( HANDLE hResource, PUINT lpwIndex,
  55. UINT wType, HANDLE hModule);
  56. STATICFN UINT mciParseArgument ( UINT uMessage, DWORD dwValue, UINT wID,
  57. LPWSTR FAR *lplpstrOutput, LPDWORD lpdwFlags, LPWSTR lpArgument,
  58. LPWSTR lpCurrentCommandItem);
  59. //
  60. // Define the init code for this file. This is commented out in debug builds
  61. // so that codeview doesn't get confused.
  62. #if DBG
  63. extern int mciDebugLevel;
  64. #endif
  65. // Number of command tables registered, including "holes"
  66. STATICDT UINT number_of_command_tables = 0;
  67. // Command table list
  68. COMMAND_TABLE_TYPE command_tables[MAX_COMMAND_TABLES];
  69. STATICDT WSZCODE wszTypeTableExtension[] = L".mci";
  70. STATICDT WSZCODE wszCoreTable[] = L"core";
  71. // Core table is loaded when the first MCI command table is requested
  72. STATICDT BOOL bCoreTableLoaded = FALSE;
  73. // One element for each device type. Value is the table type to use
  74. // or 0 if there is no device type specific table.
  75. STATICDT UINT table_types[] =
  76. {
  77. MCI_DEVTYPE_VCR, // vcr
  78. MCI_DEVTYPE_VIDEODISC, // videodisc
  79. MCI_DEVTYPE_OVERLAY, // overlay
  80. MCI_DEVTYPE_CD_AUDIO, // cdaudio
  81. MCI_DEVTYPE_DAT, // dat
  82. MCI_DEVTYPE_SCANNER, // scanner
  83. MCI_DEVTYPE_ANIMATION, // animation
  84. MCI_DEVTYPE_DIGITAL_VIDEO, // digitalvideo
  85. MCI_DEVTYPE_OTHER, // other
  86. MCI_DEVTYPE_WAVEFORM_AUDIO, // waveaudio
  87. MCI_DEVTYPE_SEQUENCER // sequencer
  88. };
  89. /*
  90. * @doc INTERNAL MCI
  91. * @func UINT | mciEatCommandEntry | Read a command resource entry and
  92. * return its length and its value and identifier
  93. *
  94. * @parm LPWCSTR | lpEntry | The start of the command resource entry
  95. *
  96. * @parm LPDWORD | lpValue | The value of the entry, returned to caller
  97. * May be NULL
  98. *
  99. * @parm PUINT | lpID | The identifier of the entry, returned to caller
  100. * May be NULL
  101. *
  102. * @rdesc The total number of bytes in the entry
  103. *
  104. */
  105. UINT mciEatCommandEntry (
  106. LPCWSTR lpEntry,
  107. LPDWORD lpValue,
  108. PUINT lpID)
  109. {
  110. LPCWSTR lpScan = lpEntry;
  111. LPBYTE lpByte;
  112. #if DBG
  113. DWORD Value;
  114. UINT Id;
  115. #endif
  116. // NOTE: The data will generally be UNALIGNED
  117. /* Skip to end */
  118. while (*lpScan++ != '\0'){}
  119. /* lpScan now points at the byte beyond the terminating zero */
  120. lpByte = (LPBYTE)lpScan;
  121. if (lpValue != NULL) {
  122. *lpValue = *(UNALIGNED DWORD *)lpScan;
  123. }
  124. #if DBG
  125. Value = *(UNALIGNED DWORD *)lpScan;
  126. #endif
  127. lpByte += sizeof(DWORD);
  128. if (lpID != NULL) {
  129. *lpID = *(UNALIGNED WORD *)lpByte;
  130. }
  131. #if DBG
  132. Id = *(UNALIGNED WORD *)lpByte;
  133. #endif
  134. lpByte += sizeof(WORD);
  135. //
  136. // WARNING !! This assumes that the table being looked at has WORD
  137. // size entries in the RCDATA resource
  138. //
  139. #if DBG
  140. dprintf5(("mciEatCommandEntry(%ls) Value: %x Id: %x", lpEntry, Value, Id));
  141. #endif
  142. return (UINT)(lpByte - (LPBYTE)lpEntry); // Total size of entry in bytes
  143. }
  144. //
  145. // Return the size used by this token in the parameter list
  146. //
  147. UINT mciGetParamSize (
  148. DWORD dwValue,
  149. UINT wID)
  150. {
  151. // MCI_RETURN returns 8 for sizeof(STRING) as there is a length
  152. // field as well as the string pointer. For non MCI_RETURN uses
  153. // of MCI_STRING we should return 4 (== sizeof pointer)
  154. // Similarly, MCI_CONSTANT used within MCI_RETURN is size 0, but
  155. // size 4 when used as an input parameter.
  156. if (wID == MCI_RETURN) {
  157. if (dwValue==MCI_STRING) {
  158. return(8);
  159. } else if (dwValue==MCI_CONSTANT) {
  160. wID = 0;
  161. } else {
  162. wID=dwValue;
  163. }
  164. }
  165. switch (wID)
  166. {
  167. case MCI_CONSTANT:
  168. case MCI_INTEGER:
  169. case MCI_STRING:
  170. case MCI_HWND:
  171. case MCI_HPAL:
  172. case MCI_HDC:
  173. return sizeof(DWORD_PTR); // In Win64, sizeof pointer is 8
  174. case MCI_RECT:
  175. return sizeof(RECT);
  176. }
  177. // Note that some items will not be found - deliberately. For example
  178. // MCI_FLAG causes 0 to be returned.
  179. return 0;
  180. }
  181. /*
  182. * @doc INTERNAL MCI
  183. * @func UINT | mciRegisterCommandTable | This function adds a new
  184. * table for the MCI parser.
  185. *
  186. * @parm HANDLE | hResource | Handle to the RCDATA resource
  187. *
  188. * @parm PUINT | lpwIndex | Pointer to command table index
  189. *
  190. * @parm UINT | wType | Specifies the device type for this command table.
  191. * Driver tables and the core table are type 0.
  192. *
  193. * @rdesc Returns the command table index number that was assigned or MCI_ERROR_VALUE
  194. * on error.
  195. *
  196. */
  197. STATICFN UINT mciRegisterCommandTable (
  198. HANDLE hResource,
  199. PUINT lpwIndex,
  200. UINT wType,
  201. HANDLE hModule)
  202. {
  203. UINT uID;
  204. /* First check for free slots */
  205. mciEnter("mciRegisterCommandTable");
  206. for (uID = 0; uID < number_of_command_tables; ++uID) {
  207. if (command_tables[uID].hResource == NULL) {
  208. break;
  209. }
  210. }
  211. /* If no empty slots then allocate another one */
  212. if (uID >= number_of_command_tables)
  213. {
  214. if (number_of_command_tables == MAX_COMMAND_TABLES)
  215. {
  216. dprintf1(("mciRegisterCommandTable: No more tables"));
  217. mciFree(lpwIndex); // Cannot use it - must free it
  218. mciLeave("mciRegisterCommandTable");
  219. return (UINT)MCI_ERROR_VALUE;
  220. } else {
  221. uID = number_of_command_tables++;
  222. // The table goes at the end of the list
  223. }
  224. }
  225. /* Fill in the slot */
  226. command_tables[uID].wType = wType;
  227. command_tables[uID].hResource = hResource;
  228. command_tables[uID].lpwIndex = lpwIndex;
  229. command_tables[uID].hModule = hModule;
  230. #if DBG
  231. command_tables[uID].wLockCount = 0;
  232. #endif
  233. // now that hResource has been filled in marking the entry as used
  234. // we can allow others access.
  235. mciLeave("mciRegisterCommandTable");
  236. #if DBG
  237. if (mciDebugLevel > 2)
  238. {
  239. dprintf2(("mciRegisterCommandTable INFO: assigned slot %d", uID));
  240. dprintf2(("mciRegisterCommandTable INFO: #tables is %d", number_of_command_tables));
  241. }
  242. #endif
  243. return uID;
  244. }
  245. /*
  246. * @doc DDK MCI
  247. * @api UINT | mciLoadCommandResource | Registers the indicated
  248. * resource as an MCI command table and builds a command table
  249. * index. If a file with the resource name and the extension '.mci' is
  250. * found in the path then the resource is taken from that file.
  251. *
  252. * @parm HANDLE | hInstance | The instance of the module whose executable
  253. * file contains the resource. This parameter is ignored if an external file
  254. * is found.
  255. *
  256. * @parm LPCWSTR | lpResName | The name of the resource
  257. *
  258. * @parm UINT | wType | The table type. Custom device specific tables MUST
  259. * give a table type of 0.
  260. *
  261. * @rdesc Returns the command table index number that was assigned or MCI_ERROR_VALUE
  262. * on error.
  263. *
  264. */
  265. UINT mciLoadCommandResource (
  266. HANDLE hInstance,
  267. LPCWSTR lpResName,
  268. UINT wType)
  269. {
  270. BOOL fResType = !HIWORD(lpResName);
  271. PUINT lpwIndex, lpwScan;
  272. HANDLE hExternal = NULL;
  273. HANDLE hResource;
  274. HANDLE hResInfo;
  275. LPWSTR lpResource, lpScan;
  276. int nCommands = 0;
  277. UINT wLen;
  278. UINT wID;
  279. // Name + '.' + Extension + '\0'
  280. WCHAR strFile[8 + 1 + 3 + 1];
  281. LPWSTR lpstrFile = strFile;
  282. LPCWSTR lpstrType = lpResName;
  283. #if DBG
  284. if (!fResType) {
  285. dprintf3(("mciLoadCommandResource INFO: Resource name >%ls< ", (LPWSTR)lpResName));
  286. } else if (LOWORD(lpResName)) {
  287. dprintf3(("mciLoadCommandResource INFO: Resource ID >%d<", (UINT)LOWORD(lpResName)));
  288. } else {
  289. dprintf3(("mciLoadCommandResource INFO: NULL resource pointer"));
  290. }
  291. #endif
  292. // Initialize the device list
  293. if (!MCI_bDeviceListInitialized && !mciInitDeviceList()) {
  294. return (UINT)MCI_ERROR_VALUE; // MCIERR_OUT_OF_MEMORY;
  295. }
  296. // Load the core table if its not already there
  297. if (!bCoreTableLoaded)
  298. {
  299. bCoreTableLoaded = TRUE;
  300. // Now we can call ourselves recursively to first load the core
  301. // table. Check if this is a request to load CORE - if yes,
  302. // simply drop through.
  303. // If its not our core table being loaded...
  304. // which is decided by comparing the string with CORE, or if a
  305. // resource id has been given, the resource id is ID_CORE and comes
  306. // from our module
  307. // The test is structured this way so that lstrcmpiW is only called
  308. // if we have a valid pointer.
  309. #define fNotCoreTable ( fResType \
  310. ? ((hInstance != ghInst) || (ID_CORE_TABLE != (UINT)(UINT_PTR)lpResName)) \
  311. : (0 != lstrcmpiW (wszCoreTable, (LPWSTR)lpResName)))
  312. if (fNotCoreTable) {
  313. // We are not being asked to load the core table. So we
  314. // explicitly load the core table first
  315. if (mciLoadCommandResource (ghInst, MAKEINTRESOURCE(ID_CORE_TABLE), 0) == MCI_ERROR_VALUE)
  316. {
  317. dprintf1(("mciLoadCommandResource: Cannot load core table"));
  318. }
  319. }
  320. }
  321. // Unless this is a resource ID, go and look for a file
  322. if (!fResType) {
  323. WCHAR ExpandedName[MAX_PATH];
  324. LPWSTR FilePart;
  325. // Check for a file with the extension ".mci"
  326. // Copy up to the first eight characters of device type
  327. // !!LATER!! Try a check for a resource first, then a file
  328. while (lpstrType < lpResName + 8 && *lpstrType != '\0') {
  329. *lpstrFile++ = *lpstrType++;
  330. }
  331. // Tack extension onto end
  332. wcscpy (lpstrFile, wszTypeTableExtension);
  333. // If the file exists and can be loaded then set flag to use it.
  334. // (Otherwise we will try and load the resource from WINMM.DLL.)
  335. if (!SearchPathW(NULL, strFile, NULL, MAX_PATH, ExpandedName,
  336. &FilePart)) {
  337. hExternal = NULL;
  338. } else {
  339. UINT OldErrorMode;
  340. OldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
  341. // Use "ExpandedName" to prevent a second search taking place
  342. hExternal = LoadLibraryExW( ExpandedName, //strFile,
  343. NULL,
  344. DONT_RESOLVE_DLL_REFERENCES);
  345. SetErrorMode(OldErrorMode);
  346. }
  347. }
  348. // Load the given table from the file or from the module if not found
  349. if (hExternal != NULL &&
  350. (hResInfo = FindResourceW(hExternal, lpResName, RT_RCDATA )) != NULL)
  351. {
  352. hInstance = hExternal;
  353. } else {
  354. hResInfo = FindResourceW(hInstance, lpResName, RT_RCDATA );
  355. }
  356. if (hResInfo == NULL)
  357. {
  358. #if DBG
  359. if (!fResType) {
  360. dprintf3(("mciLoadCommandResource Cannot find command resource name >%ls< ", (LPWSTR)lpResName));
  361. } else {
  362. dprintf3(("mciLoadCommandResource Cannot find command resource ID >%d<", (UINT)LOWORD(lpResName)));
  363. }
  364. #endif
  365. if (NULL != hExternal) {
  366. FreeLibrary(hExternal); // Clean up after ourselves
  367. }
  368. return (UINT)MCI_ERROR_VALUE;
  369. }
  370. if ((hResource = LoadResource (hInstance, hResInfo)) == NULL)
  371. {
  372. #if DBG
  373. if (!fResType) {
  374. dprintf3(("mciLoadCommandResource Cannot load command resource name >%ls< ", (LPWSTR)lpResName));
  375. } else {
  376. dprintf3(("mciLoadCommandResource Cannot load command resource ID >%d<", (UINT)LOWORD(lpResName)));
  377. }
  378. #endif
  379. if (NULL != hExternal) {
  380. FreeLibrary(hExternal); // Clean up after ourselves
  381. }
  382. return (UINT)MCI_ERROR_VALUE;
  383. }
  384. if ((lpResource = LockResource (hResource)) == NULL)
  385. {
  386. dprintf1(("mciLoadCommandResource: Cannot lock resource"));
  387. FreeResource (hResource);
  388. if (NULL != hExternal) {
  389. FreeLibrary(hExternal); // Clean up after ourselves
  390. }
  391. return (UINT)MCI_ERROR_VALUE;
  392. }
  393. /* Count the number of commands */
  394. lpScan = lpResource;
  395. while (TRUE)
  396. {
  397. (LPBYTE)lpScan = (LPBYTE)lpScan + mciEatCommandEntry(lpScan, NULL, &wID);
  398. // End of command?
  399. if (wID == MCI_COMMAND_HEAD)
  400. ++nCommands;
  401. // End of command list?
  402. else if (wID == MCI_END_COMMAND_LIST)
  403. break;
  404. }
  405. // There must be at least one command in the table
  406. if (nCommands == 0)
  407. {
  408. dprintf1(("mciLoadCommandResource: No commands in the specified table"));
  409. UnlockResource (hResource);
  410. FreeResource (hResource);
  411. if (NULL != hExternal) {
  412. FreeLibrary(hExternal); // Clean up after ourselves
  413. }
  414. return (UINT)MCI_ERROR_VALUE;
  415. } else {
  416. dprintf3(("mciLoadCommandResource: %d commands in the specified table", nCommands));
  417. }
  418. // Allocate storage for the command table index
  419. // Leave room for a MCI_TABLE_NOT_PRESENT entry to terminate it
  420. if ((lpwIndex = mciAlloc (sizeof (*lpwIndex) * (nCommands + 1)))
  421. == NULL)
  422. {
  423. dprintf1(("mciLoadCommandResource: cannot allocate command table index"));
  424. UnlockResource (hResource);
  425. FreeResource (hResource);
  426. if (NULL != hExternal) {
  427. FreeLibrary(hExternal); // Clean up after ourselves
  428. }
  429. return (UINT)MCI_ERROR_VALUE;
  430. }
  431. /* Build Command Table */
  432. lpwScan = lpwIndex;
  433. lpScan = lpResource;
  434. while (TRUE)
  435. {
  436. // Get next command entry
  437. wLen = mciEatCommandEntry (lpScan, NULL, &wID);
  438. if (wID == MCI_COMMAND_HEAD)
  439. {
  440. // Add an offset index to this command from start of resource
  441. *lpwScan++ = (UINT)((LPBYTE)lpScan - (LPBYTE)lpResource);
  442. }
  443. else if (wID == MCI_END_COMMAND_LIST)
  444. {
  445. // Mark the end of the table
  446. *lpwScan = (UINT)MCI_TABLE_NOT_PRESENT;
  447. break;
  448. }
  449. (LPBYTE)lpScan = (LPBYTE)lpScan + wLen;
  450. }
  451. UnlockResource (hResource);
  452. return mciRegisterCommandTable (hResource, lpwIndex, wType, hExternal);
  453. }
  454. /*
  455. * @doc INTERNAL MCI
  456. * @func UINT | mciLoadTableType | If the table of the given type
  457. * has not been loaded, register it
  458. *
  459. * @parm UINT | wType | The table type to load
  460. *
  461. * @rdesc Returns the command table index number that was assigned or MCI_ERROR_VALUE
  462. * on error.
  463. */
  464. UINT mciLoadTableType (
  465. UINT wType)
  466. {
  467. UINT wID;
  468. #ifdef OLD
  469. WCHAR buf[MCI_MAX_DEVICE_TYPE_LENGTH];
  470. #endif
  471. // Check to see if this table type is already loaded
  472. for (wID = 0; wID < number_of_command_tables; ++wID) {
  473. if (command_tables[wID].wType == wType) {
  474. return wID;
  475. }
  476. }
  477. // Must load table
  478. // First look up what device type specific table to load for this type
  479. if (wType < MCI_DEVTYPE_FIRST || wType > MCI_DEVTYPE_LAST) {
  480. return (UINT)MCI_ERROR_VALUE;
  481. }
  482. // Load string that corresponds to table type
  483. #ifdef OLD
  484. #ifdef WIN31CODE
  485. // Load string that corresponds to table type
  486. buf[0] = 0; // In case load string fails to set anything
  487. LoadString (ghInst, table_types[wType - MCI_DEVTYPE_FIRST],
  488. buf, sizeof(buf));
  489. {
  490. //Must be at least one character in type name
  491. int nTypeLen;
  492. if ((nTypeLen = wcslen (buf)) < 1)
  493. return MCI_ERROR_VALUE;
  494. }
  495. #else
  496. // Load string that corresponds to table type
  497. buf[0] = 0; // In case load string fails to set anything
  498. if (!LoadString (ghInst, table_types[wType - MCI_DEVTYPE_FIRST],
  499. buf, sizeof(buf))) {
  500. //Must put at least one character into type name
  501. return MCI_ERROR_VALUE;
  502. }
  503. #endif // WIN31CODE
  504. // Register the table with MCI
  505. return mciLoadCommandResource (ghInst, buf, wType);
  506. #else // not old
  507. // Command tables are stored as RCDATA blocks with an id of the device type
  508. // If mciLoadCommandResource fails to find the command table then it
  509. // will return MCI_ERROR_VALUE
  510. //if (!FindResource(ghInst, wType, RT_RCDATA))
  511. // return MCI_ERROR_VALUE;
  512. //
  513. // Register the table with MCI
  514. return mciLoadCommandResource (ghInst, MAKEINTRESOURCE(wType), wType);
  515. #endif
  516. }
  517. /*
  518. * @doc DDK MCI
  519. *
  520. * @api BOOL | mciFreeCommandResource | Frees the memory used
  521. * by the specified command table.
  522. *
  523. * @parm UINT | wTable | The table index returned from a previous call to
  524. * mciLoadCommandResource.
  525. *
  526. * @rdesc FALSE if the table index is not valid, TRUE otherwise.
  527. *
  528. */
  529. BOOL APIENTRY mciFreeCommandResource (
  530. UINT wTable)
  531. {
  532. MCIDEVICEID wID;
  533. HANDLE hResource;
  534. PUINT lpwIndex;
  535. dprintf3(("mciFreeCommandResource INFO: Free table %d", wTable));
  536. dprintf3(("mciFreeCommandResource INFO: Lockcount is %d", command_tables[wTable].wLockCount));
  537. /* Validate input -- do not let the core table be free'd */
  538. if (wTable == MCI_TABLE_NOT_PRESENT || wTable >= number_of_command_tables)
  539. {
  540. #if DBG
  541. // wTable == MCI_TABLE_NOT_PRESENT is OK
  542. if (wTable != MCI_TABLE_NOT_PRESENT) {
  543. dprintf1(("mciFreeCommandResource: Cannot free table number %d", wTable));
  544. }
  545. #endif
  546. return FALSE;
  547. }
  548. mciEnter("mciFreeCommandResource");
  549. // If this table is being used elsewhere then keep it around
  550. for (wID = 1; wID < MCI_wNextDeviceID; ++wID)
  551. {
  552. if (MCI_lpDeviceList[wID] != NULL)
  553. {
  554. if (MCI_lpDeviceList[wID]->wCustomCommandTable == wTable ||
  555. MCI_lpDeviceList[wID]->wCommandTable == wTable)
  556. {
  557. #if DBG
  558. if (mciDebugLevel > 2) {
  559. dprintf1(("mciFreeCommandResource INFO: table in use"));
  560. }
  561. #endif
  562. mciLeave("mciFreeCommandResource");
  563. return FALSE;
  564. }
  565. }
  566. }
  567. #if 0
  568. /* Search the list of tables */
  569. for (wID = 0; wID < number_of_command_tables; ++wID)
  570. /* If this resource is still in use, keep it around */
  571. if (command_tables[wID].hResource == hResource)
  572. {
  573. #if DBG
  574. if (mciDebugLevel > 2)
  575. DOUT(("mciFreeCommandResource INFO: resource in use\r\n"));
  576. #endif
  577. mciLeave("mciFreeCommandResource");
  578. return FALSE;
  579. }
  580. #endif
  581. hResource = command_tables[wTable].hResource;
  582. command_tables[wTable].hResource = NULL;
  583. // This slot can now be picked up by someone else
  584. lpwIndex = command_tables[wTable].lpwIndex;
  585. command_tables[wTable].lpwIndex = NULL;
  586. command_tables[wTable].wType = 0;
  587. FreeResource (hResource);
  588. mciFree (lpwIndex);
  589. hResource = command_tables[wTable].hModule;
  590. mciLeave("mciFreeCommandResource");
  591. if (hResource != NULL)
  592. {
  593. FreeLibrary (hResource);
  594. }
  595. // Make space at top of list
  596. if (wTable == number_of_command_tables - 1)
  597. {
  598. --number_of_command_tables;
  599. }
  600. dprintf3(("mciFreeCommandResource INFO: number_of_command_tables: %d", number_of_command_tables));
  601. return TRUE;
  602. }
  603. #if DBG
  604. void mciCheckLocks ()
  605. {
  606. UINT wTable;
  607. if (mciDebugLevel <= 2) {
  608. return;
  609. }
  610. for (wTable = 0; wTable < number_of_command_tables; ++wTable)
  611. {
  612. if (command_tables[wTable].hResource == NULL) {
  613. continue;
  614. }
  615. dprintf2(("mciCheckLocks INFO: table %d Lock count %d", wTable, command_tables[wTable].wLockCount));
  616. // dprintf2(("user: %x ", GlobalFlags (command_tables[wTable].hResource) & GMEM_LOCKCOUNT));
  617. //
  618. // if (GlobalFlags (command_tables[wTable].hResource) & GMEM_DISCARDABLE) {
  619. // dprintf(("discardable"));
  620. // } else {
  621. // dprintf(("NOT discardable"));
  622. // }
  623. }
  624. }
  625. #endif
  626. /*
  627. * @doc INTERNAL MCI
  628. * @func BOOL | mciUnlockCommandTable | Unlocks the command table given by
  629. * a table index
  630. *
  631. * @parm UINT | wCommandTable | Table to unlock
  632. *
  633. * @rdesc TRUE if success, FALSE otherwise
  634. *
  635. * @comm Used external to this module by mci.c
  636. *
  637. */
  638. BOOL mciUnlockCommandTable (
  639. UINT wCommandTable)
  640. {
  641. UnlockResource(command_tables[wCommandTable].hResource);
  642. #if DBG
  643. --command_tables[wCommandTable].wLockCount;
  644. if (mciDebugLevel > 2)
  645. {
  646. dprintf2(("mciUnlockCommandTable INFO: table %d", wCommandTable));
  647. mciCheckLocks();
  648. }
  649. #endif
  650. return TRUE;
  651. }
  652. /*
  653. * @doc INTERNAL MCI
  654. * @func LPWSTR | FindCommandInTable | Look up the given
  655. * command string in the GIVEN parser command table
  656. *
  657. * @parm UINT | wTable | Command table to use
  658. *
  659. * @parm LPCWSTR | lpstrCommand | The command to look up. It must
  660. * be in lower case with no leading or trailing blanks and with at
  661. * least one character.
  662. *
  663. * @parm PUINT | lpwMessage | The message corresponding to the command
  664. * Returned to caller.
  665. *
  666. * @rdesc NULL if the command is unknown or on error, otherwise a pointer to
  667. * the command list ffr the input command string.
  668. *
  669. * @comm If the command is found, the command resource will be locked on exit.
  670. *
  671. */
  672. LPWSTR FindCommandInTable (
  673. UINT wTable,
  674. LPCWSTR lpstrCommand,
  675. PUINT lpwMessage)
  676. {
  677. PUINT lpwIndex;
  678. LPWSTR lpResource, lpstrThisCommand;
  679. UINT wMessage;
  680. #if DBG
  681. if (HIWORD(lpstrCommand)) {
  682. dprintf3(("FindCommandInTable(%04XH, %ls)", wTable, lpstrCommand));
  683. } else {
  684. dprintf3(("FindCommandInTable(%04XH, id = %x)", wTable, (UINT)LOWORD(lpstrCommand)));
  685. }
  686. #endif
  687. //
  688. /* Validate table */
  689. //
  690. mciEnter("FindCommandInTable");
  691. if (wTable >= number_of_command_tables)
  692. {
  693. //
  694. // Check the core table but its not yet loaded
  695. //
  696. if (wTable == 0)
  697. {
  698. //
  699. // Try to load it
  700. //
  701. // if (mciLoadCommandResource (ghInst, wszCoreTable, 0) == MCI_ERROR_VALUE)
  702. if (mciLoadCommandResource (ghInst, (LPCWSTR)ID_CORE_TABLE, 0) == MCI_ERROR_VALUE)
  703. {
  704. mciLeave("FindCommandInTable");
  705. dprintf1(("FindCommandInTable: cannot load core table"));
  706. return NULL;
  707. }
  708. }
  709. else
  710. {
  711. mciLeave("FindCommandInTable");
  712. dprintf1(("FindCommandInTable: invalid table ID: %04XH", wTable));
  713. return NULL;
  714. }
  715. }
  716. if ((lpResource = LockResource (command_tables[wTable].hResource)) == NULL)
  717. {
  718. mciLeave("FindCommandInTable");
  719. dprintf1(("MCI FindCommandInTable: Cannot lock table resource"));
  720. return NULL;
  721. }
  722. #if DBG
  723. ++command_tables[wTable].wLockCount;
  724. #endif
  725. //
  726. // Look at each command in the table
  727. // We use the index table rather than the return value from
  728. // mciEatCommandEntry to step through the table
  729. //
  730. lpwIndex = command_tables[wTable].lpwIndex;
  731. if (lpwIndex == NULL)
  732. {
  733. mciLeave("FindCommandInTable");
  734. dprintf1(("MCI FindCommandInTable: null command table index"));
  735. return NULL;
  736. }
  737. while (*lpwIndex != MCI_TABLE_NOT_PRESENT)
  738. {
  739. lpstrThisCommand = (LPWSTR)(*lpwIndex++ + (LPBYTE)lpResource);
  740. //
  741. // Get message number from the table
  742. //
  743. mciEatCommandEntry ((LPCWSTR)lpstrThisCommand, (LPDWORD)&wMessage, NULL);
  744. //
  745. // Does this command match the input?
  746. // IF we have a string pointer, check the command name matches,
  747. // OR for a message, check the message values match
  748. //
  749. if (HIWORD (lpstrCommand) != 0 &&
  750. lstrcmpiW(lpstrThisCommand, lpstrCommand) == 0 ||
  751. HIWORD (lpstrCommand) == 0 &&
  752. wMessage == (UINT)LOWORD(PtrToUlong(lpstrCommand)))
  753. {
  754. //
  755. // Retain the locked resource pointer
  756. //
  757. command_tables[wTable].lpResource = lpResource;
  758. //
  759. // Address the message ID which comes after the command name
  760. //
  761. if (lpwMessage != NULL) *lpwMessage = wMessage;
  762. //
  763. // Leave table locked on exit
  764. //
  765. mciLeave("FindCommandInTable");
  766. dprintf3(("mciFindCommandInTable: found >%ls< Message %x", lpstrThisCommand, wMessage));
  767. return lpstrThisCommand;
  768. }
  769. //
  770. // Strings don't match, go to the next command in the table
  771. //
  772. }
  773. UnlockResource (command_tables[wTable].hResource);
  774. #if DBG
  775. --command_tables[wTable].wLockCount;
  776. #endif
  777. mciLeave("FindCommandInTable");
  778. dprintf3((" ...not found"));
  779. return NULL;
  780. }
  781. /*
  782. * @doc INTERNAL MCI
  783. * @func LPWSTR | FindCommandItem | Look up the given
  784. * command string in the parser command tables
  785. *
  786. * @parm MCIDEVICEID | wDeviceID | The device ID used for this command.
  787. * If 0 then only the system core command table is searched.
  788. *
  789. * @parm LPCWSTR | lpstrType | The type name of the device
  790. *
  791. * @parm LPCWSTR | lpstrCommand | The command to look up. It must
  792. * be in lower case with no leading or trailing blanks and with at
  793. * least one character. If the HIWORD is 0 then the LOWORD contains
  794. * the command message ID instead of a command name and the function is
  795. * merely to find the command list pointer.
  796. *
  797. * If the high word is 0 then the low word is an command ID value instead
  798. * of a command name
  799. *
  800. * @parm PUINT | lpwMessage | The message corresponding to the command
  801. * Returned to caller.
  802. *
  803. * @parm LPUINT | lpwTable | The table index in which the command was found
  804. * Returned to caller.
  805. *
  806. * @rdesc NULL if the command is unknown, otherwise a pointer to
  807. * the command list for the input command string.
  808. */
  809. LPWSTR FindCommandItem (
  810. MCIDEVICEID wDeviceID,
  811. LPCWSTR lpstrType,
  812. LPCWSTR lpstrCommand,
  813. PUINT lpwMessage,
  814. PUINT lpwTable)
  815. {
  816. LPWSTR lpCommand = NULL;
  817. UINT wTable;
  818. LPMCI_DEVICE_NODE nodeWorking;
  819. UINT uDeviceType = 0;
  820. UNREFERENCED_PARAMETER(lpstrType);
  821. //
  822. // Only check hiword per comments above
  823. //
  824. if (HIWORD (lpstrCommand) != (WORD)NULL) {
  825. if (*lpstrCommand == '\0')
  826. {
  827. dprintf1(("MCI FindCommandItem: lpstrCommand is NULL or empty string"));
  828. return NULL;
  829. } else {
  830. dprintf3(("FindCommandItem(%ls)", lpstrCommand));
  831. }
  832. } else {
  833. dprintf3(("FindCommandItem(command id = %x)", (UINT)LOWORD(lpstrCommand)));
  834. }
  835. //
  836. // If a specific device ID was specified then look in any custom table
  837. // or type table
  838. //
  839. if (wDeviceID != 0 && wDeviceID != MCI_ALL_DEVICE_ID)
  840. {
  841. //
  842. // If the device ID is valid
  843. //
  844. mciEnter("FindCommandItem");
  845. if (!MCI_VALID_DEVICE_ID (wDeviceID) ||
  846. (NULL == (nodeWorking = MCI_lpDeviceList[wDeviceID])))
  847. {
  848. dprintf1(("MCI FindCommandItem: Invalid device ID or pointer"));
  849. mciLeave("FindCommandItem");
  850. return NULL;
  851. }
  852. uDeviceType = nodeWorking->wDeviceType;
  853. //
  854. // If there is a custom command table then use it
  855. //
  856. if ((wTable = nodeWorking->wCustomCommandTable) != MCI_TABLE_NOT_PRESENT)
  857. {
  858. lpCommand = FindCommandInTable (wTable, lpstrCommand, lpwMessage);
  859. if (lpCommand != NULL) {
  860. mciLeave("FindCommandItem");
  861. goto exit;
  862. }
  863. }
  864. //
  865. // Get the device type table from the existing device
  866. // Relies on mciReparseCommand in mciLoadDevice to catch all device type
  867. // tables when device is not yet open.
  868. //
  869. if ((wTable = nodeWorking->wCommandTable) != MCI_TABLE_NOT_PRESENT)
  870. {
  871. lpCommand = FindCommandInTable (wTable, lpstrCommand, lpwMessage);
  872. if (lpCommand != NULL) {
  873. mciLeave("FindCommandItem");
  874. goto exit;
  875. }
  876. }
  877. mciLeave("FindCommandItem");
  878. }
  879. #if 0
  880. // If no device was specified
  881. if (uDeviceType == 0 && lpstrType != NULL && *lpstrType != '\0')
  882. {
  883. // See if the type is one known
  884. uDeviceType = mciLookUpType (lpstrType);
  885. if (uDeviceType == 0)
  886. {
  887. // Otherwise see if the type is an element with a known extension
  888. WCHAR strTemp[MCI_MAX_DEVICE_NAME_LENGTH];
  889. if (mciExtractDeviceType (lpstrType, strTemp, sizeof(strTemp)))
  890. uDeviceType = mciLookUpType (strTemp);
  891. }
  892. }
  893. /*
  894. If the command was not found in the custom table look in the type specific
  895. table
  896. */
  897. if (uDeviceType != 0)
  898. {
  899. wTable = mciLoadTableType (uDeviceType);
  900. if (wTable != MCI_TABLE_NOT_PRESENT)
  901. {
  902. lpCommand = FindCommandInTable (wTable, lpstrCommand, lpwMessage);
  903. if (lpCommand != NULL) {
  904. goto exit;
  905. }
  906. }
  907. }
  908. #endif
  909. //
  910. // If no match was found in the device or type specific tables
  911. // Look in the core table
  912. //
  913. wTable = 0;
  914. lpCommand = FindCommandInTable (wTable, lpstrCommand, lpwMessage);
  915. if (lpCommand == NULL) {
  916. wTable = (UINT)MCI_TABLE_NOT_PRESENT;
  917. }
  918. exit:;
  919. if (lpwTable != NULL) {
  920. *lpwTable = wTable;
  921. }
  922. #if DBG
  923. if (mciDebugLevel > 2)
  924. {
  925. dprintf2(("FindCommandItem INFO: check locks..."));
  926. mciCheckLocks();
  927. }
  928. #endif
  929. #if DBG
  930. dprintf3((" found: %ls in table %d", lpCommand ? lpCommand : L"(NULL)", wTable));
  931. #endif
  932. return lpCommand;
  933. }
  934. /*
  935. * @doc INTERNAL MCI
  936. * @func LPWSTR | mciCheckToken | Check to see if the command item matches
  937. * the given string, allowing multiple blanks in the input parameter to
  938. * match a corresponding single blank in the command token and ignoring
  939. * case.
  940. *
  941. * @parm LPCWSTR | lpstrToken | The command token to check
  942. *
  943. * @parm LPCWSTR | lpstrParam | The input parameter
  944. *
  945. * @rdesc NULL if no match, otherwise points to the first character
  946. * after the parameter
  947. *
  948. */
  949. STATICFN LPWSTR mciCheckToken (
  950. LPCWSTR lpstrToken,
  951. LPCWSTR lpstrParam)
  952. {
  953. /* Check for legal input */
  954. if (lpstrToken == NULL || lpstrParam == NULL) {
  955. return NULL;
  956. }
  957. while (*lpstrToken != '\0' && MCI_TOLOWER(*lpstrParam) == *lpstrToken)
  958. {
  959. // If the token contains a blank, allow more than one blank in the
  960. // parameter. If the next character is a blank, skip to the next
  961. // non-blank.
  962. if (*lpstrToken == ' ') {
  963. while (*lpstrParam == ' ') {
  964. ++lpstrParam;
  965. }
  966. } else {
  967. lpstrParam++;
  968. }
  969. lpstrToken++;
  970. }
  971. if (*lpstrToken != '\0'|| (*lpstrParam != '\0' && *lpstrParam != ' ')) {
  972. return NULL;
  973. } else {
  974. return (LPWSTR)lpstrParam;
  975. }
  976. }
  977. /*
  978. * @doc INTERNAL MCI
  979. * @func BOOL | mciParseInteger | Parse the given integer
  980. *
  981. * @parm LPWSTR FAR * | lplpstrInput | The string containing the argument.
  982. * It is updated and returned to the caller pointing to the first character
  983. * after the argument or to the first character that is in error.
  984. *
  985. * @parm LPDWORD | lpdwArgument | The place to put the output
  986. *
  987. * @rdesc Returns TRUE if not error
  988. *
  989. * @comm If there are colons in the input (':') the result is "colonized".
  990. * This means that each time a colon is read, the current result is written
  991. * and any subsequent digits are shifted left one byte. No one "segment"
  992. * can be more than 0xFF. For example, "0:1:2:3" is parsed to 0x03020100.
  993. *
  994. */
  995. STATICFN BOOL NEAR mciParseInteger (
  996. LPCWSTR FAR * lplpstrInput,
  997. LPDWORD lpdwArgument)
  998. {
  999. LPCWSTR lpstrInput = *lplpstrInput;
  1000. BOOL fDigitFound;
  1001. DWORD dwResult;
  1002. DWORD Shift = 0;
  1003. int nDigitPosition = 0;
  1004. BOOL bSigned = FALSE;
  1005. // Leading blanks have been removed by mciParseParams
  1006. if (*lpstrInput == '-')
  1007. {
  1008. ++lpstrInput;
  1009. bSigned = TRUE;
  1010. }
  1011. // Read digits
  1012. *lpdwArgument = 0; /* Initialize */
  1013. dwResult = 0;
  1014. fDigitFound = FALSE; /* Initialize */
  1015. while (*lpstrInput >= '0' && *lpstrInput <= '9' || *lpstrInput == ':')
  1016. {
  1017. // ':' indicates colonized data
  1018. if (*lpstrInput == ':')
  1019. {
  1020. // Cannot mix colonized and signed forms
  1021. if (bSigned)
  1022. {
  1023. dprintf1(("mciParseInteger: Bad integer: mixing signed and colonized forms"));
  1024. return FALSE;
  1025. }
  1026. // Check for overflow in accumulated colonized byte
  1027. if (dwResult > 0xFF) {
  1028. dprintf1(("mciParseInteger: Overflow in accumulated colonized byte"));
  1029. return FALSE;
  1030. }
  1031. // Copy and move to next byte converted in output
  1032. *lpdwArgument += dwResult << Shift;
  1033. Shift += 8;
  1034. ++lpstrInput;
  1035. // Initialize next colonized byte
  1036. dwResult = 0;
  1037. ++nDigitPosition;
  1038. // Only allow four colonized components
  1039. if (nDigitPosition > 3)
  1040. {
  1041. dprintf1(("mciParseInteger: Bad integer: Too many colonized components"));
  1042. return FALSE;
  1043. }
  1044. }
  1045. else
  1046. {
  1047. WCHAR cDigit = (WCHAR)(*lpstrInput++ - '0');
  1048. // Satisfies condition that at least one digit must be read
  1049. fDigitFound = TRUE;
  1050. if (dwResult > 0xFFFFFFFF / 10)
  1051. {
  1052. // Overflow if multiply was to occur
  1053. dprintf1(("mciParseInteger: Multiply overflow pending"));
  1054. return FALSE;
  1055. }
  1056. else
  1057. {
  1058. // Multiply for next digit
  1059. dwResult *= 10;
  1060. }
  1061. #if 0 // WIN32 Danger Will Robinson horribly bogus technique used here!
  1062. // Check to see if adding the new digit will overflow
  1063. if (dwResult != 0 && (-(int)dwResult) <= (int)cDigit) {
  1064. // Overflow will occur
  1065. dprintf1(("mciParseInteger: Add overflow pending"));
  1066. return FALSE;
  1067. }
  1068. #endif
  1069. // Add new digit
  1070. dwResult += cDigit;
  1071. }
  1072. }
  1073. if (nDigitPosition == 0)
  1074. {
  1075. // No colonized components
  1076. if (bSigned)
  1077. {
  1078. // Check for overflow from negation
  1079. if (dwResult > 0x7FFFFFFF) {
  1080. dprintf1(("mciParseInteger: Negation overflow"));
  1081. return FALSE;
  1082. }
  1083. // Negate result because a '-' sign was parsed
  1084. dwResult = (DWORD)-(int)dwResult;
  1085. }
  1086. *lpdwArgument = dwResult;
  1087. }
  1088. else
  1089. // Store last colonized component
  1090. {
  1091. // Check for overflow
  1092. if (dwResult > 0xFF) {
  1093. dprintf1(("mciParseInteger: Yet another overflow"));
  1094. return FALSE;
  1095. }
  1096. // Store component
  1097. *lpdwArgument += dwResult << Shift;
  1098. }
  1099. *lplpstrInput = lpstrInput;
  1100. /*
  1101. If there were no digits or if the digits were followed by a character
  1102. other than a blank or a '\0', then return a syntax error.
  1103. */
  1104. if (fDigitFound == FALSE ||
  1105. (*lpstrInput != ' ' && *lpstrInput != '\0')) {
  1106. dprintf1(("mciParseInteger: syntax error"));
  1107. return FALSE;
  1108. }
  1109. else {
  1110. dprintf4(("mciParseInteger(%ls, %08XH)", *lplpstrInput, *lpdwArgument));
  1111. return TRUE;
  1112. }
  1113. }
  1114. /*
  1115. * @doc INTERNAL MCI
  1116. * @func BOOL | mciParseConstant | Parse the given integer
  1117. *
  1118. * @parm LPWSTR FAR * | lplpstrInput | The string containing the argument.
  1119. * It is updated and returned to the caller pointing to the first character
  1120. * after the argument or to the first character that is in error.
  1121. *
  1122. * @parm LPDWORD | lpdwArgument | The place to put the output
  1123. *
  1124. * @parm LPWSTR | lpItem | Pointer into command table.
  1125. *
  1126. * @rdesc Returns TRUE if not error
  1127. *
  1128. */
  1129. STATICFN BOOL mciParseConstant (
  1130. LPCWSTR FAR * lplpstrInput,
  1131. LPDWORD lpdwArgument,
  1132. LPWSTR lpItem)
  1133. {
  1134. LPWSTR lpPrev;
  1135. DWORD dwValue;
  1136. UINT wID;
  1137. // Skip past constant header
  1138. (LPBYTE)lpItem = (LPBYTE)lpItem +
  1139. mciEatCommandEntry(lpItem, &dwValue, &wID);
  1140. while (TRUE)
  1141. {
  1142. LPWSTR lpstrAfter;
  1143. lpPrev = lpItem;
  1144. (LPBYTE)lpItem = (LPBYTE)lpItem +
  1145. mciEatCommandEntry (lpItem, &dwValue, &wID);
  1146. if (wID == MCI_END_CONSTANT) {
  1147. break;
  1148. }
  1149. if ((lpstrAfter = mciCheckToken (lpPrev, *lplpstrInput)) != NULL)
  1150. {
  1151. *lpdwArgument = dwValue;
  1152. *lplpstrInput = lpstrAfter;
  1153. return TRUE;
  1154. }
  1155. }
  1156. return mciParseInteger (lplpstrInput, lpdwArgument);
  1157. }
  1158. /*
  1159. * @doc INTERNAL MCI
  1160. * @func UINT | mciParseArgument | Parse the given argument
  1161. *
  1162. * @parm DWORD | dwValue | The argument value
  1163. *
  1164. * @parm UINT | wID | The argument ID
  1165. *
  1166. * @parm LPWSTR FAR * | lplpstrOutput | The string containing the argument.
  1167. * It is updated and returned to the caller pointing to the first character
  1168. * after the argument or to the first character that is in error.
  1169. *
  1170. * @parm LPDWORD | lpdwFlags | The output flags
  1171. *
  1172. * @parm LPDWORD | lpArgument | The place to put the output
  1173. *
  1174. * @rdesc Returns 0 if no error or
  1175. * @flag MCIERR_BAD_INTEGER | An integer argument could not be parsed
  1176. * @flag MCIERR_MISSING_STRING_ARGUMENT | An expected string argument
  1177. * @flag MCIERR_PARM_OVERFLOW | The output buffer was a NULL pointer
  1178. * was missing
  1179. *
  1180. */
  1181. STATICFN UINT mciParseArgument (
  1182. UINT uMessage,
  1183. DWORD dwValue,
  1184. UINT wID,
  1185. LPWSTR FAR * lplpstrOutput,
  1186. LPDWORD lpdwFlags,
  1187. LPWSTR lpArgument,
  1188. LPWSTR lpCurrentCommandItem)
  1189. {
  1190. LPCWSTR lpstrInput = *lplpstrOutput;
  1191. UINT wRetval = 0;
  1192. int dummy;
  1193. /* Switch on the argument type */
  1194. dprintf2(("mciParseArgument: msg=%04x, value=%08x, argument=%ls",
  1195. uMessage, dwValue, lpArgument));
  1196. switch (wID)
  1197. {
  1198. // The parameter is a flag
  1199. case MCI_FLAG:
  1200. break;
  1201. case MCI_CONSTANT:
  1202. if (*lpstrInput == '\0') {
  1203. wRetval = MCIERR_NO_INTEGER;
  1204. }
  1205. else if (!mciParseConstant (&lpstrInput, (LPDWORD)lpArgument,
  1206. lpCurrentCommandItem)) {
  1207. wRetval = MCIERR_BAD_CONSTANT;
  1208. }
  1209. // This entire else clause is only for WOW which doesn't exist
  1210. // on Win64
  1211. #ifndef _WIN64
  1212. else if ( WinmmRunningInWOW ) {
  1213. //
  1214. // Horrible hack: The command table does not contain
  1215. // enough information to perform the thunk correctly,
  1216. // hence this special case.
  1217. //
  1218. if ( uMessage == MCI_WINDOW
  1219. && dwValue == MCI_OVLY_WINDOW_HWND
  1220. && !IsWindow( (HWND)*(LPDWORD)lpArgument ) ) {
  1221. *(HWND *)lpArgument = HWND32(LOWORD(*(LPDWORD)lpArgument));
  1222. }
  1223. // If the message is MCI_SETVIDEO and we have
  1224. // MCI_DGV_SETVIDEO_VALUE it is possible that we have to
  1225. // convert the constant number to a palette handle, but ONLY
  1226. // if the ITEM field is "palette handle". We may not know
  1227. // that now as the string may be of the form:
  1228. // setvideo alias to NNN palette handle
  1229. // OR setvideo alias to NNN stream
  1230. // Hence any hacking for WOW has to be done when the
  1231. // parsing has been completed.
  1232. }
  1233. #endif // !WIN64
  1234. break;
  1235. /* Deal with the integer specific cases */
  1236. case MCI_HDC:
  1237. case MCI_HPAL:
  1238. case MCI_INTEGER:
  1239. case MCI_HWND:
  1240. if (!mciParseInteger (&lpstrInput, (LPDWORD)lpArgument)) {
  1241. wRetval = MCIERR_BAD_INTEGER;
  1242. }
  1243. #ifndef _WIN64
  1244. else if ( WinmmRunningInWOW ) {
  1245. switch (wID) {
  1246. case MCI_HPAL:
  1247. /* The parameter has an HPAL argument, try to parse it */
  1248. //
  1249. // If this specified hpal is not valid, mangle the hpal
  1250. // so that it appears to originate from WOW. I use GetObject
  1251. // to test the validity of the specified hpal.
  1252. //
  1253. #ifdef _WIN64
  1254. GetObject( (HPALETTE)*(PDWORD_PTR)lpArgument,sizeof(int), &dummy );
  1255. #else // !WIN64
  1256. if ( !GetObject( (HPALETTE)*(PDWORD_PTR)lpArgument,
  1257. sizeof(int), &dummy ) ) {
  1258. *(HPALETTE *)lpArgument =
  1259. HPALETTE32(LOWORD(*(LPDWORD)lpArgument));
  1260. }
  1261. #endif // !WIN64
  1262. break;
  1263. case MCI_HWND:
  1264. /* The parameter has an HWND argument, try to parse it */
  1265. //
  1266. // If this specified hwnd is not valid, mangle the hwnd
  1267. // so that it appears to originate from WOW.
  1268. //
  1269. if ( !IsWindow( (HWND)*(LPDWORD)lpArgument ) ) {
  1270. *(HWND *)lpArgument = HWND32(LOWORD(*(LPDWORD)lpArgument));
  1271. }
  1272. break;
  1273. case MCI_HDC:
  1274. //
  1275. // If this specified hdc is not valid, mangle the hdc
  1276. // so that it appears to originate from WOW. I use GetBkMode
  1277. // to test the validity of the specified hdc.
  1278. //
  1279. if ( !GetBkMode( (HDC)*(LPDWORD)lpArgument ) ) {
  1280. *(HDC *)lpArgument = HDC32(LOWORD(*(LPDWORD)lpArgument));
  1281. }
  1282. break;
  1283. case MCI_INTEGER:
  1284. default: ;
  1285. }
  1286. }
  1287. #endif // !WIN64
  1288. break; /* switch */
  1289. case MCI_RECT:
  1290. {
  1291. // Read in four integer parameters. Resulting structure is the
  1292. // same as a Windows RECT
  1293. LONG lTemp;
  1294. int n;
  1295. for (n = 0; n < 4; ++n)
  1296. {
  1297. if (!mciParseInteger (&lpstrInput, (LPDWORD)&lTemp))
  1298. {
  1299. wRetval = MCIERR_BAD_INTEGER;
  1300. break;
  1301. }
  1302. // Each component is a signed 16 bit number
  1303. if (lTemp > 32768 || lTemp < -32767)
  1304. {
  1305. wRetval = MCIERR_BAD_INTEGER;
  1306. break;
  1307. }
  1308. ((int FAR *)lpArgument)[n] = (int)lTemp;
  1309. // Remove leading blanks before next digit
  1310. while (*lpstrInput == ' ') ++lpstrInput;
  1311. }
  1312. break;
  1313. }
  1314. case MCI_STRING:
  1315. {
  1316. LPWSTR lpstrOutput;
  1317. /* The parameter has an string argument, read it */
  1318. // Leading blanks have been removed by mciParseParams
  1319. /* Are there any non-blank characters left in the input? */
  1320. if (*lpstrInput == '\0')
  1321. {
  1322. /* Return an error */
  1323. wRetval = MCIERR_MISSING_STRING_ARGUMENT;
  1324. break; /* switch */
  1325. }
  1326. if ((wRetval = mciEatToken (&lpstrInput, ' ', &lpstrOutput, FALSE))
  1327. != 0)
  1328. {
  1329. dprintf1(("mciParseArgument: error parsing string"));
  1330. return wRetval;
  1331. }
  1332. *(PDWORD_PTR)lpArgument = (DWORD_PTR)lpstrOutput;
  1333. // NOTE: mciSendString frees the output string after command execution
  1334. // by calling mciParserFree
  1335. break; /* switch */
  1336. } /* case */
  1337. } /* switch */
  1338. /* Update the output flags if there was no error */
  1339. if (wRetval == 0)
  1340. {
  1341. if (*lpdwFlags & dwValue)
  1342. {
  1343. if (wID == MCI_CONSTANT)
  1344. wRetval = MCIERR_FLAGS_NOT_COMPATIBLE;
  1345. else
  1346. wRetval = MCIERR_DUPLICATE_FLAGS;
  1347. } else
  1348. *lpdwFlags |= dwValue;
  1349. }
  1350. /*
  1351. Return the input pointer pointing at the first character after
  1352. the argument or to the first character that is in error
  1353. */
  1354. *lplpstrOutput = (LPWSTR)lpstrInput;
  1355. return wRetval;
  1356. }
  1357. /*
  1358. * @doc MCI INTERNAL
  1359. * @func UINT | mciParseParams | Parse the command parameters
  1360. *
  1361. * @parm LPCWSTR | lpstrParams | The parameter string
  1362. *
  1363. * @parm LPCWSTR | lpCommandList | The command table description
  1364. * of the command tokens
  1365. *
  1366. * @parm LPDWORD | lpdwFlags | Return the parsed flags here
  1367. *
  1368. * @parm LPDWORD | lpdwOutputParams | Return the list of parameters here
  1369. *
  1370. * @parm DWORD | dwParamsSize | The size allocated for the parameter list
  1371. *
  1372. * @parm LPWSTR FAR * FAR * | lpPointerList | A NULL terminated list of
  1373. * pointers allocated by this function that should be free'd when
  1374. * no longer needed. The list itself should be free'd also. In both
  1375. * cases, use mciFree().
  1376. *
  1377. * @parm PUINT | lpwParsingError | If not NULL then if the command is
  1378. * 'open', unrecognized keywords return an error here, and the
  1379. * function return value is 0 (unless other errors occur). This
  1380. * is used to allow reparsing of the command by mciLoadDevice
  1381. *
  1382. * @rdesc Returns zero if successful or one of the following error codes:
  1383. * @flag MCIERR_PARM_OVERFLOW | Not enough space for parameters
  1384. * @flag MCIERR_UNRECOGNIZED_KEYWORD | Unrecognized keyword
  1385. *
  1386. * @comm Any syntax error, including missing arguments, will result in
  1387. * a non-zero error return and invalid output data.
  1388. *
  1389. */
  1390. UINT mciParseParams (
  1391. UINT uMessage,
  1392. LPCWSTR lpstrParams,
  1393. LPCWSTR lpCommandList,
  1394. LPDWORD lpdwFlags,
  1395. LPWSTR lpOutputParams,
  1396. UINT wParamsSize,
  1397. LPWSTR FAR * FAR *lpPointerList,
  1398. PUINT lpwParsingError)
  1399. {
  1400. LPWSTR lpFirstCommandItem, lpCurrentCommandItem;
  1401. UINT wArgumentPosition, wErr, wDefaultID;
  1402. UINT uLen;
  1403. UINT wID;
  1404. DWORD dwValue, dwDefaultValue;
  1405. BOOL bOpenCommand;
  1406. LPWSTR FAR *lpstrPointerList;
  1407. UINT wPointers = 0;
  1408. UINT wHeaderSize;
  1409. LPWSTR lpDefaultCommandItem = NULL;
  1410. UINT wDefaultArgumentPosition;
  1411. if (lpwParsingError != NULL) {
  1412. *lpwParsingError = 0;
  1413. }
  1414. // If the parameter pointer is NULL, return
  1415. if (lpstrParams == NULL)
  1416. {
  1417. dprintf1(("Warning: lpstrParams is null in mciParseParams()"));
  1418. return 0;
  1419. }
  1420. if ((lpstrPointerList =
  1421. mciAlloc ((MCI_MAX_PARAM_SLOTS + 1) * sizeof (LPWSTR)))
  1422. == NULL)
  1423. {
  1424. *lpPointerList = NULL;
  1425. return MCIERR_OUT_OF_MEMORY;
  1426. }
  1427. // If this is the "open" command then allow parameter errors
  1428. bOpenCommand = lstrcmpiW((LPWSTR)lpCommandList, wszOpen) == 0;
  1429. /* Clear all the flags */
  1430. *lpdwFlags = 0;
  1431. /* Initialize the entry for the callback message window handle */
  1432. /* Each MCI parameter block uses the first word in the parameter */
  1433. /* block for the callback window handle. */
  1434. wHeaderSize = sizeof (((PMCI_GENERIC_PARMS)lpOutputParams)->dwCallback);
  1435. if (wHeaderSize > wParamsSize) { // bit of our caller...
  1436. wErr = MCIERR_PARAM_OVERFLOW;
  1437. goto error_exit;
  1438. }
  1439. /* Skip past the header */
  1440. lpFirstCommandItem = (LPWSTR)((LPBYTE)lpCommandList
  1441. + mciEatCommandEntry( lpCommandList, NULL, NULL ));
  1442. uLen = mciEatCommandEntry (lpFirstCommandItem, &dwValue, &wID);
  1443. /* Make room in lpdwOutputParams for the return arguments if any */
  1444. if (wID == MCI_RETURN)
  1445. {
  1446. (LPBYTE)lpFirstCommandItem = (LPBYTE)lpFirstCommandItem + uLen;
  1447. wHeaderSize += mciGetParamSize (dwValue, wID);
  1448. if (wHeaderSize > wParamsSize) {
  1449. wErr = MCIERR_PARAM_OVERFLOW;
  1450. goto error_exit;
  1451. }
  1452. }
  1453. (LPBYTE)lpOutputParams = (LPBYTE)lpOutputParams + wHeaderSize; // Each output parameter is LPWSTR size
  1454. // Scan the parameter string looking up each parameter in the given
  1455. // command list
  1456. while (TRUE)
  1457. {
  1458. LPCWSTR lpstrArgument = NULL;
  1459. /* Remove leading blanks */
  1460. while (*lpstrParams == ' ') { ++lpstrParams;
  1461. }
  1462. /* Break at end of parameter string */
  1463. if (*lpstrParams == '\0') { break;
  1464. }
  1465. /* Scan for this parameter in the command list */
  1466. lpCurrentCommandItem = lpFirstCommandItem;
  1467. wArgumentPosition = 0;
  1468. uLen = mciEatCommandEntry (lpCurrentCommandItem, &dwValue, &wID);
  1469. /* While there are more tokens in the Command List */
  1470. while (wID != MCI_END_COMMAND)
  1471. {
  1472. /* Check for a default argument if not already read */
  1473. if (lpDefaultCommandItem == NULL &&
  1474. *lpCurrentCommandItem == '\0')
  1475. {
  1476. // Remember default argument
  1477. lpDefaultCommandItem = lpCurrentCommandItem;
  1478. dwDefaultValue = dwValue;
  1479. wDefaultID = wID;
  1480. wDefaultArgumentPosition = wArgumentPosition;
  1481. // break;
  1482. }
  1483. /* Check to see if this token matches */
  1484. else if ((lpstrArgument =
  1485. mciCheckToken (lpCurrentCommandItem, lpstrParams)) != NULL)
  1486. { break;
  1487. }
  1488. /* This token did not match the input but advance the argument position */
  1489. wArgumentPosition += mciGetParamSize (dwValue, wID);
  1490. /* Go to next token */
  1491. (LPBYTE)lpCurrentCommandItem = (LPBYTE)lpCurrentCommandItem + uLen;
  1492. // Is this command parameter a constant?
  1493. if (wID == MCI_CONSTANT)
  1494. {
  1495. // Skip constant list
  1496. do
  1497. (LPBYTE)lpCurrentCommandItem = (LPBYTE)lpCurrentCommandItem
  1498. + mciEatCommandEntry (lpCurrentCommandItem, &dwValue, &wID);
  1499. while (wID != MCI_END_CONSTANT);
  1500. }
  1501. uLen = mciEatCommandEntry (lpCurrentCommandItem, &dwValue, &wID);
  1502. } /* while */
  1503. /* If there were no matches */
  1504. if (lpstrArgument == NULL)
  1505. {
  1506. // If a default argument exists then try it
  1507. if (lpDefaultCommandItem != NULL)
  1508. {
  1509. lpstrArgument = (LPWSTR)lpstrParams;
  1510. dwValue = dwDefaultValue;
  1511. wID = wDefaultID;
  1512. lpCurrentCommandItem = lpDefaultCommandItem;
  1513. wArgumentPosition = wDefaultArgumentPosition;
  1514. }
  1515. else
  1516. {
  1517. // Allow missing paramters on OPEN command if indicated by a
  1518. // non-null lpwParsingError address
  1519. if (!bOpenCommand || lpwParsingError == NULL)
  1520. {
  1521. wErr = MCIERR_UNRECOGNIZED_KEYWORD;
  1522. goto error_exit;
  1523. }
  1524. else
  1525. {
  1526. // Skip the parameter if OPEN command
  1527. while (*lpstrParams != ' ' && *lpstrParams != '\0')
  1528. ++lpstrParams;
  1529. if (lpwParsingError != NULL)
  1530. *lpwParsingError = MCIERR_UNRECOGNIZED_KEYWORD;
  1531. continue;
  1532. }
  1533. }
  1534. }
  1535. /* Is there room in the output buffer for this argument? */
  1536. if (wArgumentPosition + wHeaderSize + mciGetParamSize (dwValue, wID)
  1537. > wParamsSize)
  1538. {
  1539. dprintf1(("mciParseParams: parameter space overflow"));
  1540. wErr = MCIERR_PARAM_OVERFLOW;
  1541. goto error_exit;
  1542. }
  1543. // Remove leading blanks
  1544. while (*lpstrArgument == ' ') {
  1545. ++lpstrArgument;
  1546. }
  1547. /* Process this parameter, filling in any flags or arguments */
  1548. if ((wErr = mciParseArgument (uMessage, dwValue, wID,
  1549. (LPWSTR FAR *)&lpstrArgument,
  1550. lpdwFlags,
  1551. (LPWSTR)((LPBYTE)lpOutputParams + wArgumentPosition),
  1552. lpCurrentCommandItem))
  1553. != 0)
  1554. {
  1555. goto error_exit;
  1556. }
  1557. lpstrParams = lpstrArgument;
  1558. if (wID == MCI_STRING)
  1559. {
  1560. if (wPointers >= MCI_MAX_PARAM_SLOTS)
  1561. {
  1562. dprintf1(("Warning: Out of pointer list slots in mciParseParams"));
  1563. break;
  1564. }
  1565. lpstrPointerList[wPointers++] =
  1566. *((LPWSTR *)((LPBYTE)lpOutputParams + wArgumentPosition));
  1567. }
  1568. /* Continue reading the parameter string */
  1569. } /* while */
  1570. // Terminate list
  1571. lpstrPointerList[wPointers] = NULL;
  1572. // Copy reference for caller
  1573. *lpPointerList = lpstrPointerList;
  1574. //
  1575. // This is a hack to make sure that
  1576. // the string version of MCI_SETVIDEO can actually set a palette
  1577. // when called from a WOW app.
  1578. //
  1579. #ifndef _WIN64
  1580. if (WinmmRunningInWOW)
  1581. {
  1582. DWORD dummy; // To hold response from GetObject
  1583. if ((uMessage == MCI_SETVIDEO)
  1584. && (*lpdwFlags & MCI_DGV_SETVIDEO_VALUE)
  1585. && (*lpdwFlags & MCI_DGV_SETVIDEO_ITEM)
  1586. && (*(LPDWORD)lpOutputParams == MCI_DGV_SETVIDEO_PALHANDLE)
  1587. && (!GetObject( (HPALETTE)*(((LPDWORD)lpOutputParams)+1),
  1588. sizeof(int), &dummy ) ))
  1589. {
  1590. dprintf2(("Replacing WOW palette handle %x", *(HPALETTE *)(((LPDWORD)lpOutputParams)+1)));
  1591. *(HPALETTE *)(((LPDWORD)lpOutputParams)+1) =
  1592. HPALETTE32(LOWORD(*(((LPDWORD)lpOutputParams)+1) ));
  1593. dprintf2(("WOW palette handle now %x", *(HPALETTE *)(((LPDWORD)lpOutputParams)+1)));
  1594. }
  1595. }
  1596. #endif // !WIN64
  1597. // Return Success
  1598. return 0;
  1599. error_exit:
  1600. *lpPointerList = NULL;
  1601. // Terminate list
  1602. lpstrPointerList[wPointers] = NULL;
  1603. mciParserFree (lpstrPointerList);
  1604. return(wErr);
  1605. }
  1606. /*
  1607. * @doc INTERNAL MCI
  1608. * @func UINT | mciParseCommand | This function converts an MCI
  1609. * control string to an MCI control message suitable for sending to
  1610. * <f mciSendCommand>. The input string usually comes from <f mciSendString>
  1611. * and always has the device name stripped off the front.
  1612. *
  1613. * @parm MCIDEVICEID | wDeviceID | Identifies the device. First searches the
  1614. * parsing table belonging to the driver.
  1615. * Then searches the command tables matching the type
  1616. * of the given device. Then searches the core command table.
  1617. *
  1618. * @parm LPWSTR | lpstrCommand | An MCI control command without
  1619. * a device name prefix. There must be no leading or trailing
  1620. * blanks.
  1621. *
  1622. * @parm LPCWSTR | lpstrDeviceName | The device name (second token on the
  1623. * command line). It is used to identify the device type.
  1624. *
  1625. * @parm LPWSTR FAR * | lpCommandList | If not NULL then the address of
  1626. * the command list for the parsed command (if successful) is copied here.
  1627. * It is used later by mciSendString when parsing arguments
  1628. *
  1629. * @parm PUINT | lpwTable | The table resource ID to be unlocked
  1630. * after parsing. Returned to caller.
  1631. *
  1632. * @rdesc Returns the command ID or 0 if not found.
  1633. *
  1634. */
  1635. UINT mciParseCommand (
  1636. MCIDEVICEID wDeviceID,
  1637. LPWSTR lpstrCommand,
  1638. LPCWSTR lpstrDeviceName,
  1639. LPWSTR * lpCommandList,
  1640. PUINT lpwTable)
  1641. {
  1642. LPWSTR lpCommandItem;
  1643. UINT wMessage;
  1644. dprintf2(("mciParseCommand(%ls, %ls)", lpstrCommand ? lpstrCommand : L"(NULL)", lpstrDeviceName ? lpstrDeviceName : L"(NULL)"));
  1645. // Put the command in lower case
  1646. // mciToLower (lpstrCommand);
  1647. // Look up lpstrCommand in the parser's command tables.
  1648. if ((lpCommandItem = FindCommandItem (wDeviceID, lpstrDeviceName,
  1649. lpstrCommand,
  1650. &wMessage, lpwTable))
  1651. == NULL) {
  1652. return 0;
  1653. }
  1654. /* Return the command list to the caller */
  1655. if (lpCommandList != NULL) {
  1656. *lpCommandList = lpCommandItem;
  1657. } else {
  1658. dprintf1(("Warning: NULL lpCommandList in mciParseCommand"));
  1659. }
  1660. return wMessage;
  1661. }
  1662. /*
  1663. * @doc INTERNAL MCI
  1664. * @func VOID | mciParserFree | Free any buffers allocated to
  1665. * receive string arguments.
  1666. *
  1667. * @parm LPWSTR FAR * | lpstrPointerList | A NULL terminated list of far
  1668. * pointers to strings to be free'd
  1669. *
  1670. */
  1671. VOID mciParserFree (
  1672. LPWSTR FAR *lpstrPointerList)
  1673. {
  1674. LPWSTR FAR *lpstrOriginal = lpstrPointerList;
  1675. if (lpstrPointerList == NULL) {
  1676. return;
  1677. }
  1678. while (*lpstrPointerList != NULL) {
  1679. mciFree (*lpstrPointerList++);
  1680. }
  1681. mciFree (lpstrOriginal);
  1682. }