1 /* 2 * MCI internal functions 3 * 4 * Copyright 1998/1999 Eric Pouech 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* TODO: 22 * - implement WINMM (32bit) multitasking and use it in all MCI drivers 23 * instead of the home grown one 24 * - 16bit mmTaskXXX functions are currently broken because the 16 25 * loader does not support binary command lines => provide Wine's 26 * own mmtask.tsk not using binary command line. 27 * - correctly handle the MCI_ALL_DEVICE_ID in functions. 28 * - finish mapping 16 <=> 32 of MCI structures and commands 29 * - implement auto-open feature (ie, when a string command is issued 30 * for a not yet opened device, MCI automatically opens it) 31 * - use a default registry setting to replace the [mci] section in 32 * configuration file (layout of info in registry should be compatible 33 * with all Windows' version - which use different layouts of course) 34 * - implement automatic open 35 * + only works on string interface, on regular devices (don't work on all 36 * nor custom devices) 37 * - command table handling isn't thread safe 38 */ 39 40 #include <stdlib.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <string.h> 44 45 #include "windef.h" 46 #include "winbase.h" 47 #include "wingdi.h" 48 #include "mmsystem.h" 49 #include "winuser.h" 50 #include "winnls.h" 51 #include "winreg.h" 52 #include "wownt32.h" 53 54 #include "digitalv.h" 55 #include "winemm.h" 56 57 #include "wine/debug.h" 58 59 WINE_DEFAULT_DEBUG_CHANNEL(mci); 60 61 /* First MCI valid device ID (0 means error) */ 62 #define MCI_MAGIC 0x0001 63 64 /* MCI settings */ 65 static const WCHAR wszHklmMci [] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\MCI"; 66 67 static WINE_MCIDRIVER *MciDrivers; 68 69 static UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data); 70 static UINT MCI_SetCommandTable(HGLOBAL hMem, UINT uDevType); 71 72 /* dup a string and uppercase it */ 73 static inline LPWSTR str_dup_upper( LPCWSTR str ) 74 { 75 INT len = (lstrlenW(str) + 1) * sizeof(WCHAR); 76 LPWSTR p = HeapAlloc( GetProcessHeap(), 0, len ); 77 if (p) 78 { 79 memcpy( p, str, len ); 80 CharUpperW( p ); 81 } 82 return p; 83 } 84 85 /************************************************************************** 86 * MCI_GetDriver [internal] 87 */ 88 static LPWINE_MCIDRIVER MCI_GetDriver(UINT wDevID) 89 { 90 LPWINE_MCIDRIVER wmd = 0; 91 92 EnterCriticalSection(&WINMM_cs); 93 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) { 94 if (wmd->wDeviceID == wDevID) 95 break; 96 } 97 LeaveCriticalSection(&WINMM_cs); 98 return wmd; 99 } 100 101 /************************************************************************** 102 * MCI_GetDriverFromString [internal] 103 */ 104 static UINT MCI_GetDriverFromString(LPCWSTR lpstrName) 105 { 106 LPWINE_MCIDRIVER wmd; 107 UINT ret = 0; 108 109 if (!lpstrName) 110 return 0; 111 112 if (!wcsicmp(lpstrName, L"ALL")) 113 return MCI_ALL_DEVICE_ID; 114 115 EnterCriticalSection(&WINMM_cs); 116 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) { 117 if (wmd->lpstrAlias && wcsicmp(wmd->lpstrAlias, lpstrName) == 0) { 118 ret = wmd->wDeviceID; 119 break; 120 } 121 } 122 LeaveCriticalSection(&WINMM_cs); 123 124 return ret; 125 } 126 127 /************************************************************************** 128 * MCI_MessageToString [internal] 129 */ 130 static const char* MCI_MessageToString(UINT wMsg) 131 { 132 #define CASE(s) case (s): return #s 133 134 switch (wMsg) { 135 CASE(DRV_LOAD); 136 CASE(DRV_ENABLE); 137 CASE(DRV_OPEN); 138 CASE(DRV_CLOSE); 139 CASE(DRV_DISABLE); 140 CASE(DRV_FREE); 141 CASE(DRV_CONFIGURE); 142 CASE(DRV_QUERYCONFIGURE); 143 CASE(DRV_INSTALL); 144 CASE(DRV_REMOVE); 145 CASE(DRV_EXITSESSION); 146 CASE(DRV_EXITAPPLICATION); 147 CASE(DRV_POWER); 148 CASE(MCI_BREAK); 149 CASE(MCI_CLOSE); 150 CASE(MCI_CLOSE_DRIVER); 151 CASE(MCI_COPY); 152 CASE(MCI_CUE); 153 CASE(MCI_CUT); 154 CASE(MCI_DELETE); 155 CASE(MCI_ESCAPE); 156 CASE(MCI_FREEZE); 157 CASE(MCI_PAUSE); 158 CASE(MCI_PLAY); 159 CASE(MCI_GETDEVCAPS); 160 CASE(MCI_INFO); 161 CASE(MCI_LOAD); 162 CASE(MCI_OPEN); 163 CASE(MCI_OPEN_DRIVER); 164 CASE(MCI_PASTE); 165 CASE(MCI_PUT); 166 CASE(MCI_REALIZE); 167 CASE(MCI_RECORD); 168 CASE(MCI_RESUME); 169 CASE(MCI_SAVE); 170 CASE(MCI_SEEK); 171 CASE(MCI_SET); 172 CASE(MCI_SOUND); 173 CASE(MCI_SPIN); 174 CASE(MCI_STATUS); 175 CASE(MCI_STEP); 176 CASE(MCI_STOP); 177 CASE(MCI_SYSINFO); 178 CASE(MCI_UNFREEZE); 179 CASE(MCI_UPDATE); 180 CASE(MCI_WHERE); 181 CASE(MCI_WINDOW); 182 /* constants for digital video */ 183 CASE(MCI_CAPTURE); 184 CASE(MCI_MONITOR); 185 CASE(MCI_RESERVE); 186 CASE(MCI_SETAUDIO); 187 CASE(MCI_SIGNAL); 188 CASE(MCI_SETVIDEO); 189 CASE(MCI_QUALITY); 190 CASE(MCI_LIST); 191 CASE(MCI_UNDO); 192 CASE(MCI_CONFIGURE); 193 CASE(MCI_RESTORE); 194 #undef CASE 195 default: 196 return wine_dbg_sprintf("MCI_<<%04X>>", wMsg); 197 } 198 } 199 200 static LPWSTR MCI_strdupAtoW( LPCSTR str ) 201 { 202 LPWSTR ret; 203 INT len; 204 205 if (!str) return NULL; 206 len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); 207 ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); 208 if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); 209 return ret; 210 } 211 212 static int MCI_MapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR *dwParam2) 213 { 214 if (msg < DRV_RESERVED) return 0; 215 216 switch (msg) 217 { 218 case MCI_CLOSE: 219 case MCI_CONFIGURE: 220 case MCI_PLAY: 221 case MCI_SEEK: 222 case MCI_STOP: 223 case MCI_PAUSE: 224 case MCI_GETDEVCAPS: 225 case MCI_SPIN: 226 case MCI_SET: 227 case MCI_STEP: 228 case MCI_RECORD: 229 case MCI_BREAK: 230 case MCI_STATUS: 231 case MCI_CUE: 232 case MCI_REALIZE: 233 case MCI_PUT: 234 case MCI_WHERE: 235 case MCI_FREEZE: 236 case MCI_UNFREEZE: 237 case MCI_CUT: 238 case MCI_COPY: 239 case MCI_PASTE: 240 case MCI_UPDATE: 241 case MCI_RESUME: 242 case MCI_DELETE: 243 case MCI_MONITOR: 244 case MCI_SIGNAL: 245 case MCI_UNDO: 246 return 0; 247 248 case MCI_OPEN: 249 { /* MCI_ANIM_OPEN_PARMS is the largest known MCI_OPEN_PARMS 250 * structure, larger than MCI_WAVE_OPEN_PARMS */ 251 MCI_ANIM_OPEN_PARMSA *mci_openA = (MCI_ANIM_OPEN_PARMSA*)*dwParam2; 252 MCI_ANIM_OPEN_PARMSW *mci_openW; 253 DWORD_PTR *ptr; 254 255 ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD_PTR) + sizeof(*mci_openW)); 256 if (!ptr) return -1; 257 258 *ptr++ = *dwParam2; /* save the previous pointer */ 259 *dwParam2 = (DWORD_PTR)ptr; 260 mci_openW = (MCI_ANIM_OPEN_PARMSW *)ptr; 261 262 if (dwParam1 & MCI_NOTIFY) 263 mci_openW->dwCallback = mci_openA->dwCallback; 264 265 if (dwParam1 & MCI_OPEN_TYPE) 266 { 267 if (dwParam1 & MCI_OPEN_TYPE_ID) 268 mci_openW->lpstrDeviceType = (LPCWSTR)mci_openA->lpstrDeviceType; 269 else 270 mci_openW->lpstrDeviceType = MCI_strdupAtoW(mci_openA->lpstrDeviceType); 271 } 272 if (dwParam1 & MCI_OPEN_ELEMENT) 273 { 274 if (dwParam1 & MCI_OPEN_ELEMENT_ID) 275 mci_openW->lpstrElementName = (LPCWSTR)mci_openA->lpstrElementName; 276 else 277 mci_openW->lpstrElementName = MCI_strdupAtoW(mci_openA->lpstrElementName); 278 } 279 if (dwParam1 & MCI_OPEN_ALIAS) 280 mci_openW->lpstrAlias = MCI_strdupAtoW(mci_openA->lpstrAlias); 281 /* We don't know how many DWORD follow, as 282 * the structure depends on the device. */ 283 if (HIWORD(dwParam1)) 284 memcpy(&mci_openW->dwStyle, &mci_openA->dwStyle, sizeof(MCI_ANIM_OPEN_PARMSW) - sizeof(MCI_OPEN_PARMSW)); 285 } 286 return 1; 287 288 case MCI_WINDOW: 289 if (dwParam1 & MCI_ANIM_WINDOW_TEXT) 290 { 291 MCI_ANIM_WINDOW_PARMSA *mci_windowA = (MCI_ANIM_WINDOW_PARMSA *)*dwParam2; 292 MCI_ANIM_WINDOW_PARMSW *mci_windowW; 293 294 mci_windowW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_windowW)); 295 if (!mci_windowW) return -1; 296 297 *dwParam2 = (DWORD_PTR)mci_windowW; 298 299 mci_windowW->lpstrText = MCI_strdupAtoW(mci_windowA->lpstrText); 300 301 if (dwParam1 & MCI_NOTIFY) 302 mci_windowW->dwCallback = mci_windowA->dwCallback; 303 if (dwParam1 & MCI_ANIM_WINDOW_HWND) 304 mci_windowW->hWnd = mci_windowA->hWnd; 305 if (dwParam1 & MCI_ANIM_WINDOW_STATE) 306 mci_windowW->nCmdShow = mci_windowA->nCmdShow; 307 308 return 1; 309 } 310 return 0; 311 312 case MCI_SYSINFO: 313 if (dwParam1 & (MCI_SYSINFO_INSTALLNAME | MCI_SYSINFO_NAME)) 314 { 315 MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*dwParam2; 316 MCI_SYSINFO_PARMSW *mci_sysinfoW; 317 DWORD_PTR *ptr; 318 319 ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_sysinfoW) + sizeof(DWORD_PTR)); 320 if (!ptr) return -1; 321 322 *ptr++ = *dwParam2; /* save the previous pointer */ 323 *dwParam2 = (DWORD_PTR)ptr; 324 mci_sysinfoW = (MCI_SYSINFO_PARMSW *)ptr; 325 326 if (dwParam1 & MCI_NOTIFY) 327 mci_sysinfoW->dwCallback = mci_sysinfoA->dwCallback; 328 329 /* Size is measured in numbers of characters, despite what MSDN says. */ 330 mci_sysinfoW->dwRetSize = mci_sysinfoA->dwRetSize; 331 mci_sysinfoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_sysinfoW->dwRetSize * sizeof(WCHAR)); 332 mci_sysinfoW->dwNumber = mci_sysinfoA->dwNumber; 333 mci_sysinfoW->wDeviceType = mci_sysinfoA->wDeviceType; 334 return 1; 335 } 336 return 0; 337 case MCI_INFO: 338 { 339 MCI_DGV_INFO_PARMSA *mci_infoA = (MCI_DGV_INFO_PARMSA *)*dwParam2; 340 MCI_DGV_INFO_PARMSW *mci_infoW; 341 DWORD_PTR *ptr; 342 343 ptr = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_infoW) + sizeof(DWORD_PTR)); 344 if (!ptr) return -1; 345 346 *ptr++ = *dwParam2; /* save the previous pointer */ 347 *dwParam2 = (DWORD_PTR)ptr; 348 mci_infoW = (MCI_DGV_INFO_PARMSW *)ptr; 349 350 if (dwParam1 & MCI_NOTIFY) 351 mci_infoW->dwCallback = mci_infoA->dwCallback; 352 353 /* Size is measured in numbers of characters. */ 354 mci_infoW->dwRetSize = mci_infoA->dwRetSize; 355 mci_infoW->lpstrReturn = HeapAlloc(GetProcessHeap(), 0, mci_infoW->dwRetSize * sizeof(WCHAR)); 356 if (dwParam1 & MCI_DGV_INFO_ITEM) 357 mci_infoW->dwItem = mci_infoA->dwItem; 358 return 1; 359 } 360 case MCI_SAVE: 361 case MCI_LOAD: 362 case MCI_CAPTURE: 363 case MCI_RESTORE: 364 { /* All these commands have the same layout: callback + string + optional rect */ 365 MCI_OVLY_LOAD_PARMSA *mci_loadA = (MCI_OVLY_LOAD_PARMSA *)*dwParam2; 366 MCI_OVLY_LOAD_PARMSW *mci_loadW; 367 368 mci_loadW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_loadW)); 369 if (!mci_loadW) return -1; 370 371 *dwParam2 = (DWORD_PTR)mci_loadW; 372 if (dwParam1 & MCI_NOTIFY) 373 mci_loadW->dwCallback = mci_loadA->dwCallback; 374 mci_loadW->lpfilename = MCI_strdupAtoW(mci_loadA->lpfilename); 375 if ((MCI_SAVE == msg && dwParam1 & MCI_DGV_RECT) || 376 (MCI_LOAD == msg && dwParam1 & MCI_OVLY_RECT) || 377 (MCI_CAPTURE == msg && dwParam1 & MCI_DGV_CAPTURE_AT) || 378 (MCI_RESTORE == msg && dwParam1 & MCI_DGV_RESTORE_AT)) 379 mci_loadW->rc = mci_loadA->rc; 380 return 1; 381 } 382 case MCI_SOUND: 383 case MCI_ESCAPE: 384 { /* All these commands have the same layout: callback + string */ 385 MCI_VD_ESCAPE_PARMSA *mci_vd_escapeA = (MCI_VD_ESCAPE_PARMSA *)*dwParam2; 386 MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW; 387 388 mci_vd_escapeW = HeapAlloc(GetProcessHeap(), 0, sizeof(*mci_vd_escapeW)); 389 if (!mci_vd_escapeW) return -1; 390 391 *dwParam2 = (DWORD_PTR)mci_vd_escapeW; 392 if (dwParam1 & MCI_NOTIFY) 393 mci_vd_escapeW->dwCallback = mci_vd_escapeA->dwCallback; 394 mci_vd_escapeW->lpstrCommand = MCI_strdupAtoW(mci_vd_escapeA->lpstrCommand); 395 return 1; 396 } 397 case MCI_SETAUDIO: 398 case MCI_SETVIDEO: 399 if (!(dwParam1 & (MCI_DGV_SETVIDEO_QUALITY | MCI_DGV_SETVIDEO_ALG 400 | MCI_DGV_SETAUDIO_QUALITY | MCI_DGV_SETAUDIO_ALG))) 401 return 0; 402 /* fall through to default */ 403 case MCI_RESERVE: 404 case MCI_QUALITY: 405 case MCI_LIST: 406 default: 407 FIXME("Message %s needs translation\n", MCI_MessageToString(msg)); 408 return 0; /* pass through untouched */ 409 } 410 } 411 412 static void MCI_UnmapMsgAtoW(UINT msg, DWORD_PTR dwParam1, DWORD_PTR dwParam2, 413 DWORD result) 414 { 415 switch (msg) 416 { 417 case MCI_OPEN: 418 { 419 DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1; 420 MCI_OPEN_PARMSA *mci_openA = (MCI_OPEN_PARMSA *)*ptr; 421 MCI_OPEN_PARMSW *mci_openW = (MCI_OPEN_PARMSW *)dwParam2; 422 423 mci_openA->wDeviceID = mci_openW->wDeviceID; 424 425 if (dwParam1 & MCI_OPEN_TYPE) 426 { 427 if (!(dwParam1 & MCI_OPEN_TYPE_ID)) 428 HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrDeviceType); 429 } 430 if (dwParam1 & MCI_OPEN_ELEMENT) 431 { 432 if (!(dwParam1 & MCI_OPEN_ELEMENT_ID)) 433 HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrElementName); 434 } 435 if (dwParam1 & MCI_OPEN_ALIAS) 436 HeapFree(GetProcessHeap(), 0, (LPWSTR)mci_openW->lpstrAlias); 437 HeapFree(GetProcessHeap(), 0, ptr); 438 } 439 break; 440 case MCI_WINDOW: 441 if (dwParam1 & MCI_ANIM_WINDOW_TEXT) 442 { 443 MCI_ANIM_WINDOW_PARMSW *mci_windowW = (MCI_ANIM_WINDOW_PARMSW *)dwParam2; 444 445 HeapFree(GetProcessHeap(), 0, (void*)mci_windowW->lpstrText); 446 HeapFree(GetProcessHeap(), 0, mci_windowW); 447 } 448 break; 449 450 case MCI_SYSINFO: 451 if (dwParam1 & (MCI_SYSINFO_INSTALLNAME | MCI_SYSINFO_NAME)) 452 { 453 DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1; 454 MCI_SYSINFO_PARMSA *mci_sysinfoA = (MCI_SYSINFO_PARMSA *)*ptr; 455 MCI_SYSINFO_PARMSW *mci_sysinfoW = (MCI_SYSINFO_PARMSW *)dwParam2; 456 457 if (!result) 458 { 459 WideCharToMultiByte(CP_ACP, 0, 460 mci_sysinfoW->lpstrReturn, -1, 461 mci_sysinfoA->lpstrReturn, mci_sysinfoA->dwRetSize, 462 NULL, NULL); 463 } 464 465 HeapFree(GetProcessHeap(), 0, mci_sysinfoW->lpstrReturn); 466 HeapFree(GetProcessHeap(), 0, ptr); 467 } 468 break; 469 case MCI_INFO: 470 { 471 DWORD_PTR *ptr = (DWORD_PTR *)dwParam2 - 1; 472 MCI_INFO_PARMSA *mci_infoA = (MCI_INFO_PARMSA *)*ptr; 473 MCI_INFO_PARMSW *mci_infoW = (MCI_INFO_PARMSW *)dwParam2; 474 475 if (!result) 476 { 477 WideCharToMultiByte(CP_ACP, 0, 478 mci_infoW->lpstrReturn, -1, 479 mci_infoA->lpstrReturn, mci_infoA->dwRetSize, 480 NULL, NULL); 481 } 482 483 HeapFree(GetProcessHeap(), 0, mci_infoW->lpstrReturn); 484 HeapFree(GetProcessHeap(), 0, ptr); 485 } 486 break; 487 case MCI_SAVE: 488 case MCI_LOAD: 489 case MCI_CAPTURE: 490 case MCI_RESTORE: 491 { /* All these commands have the same layout: callback + string + optional rect */ 492 MCI_OVLY_LOAD_PARMSW *mci_loadW = (MCI_OVLY_LOAD_PARMSW *)dwParam2; 493 494 HeapFree(GetProcessHeap(), 0, (void*)mci_loadW->lpfilename); 495 HeapFree(GetProcessHeap(), 0, mci_loadW); 496 } 497 break; 498 case MCI_SOUND: 499 case MCI_ESCAPE: 500 { /* All these commands have the same layout: callback + string */ 501 MCI_VD_ESCAPE_PARMSW *mci_vd_escapeW = (MCI_VD_ESCAPE_PARMSW *)dwParam2; 502 503 HeapFree(GetProcessHeap(), 0, (void*)mci_vd_escapeW->lpstrCommand); 504 HeapFree(GetProcessHeap(), 0, mci_vd_escapeW); 505 } 506 break; 507 508 default: 509 FIXME("Message %s needs unmapping\n", MCI_MessageToString(msg)); 510 break; 511 } 512 } 513 514 /************************************************************************** 515 * MCI_GetDevTypeFromFileName [internal] 516 */ 517 static DWORD MCI_GetDevTypeFromFileName(LPCWSTR fileName, LPWSTR buf, UINT len) 518 { 519 LPCWSTR tmp; 520 HKEY hKey; 521 if ((tmp = wcsrchr(fileName, '.'))) { 522 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, 523 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\MCI Extensions", 524 0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) { 525 DWORD dwLen = len; 526 LONG lRet = RegQueryValueExW( hKey, tmp + 1, 0, 0, (void*)buf, &dwLen ); 527 RegCloseKey( hKey ); 528 if (lRet == ERROR_SUCCESS) return 0; 529 } 530 TRACE("No ...\\MCI Extensions entry for %s found.\n", debugstr_w(tmp)); 531 } 532 return MCIERR_EXTENSION_NOT_FOUND; 533 } 534 535 /************************************************************************** 536 * MCI_GetDevTypeFromResource [internal] 537 */ 538 static UINT MCI_GetDevTypeFromResource(LPCWSTR lpstrName) 539 { 540 WCHAR buf[32]; 541 UINT uDevType; 542 for (uDevType = MCI_DEVTYPE_FIRST; uDevType <= MCI_DEVTYPE_LAST; uDevType++) { 543 if (LoadStringW(hWinMM32Instance, uDevType, buf, ARRAY_SIZE(buf))) { 544 /* FIXME: ignore digits suffix */ 545 if (!wcsicmp(buf, lpstrName)) 546 return uDevType; 547 } 548 } 549 return 0; 550 } 551 552 #define MAX_MCICMDTABLE 20 553 #define MCI_COMMAND_TABLE_NOT_LOADED 0xFFFE 554 555 typedef struct tagWINE_MCICMDTABLE { 556 UINT uDevType; 557 HGLOBAL hMem; 558 const BYTE* lpTable; 559 UINT nVerbs; /* number of verbs in command table */ 560 LPCWSTR* aVerbs; /* array of verbs to speed up the verb look up process */ 561 } WINE_MCICMDTABLE, *LPWINE_MCICMDTABLE; 562 563 static WINE_MCICMDTABLE S_MciCmdTable[MAX_MCICMDTABLE]; 564 565 /************************************************************************** 566 * MCI_IsCommandTableValid [internal] 567 */ 568 static BOOL MCI_IsCommandTableValid(UINT uTbl) 569 { 570 const BYTE* lmem; 571 LPCWSTR str; 572 DWORD flg; 573 WORD eid; 574 int idx = 0; 575 BOOL inCst = FALSE; 576 577 TRACE("Dumping cmdTbl=%d [lpTable=%p devType=%d]\n", 578 uTbl, S_MciCmdTable[uTbl].lpTable, S_MciCmdTable[uTbl].uDevType); 579 580 if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable) 581 return FALSE; 582 583 lmem = S_MciCmdTable[uTbl].lpTable; 584 do { 585 str = (LPCWSTR)lmem; 586 lmem += (lstrlenW(str) + 1) * sizeof(WCHAR); 587 flg = *(const DWORD*)lmem; 588 eid = *(const WORD*)(lmem + sizeof(DWORD)); 589 lmem += sizeof(DWORD) + sizeof(WORD); 590 idx ++; 591 /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */ 592 switch (eid) { 593 case MCI_COMMAND_HEAD: if (!*str || !flg) return FALSE; idx = 0; break; /* check unicity of str in table */ 594 case MCI_STRING: if (inCst) return FALSE; break; 595 case MCI_HWND: /* Occurs inside MCI_CONSTANT as in "window handle default" */ 596 case MCI_HPAL: 597 case MCI_HDC: 598 case MCI_INTEGER: if (!*str) return FALSE; break; 599 case MCI_END_COMMAND: if (*str || flg || idx == 0) return FALSE; idx = 0; break; 600 case MCI_RETURN: if (*str || idx != 1) return FALSE; break; 601 case MCI_FLAG: if (!*str) return FALSE; break; 602 case MCI_END_COMMAND_LIST: if (*str || flg) return FALSE; idx = 0; break; 603 case MCI_RECT: if (!*str || inCst) return FALSE; break; 604 case MCI_CONSTANT: if (inCst) return FALSE; inCst = TRUE; break; 605 case MCI_END_CONSTANT: if (*str || flg || !inCst) return FALSE; inCst = FALSE; break; 606 default: return FALSE; 607 } 608 } while (eid != MCI_END_COMMAND_LIST); 609 return TRUE; 610 } 611 612 /************************************************************************** 613 * MCI_DumpCommandTable [internal] 614 */ 615 static BOOL MCI_DumpCommandTable(UINT uTbl) 616 { 617 const BYTE* lmem; 618 LPCWSTR str; 619 WORD eid; 620 621 if (!MCI_IsCommandTableValid(uTbl)) { 622 ERR("Ooops: %d is not valid\n", uTbl); 623 return FALSE; 624 } 625 626 lmem = S_MciCmdTable[uTbl].lpTable; 627 do { 628 do { 629 /* DWORD flg; */ 630 str = (LPCWSTR)lmem; 631 lmem += (lstrlenW(str) + 1) * sizeof(WCHAR); 632 /* flg = *(const DWORD*)lmem; */ 633 eid = *(const WORD*)(lmem + sizeof(DWORD)); 634 /* TRACE("cmd=%s %08lx %04x\n", debugstr_w(str), flg, eid); */ 635 lmem += sizeof(DWORD) + sizeof(WORD); 636 } while (eid != MCI_END_COMMAND && eid != MCI_END_COMMAND_LIST); 637 /* EPP TRACE(" => end of command%s\n", (eid == MCI_END_COMMAND_LIST) ? " list" : ""); */ 638 } while (eid != MCI_END_COMMAND_LIST); 639 return TRUE; 640 } 641 642 643 /************************************************************************** 644 * MCI_GetCommandTable [internal] 645 */ 646 static UINT MCI_GetCommandTable(UINT uDevType) 647 { 648 UINT uTbl; 649 WCHAR buf[32]; 650 LPCWSTR str = NULL; 651 652 /* first look up existing for existing devType */ 653 for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) { 654 if (S_MciCmdTable[uTbl].lpTable && S_MciCmdTable[uTbl].uDevType == uDevType) 655 return uTbl; 656 } 657 658 /* well try to load id */ 659 if (uDevType >= MCI_DEVTYPE_FIRST && uDevType <= MCI_DEVTYPE_LAST) { 660 if (LoadStringW(hWinMM32Instance, uDevType, buf, ARRAY_SIZE(buf))) { 661 str = buf; 662 } 663 } else if (uDevType == 0) { 664 str = L"CORE"; 665 } 666 uTbl = MCI_NO_COMMAND_TABLE; 667 if (str) { 668 HRSRC hRsrc = FindResourceW(hWinMM32Instance, str, (LPCWSTR)RT_RCDATA); 669 HANDLE hMem = 0; 670 671 if (hRsrc) hMem = LoadResource(hWinMM32Instance, hRsrc); 672 if (hMem) { 673 uTbl = MCI_SetCommandTable(hMem, uDevType); 674 } else { 675 WARN("No command table found in resource %p[%s]\n", 676 hWinMM32Instance, debugstr_w(str)); 677 } 678 } 679 TRACE("=> %d\n", uTbl); 680 return uTbl; 681 } 682 683 /************************************************************************** 684 * MCI_SetCommandTable [internal] 685 */ 686 static UINT MCI_SetCommandTable(HGLOBAL hMem, UINT uDevType) 687 { 688 int uTbl; 689 static BOOL bInitDone = FALSE; 690 691 /* <HACK> 692 * The CORE command table must be loaded first, so that MCI_GetCommandTable() 693 * can be called with 0 as a uDevType to retrieve it. 694 * </HACK> 695 */ 696 if (!bInitDone) { 697 bInitDone = TRUE; 698 MCI_GetCommandTable(0); 699 } 700 TRACE("(%p, %u)\n", hMem, uDevType); 701 for (uTbl = 0; uTbl < MAX_MCICMDTABLE; uTbl++) { 702 if (!S_MciCmdTable[uTbl].lpTable) { 703 const BYTE* lmem; 704 LPCWSTR str; 705 WORD eid; 706 WORD count; 707 708 S_MciCmdTable[uTbl].uDevType = uDevType; 709 S_MciCmdTable[uTbl].lpTable = LockResource(hMem); 710 S_MciCmdTable[uTbl].hMem = hMem; 711 712 if (TRACE_ON(mci)) { 713 MCI_DumpCommandTable(uTbl); 714 } 715 716 /* create the verbs table */ 717 /* get # of entries */ 718 lmem = S_MciCmdTable[uTbl].lpTable; 719 count = 0; 720 do { 721 str = (LPCWSTR)lmem; 722 lmem += (lstrlenW(str) + 1) * sizeof(WCHAR); 723 eid = *(const WORD*)(lmem + sizeof(DWORD)); 724 lmem += sizeof(DWORD) + sizeof(WORD); 725 if (eid == MCI_COMMAND_HEAD) 726 count++; 727 } while (eid != MCI_END_COMMAND_LIST); 728 729 S_MciCmdTable[uTbl].aVerbs = HeapAlloc(GetProcessHeap(), 0, count * sizeof(LPCWSTR)); 730 S_MciCmdTable[uTbl].nVerbs = count; 731 732 lmem = S_MciCmdTable[uTbl].lpTable; 733 count = 0; 734 do { 735 str = (LPCWSTR)lmem; 736 lmem += (lstrlenW(str) + 1) * sizeof(WCHAR); 737 eid = *(const WORD*)(lmem + sizeof(DWORD)); 738 lmem += sizeof(DWORD) + sizeof(WORD); 739 if (eid == MCI_COMMAND_HEAD) 740 S_MciCmdTable[uTbl].aVerbs[count++] = str; 741 } while (eid != MCI_END_COMMAND_LIST); 742 /* assert(count == S_MciCmdTable[uTbl].nVerbs); */ 743 return uTbl; 744 } 745 } 746 747 return MCI_NO_COMMAND_TABLE; 748 } 749 750 /************************************************************************** 751 * MCI_UnLoadMciDriver [internal] 752 */ 753 static BOOL MCI_UnLoadMciDriver(LPWINE_MCIDRIVER wmd) 754 { 755 LPWINE_MCIDRIVER* tmp; 756 757 if (!wmd) 758 return TRUE; 759 760 CloseDriver(wmd->hDriver, 0, 0); 761 762 if (wmd->dwPrivate != 0) 763 WARN("Unloading mci driver with non nul dwPrivate field\n"); 764 765 EnterCriticalSection(&WINMM_cs); 766 for (tmp = &MciDrivers; *tmp; tmp = &(*tmp)->lpNext) { 767 if (*tmp == wmd) { 768 *tmp = wmd->lpNext; 769 break; 770 } 771 } 772 LeaveCriticalSection(&WINMM_cs); 773 774 HeapFree(GetProcessHeap(), 0, wmd->lpstrDeviceType); 775 HeapFree(GetProcessHeap(), 0, wmd->lpstrAlias); 776 777 HeapFree(GetProcessHeap(), 0, wmd); 778 return TRUE; 779 } 780 781 /************************************************************************** 782 * MCI_OpenMciDriver [internal] 783 */ 784 static BOOL MCI_OpenMciDriver(LPWINE_MCIDRIVER wmd, LPCWSTR drvTyp, DWORD_PTR lp) 785 { 786 WCHAR libName[128]; 787 788 if (!DRIVER_GetLibName(drvTyp, L"MCI", libName, sizeof(libName))) 789 return FALSE; 790 791 /* First load driver */ 792 wmd->hDriver = (HDRVR)DRIVER_TryOpenDriver32(libName, lp); 793 return wmd->hDriver != NULL; 794 } 795 796 /************************************************************************** 797 * MCI_LoadMciDriver [internal] 798 */ 799 static DWORD MCI_LoadMciDriver(LPCWSTR _strDevTyp, LPWINE_MCIDRIVER* lpwmd) 800 { 801 LPWSTR strDevTyp = str_dup_upper(_strDevTyp); 802 LPWINE_MCIDRIVER wmd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmd)); 803 MCI_OPEN_DRIVER_PARMSW modp; 804 DWORD dwRet = 0; 805 806 if (!wmd || !strDevTyp) { 807 dwRet = MCIERR_OUT_OF_MEMORY; 808 goto errCleanUp; 809 } 810 811 wmd->lpfnYieldProc = MCI_DefYieldProc; 812 wmd->dwYieldData = VK_CANCEL; 813 wmd->CreatorThread = GetCurrentThreadId(); 814 815 EnterCriticalSection(&WINMM_cs); 816 /* wmd must be inserted in list before sending opening the driver, because it 817 * may want to lookup at wDevID 818 */ 819 wmd->lpNext = MciDrivers; 820 MciDrivers = wmd; 821 822 for (modp.wDeviceID = MCI_MAGIC; 823 MCI_GetDriver(modp.wDeviceID) != 0; 824 modp.wDeviceID++); 825 826 wmd->wDeviceID = modp.wDeviceID; 827 828 LeaveCriticalSection(&WINMM_cs); 829 830 TRACE("wDevID=%04X\n", modp.wDeviceID); 831 832 modp.lpstrParams = NULL; 833 834 if (!MCI_OpenMciDriver(wmd, strDevTyp, (DWORD_PTR)&modp)) { 835 /* silence warning if all is used... some bogus program use commands like 836 * 'open all'... 837 */ 838 if (wcsicmp(strDevTyp, L"ALL") == 0) { 839 dwRet = MCIERR_CANNOT_USE_ALL; 840 } else { 841 FIXME("Couldn't load driver for type %s.\n", 842 debugstr_w(strDevTyp)); 843 dwRet = MCIERR_DEVICE_NOT_INSTALLED; 844 } 845 goto errCleanUp; 846 } 847 848 /* FIXME: should also check that module's description is of the form 849 * MODULENAME:[MCI] comment 850 */ 851 852 /* some drivers will return 0x0000FFFF, some others 0xFFFFFFFF */ 853 wmd->uSpecificCmdTable = LOWORD(modp.wCustomCommandTable); 854 wmd->uTypeCmdTable = MCI_COMMAND_TABLE_NOT_LOADED; 855 856 TRACE("Loaded driver %p (%s), type is %d, cmdTable=%08x\n", 857 wmd->hDriver, debugstr_w(strDevTyp), modp.wType, modp.wCustomCommandTable); 858 859 wmd->lpstrDeviceType = strDevTyp; 860 wmd->wType = modp.wType; 861 862 TRACE("mcidev=%d, uDevTyp=%04X wDeviceID=%04X !\n", 863 modp.wDeviceID, modp.wType, modp.wDeviceID); 864 *lpwmd = wmd; 865 return 0; 866 errCleanUp: 867 MCI_UnLoadMciDriver(wmd); 868 HeapFree(GetProcessHeap(), 0, strDevTyp); 869 *lpwmd = 0; 870 return dwRet; 871 } 872 873 /************************************************************************** 874 * MCI_SendCommandFrom32 [internal] 875 */ 876 static DWORD MCI_SendCommandFrom32(MCIDEVICEID wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 877 { 878 DWORD dwRet = MCIERR_INVALID_DEVICE_ID; 879 LPWINE_MCIDRIVER wmd = MCI_GetDriver(wDevID); 880 881 if (wmd) { 882 if(wmd->CreatorThread != GetCurrentThreadId()) 883 return MCIERR_INVALID_DEVICE_NAME; 884 885 dwRet = SendDriverMessage(wmd->hDriver, wMsg, dwParam1, dwParam2); 886 } 887 return dwRet; 888 } 889 890 /************************************************************************** 891 * MCI_FinishOpen [internal] 892 * 893 * Three modes of operation: 894 * 1 open foo.ext ... -> OPEN_ELEMENT with lpstrElementName=foo.ext 895 * open sequencer!foo.ext same with lpstrElementName=foo.ext 896 * 2 open new type waveaudio -> OPEN_ELEMENT with empty ("") lpstrElementName 897 * 3 open sequencer -> OPEN_ELEMENT unset, and 898 * capability sequencer (auto-open) likewise 899 */ 900 static DWORD MCI_FinishOpen(LPWINE_MCIDRIVER wmd, LPMCI_OPEN_PARMSW lpParms, 901 DWORD dwParam) 902 { 903 LPCWSTR alias = NULL; 904 /* Open always defines an alias for further reference */ 905 if (dwParam & MCI_OPEN_ALIAS) { /* open ... alias */ 906 alias = lpParms->lpstrAlias; 907 if (MCI_GetDriverFromString(alias)) 908 return MCIERR_DUPLICATE_ALIAS; 909 } else { 910 if ((dwParam & MCI_OPEN_ELEMENT) /* open file.wav */ 911 && !(dwParam & MCI_OPEN_ELEMENT_ID)) 912 alias = lpParms->lpstrElementName; 913 else if (dwParam & MCI_OPEN_TYPE ) /* open cdaudio */ 914 alias = wmd->lpstrDeviceType; 915 if (alias && MCI_GetDriverFromString(alias)) 916 return MCIERR_DEVICE_OPEN; 917 } 918 if (alias) { 919 wmd->lpstrAlias = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(alias)+1) * sizeof(WCHAR)); 920 if (!wmd->lpstrAlias) return MCIERR_OUT_OF_MEMORY; 921 lstrcpyW( wmd->lpstrAlias, alias); 922 /* In most cases, natives adds MCI_OPEN_ALIAS to the flags passed to the driver. 923 * Don't. The drivers don't care about the winmm alias. */ 924 } 925 lpParms->wDeviceID = wmd->wDeviceID; 926 927 return MCI_SendCommandFrom32(wmd->wDeviceID, MCI_OPEN_DRIVER, dwParam, 928 (DWORD_PTR)lpParms); 929 } 930 931 /************************************************************************** 932 * MCI_FindCommand [internal] 933 */ 934 static LPCWSTR MCI_FindCommand(UINT uTbl, LPCWSTR verb) 935 { 936 UINT idx; 937 938 if (uTbl >= MAX_MCICMDTABLE || !S_MciCmdTable[uTbl].lpTable) 939 return NULL; 940 941 /* another improvement would be to have the aVerbs array sorted, 942 * so that we could use a dichotomic search on it, rather than this dumb 943 * array look up 944 */ 945 for (idx = 0; idx < S_MciCmdTable[uTbl].nVerbs; idx++) { 946 if (wcsicmp(S_MciCmdTable[uTbl].aVerbs[idx], verb) == 0) 947 return S_MciCmdTable[uTbl].aVerbs[idx]; 948 } 949 950 return NULL; 951 } 952 953 /************************************************************************** 954 * MCI_GetReturnType [internal] 955 */ 956 static DWORD MCI_GetReturnType(LPCWSTR lpCmd) 957 { 958 lpCmd = (LPCWSTR)((const BYTE*)(lpCmd + lstrlenW(lpCmd) + 1) + sizeof(DWORD) + sizeof(WORD)); 959 if (*lpCmd == '\0' && *(const WORD*)((const BYTE*)(lpCmd + 1) + sizeof(DWORD)) == MCI_RETURN) { 960 return *(const DWORD*)(lpCmd + 1); 961 } 962 return 0L; 963 } 964 965 /************************************************************************** 966 * MCI_GetMessage [internal] 967 */ 968 static WORD MCI_GetMessage(LPCWSTR lpCmd) 969 { 970 return (WORD)*(const DWORD*)(lpCmd + lstrlenW(lpCmd) + 1); 971 } 972 973 /************************************************************************** 974 * MCI_GetDWord [internal] 975 * 976 * Accept 0 -1 255 255:0 255:255:255:255 :::1 1::: 2::3 ::4: 12345678 977 * Refuse -1:0 0:-1 :: 256:0 1:256 0::::1 978 */ 979 static BOOL MCI_GetDWord(DWORD* data, LPWSTR* ptr) 980 { 981 LPWSTR ret = *ptr; 982 DWORD total = 0, shift = 0; 983 BOOL sign = FALSE, digits = FALSE; 984 985 while (*ret == ' ' || *ret == '\t') ret++; 986 if (*ret == '-') { 987 ret++; 988 sign = TRUE; 989 } 990 for(;;) { 991 DWORD val = 0; 992 while ('0' <= *ret && *ret <= '9') { 993 val = *ret++ - '0' + 10 * val; 994 digits = TRUE; 995 } 996 switch (*ret) { 997 case '\0': break; 998 case '\t': 999 case ' ': ret++; break; 1000 default: return FALSE; 1001 case ':': 1002 if ((val >= 256) || (shift >= 24)) return FALSE; 1003 total |= val << shift; 1004 shift += 8; 1005 ret++; 1006 continue; 1007 } 1008 1009 if (!digits) return FALSE; 1010 if (shift && (val >= 256 || sign)) return FALSE; 1011 total |= val << shift; 1012 *data = sign ? -total : total; 1013 *ptr = ret; 1014 return TRUE; 1015 } 1016 } 1017 1018 /************************************************************************** 1019 * MCI_GetString [internal] 1020 */ 1021 static DWORD MCI_GetString(LPWSTR* str, LPWSTR* args) 1022 { 1023 LPWSTR ptr = *args; 1024 1025 /* see if we have a quoted string */ 1026 if (*ptr == '"') { 1027 ptr = wcschr(*str = ptr + 1, '"'); 1028 if (!ptr) return MCIERR_NO_CLOSING_QUOTE; 1029 /* FIXME: shall we escape \" from string ?? */ 1030 if (ptr[-1] == '\\') TRACE("Ooops: un-escaped \"\n"); 1031 *ptr++ = '\0'; /* remove trailing " */ 1032 if (*ptr != ' ' && *ptr != '\0') return MCIERR_EXTRA_CHARACTERS; 1033 } else { 1034 ptr = wcschr(ptr, ' '); 1035 1036 if (ptr) { 1037 *ptr++ = '\0'; 1038 } else { 1039 ptr = *args + lstrlenW(*args); 1040 } 1041 *str = *args; 1042 } 1043 1044 *args = ptr; 1045 return 0; 1046 } 1047 1048 #define MCI_DATA_SIZE 16 1049 1050 /************************************************************************** 1051 * MCI_ParseOptArgs [internal] 1052 */ 1053 static DWORD MCI_ParseOptArgs(DWORD* data, int _offset, LPCWSTR lpCmd, 1054 LPWSTR args, LPDWORD dwFlags) 1055 { 1056 int len, offset; 1057 const char* lmem; 1058 LPCWSTR str; 1059 DWORD dwRet, flg, cflg = 0; 1060 WORD eid; 1061 BOOL inCst, found; 1062 1063 /* loop on arguments */ 1064 while (*args) { 1065 lmem = (const char*)lpCmd; 1066 found = inCst = FALSE; 1067 offset = _offset; 1068 1069 /* skip any leading white space(s) */ 1070 while (*args == ' ') args++; 1071 TRACE("args=%s\n", debugstr_w(args)); 1072 1073 do { /* loop on options for command table for the requested verb */ 1074 str = (LPCWSTR)lmem; 1075 lmem += ((len = lstrlenW(str)) + 1) * sizeof(WCHAR); 1076 flg = *(const DWORD*)lmem; 1077 eid = *(const WORD*)(lmem + sizeof(DWORD)); 1078 lmem += sizeof(DWORD) + sizeof(WORD); 1079 /* TRACE("\tcmd=%s inCst=%c eid=%04x\n", debugstr_w(str), inCst ? 'Y' : 'N', eid); */ 1080 1081 switch (eid) { 1082 case MCI_CONSTANT: 1083 inCst = TRUE; cflg = flg; break; 1084 case MCI_END_CONSTANT: 1085 /* there may be additional integral values after flag in constant */ 1086 if (inCst && MCI_GetDWord(&(data[offset]), &args)) { 1087 *dwFlags |= cflg; 1088 } 1089 inCst = FALSE; cflg = 0; 1090 break; 1091 case MCI_RETURN: 1092 if (offset != _offset) { 1093 FIXME("MCI_RETURN not in first position\n"); 1094 return MCIERR_PARSER_INTERNAL; 1095 } 1096 } 1097 1098 if (wcsnicmp(args, str, len) == 0 && 1099 ((eid == MCI_STRING && len == 0) || args[len] == 0 || args[len] == ' ')) { 1100 /* store good values into data[] */ 1101 args += len; 1102 while (*args == ' ') args++; 1103 found = TRUE; 1104 1105 switch (eid) { 1106 case MCI_COMMAND_HEAD: 1107 case MCI_RETURN: 1108 case MCI_END_COMMAND: 1109 case MCI_END_COMMAND_LIST: 1110 case MCI_CONSTANT: /* done above */ 1111 case MCI_END_CONSTANT: /* done above */ 1112 break; 1113 case MCI_FLAG: 1114 *dwFlags |= flg; 1115 TRACE("flag=%08lx\n", flg); 1116 break; 1117 case MCI_HWND: 1118 case MCI_HPAL: 1119 case MCI_HDC: 1120 case MCI_INTEGER: 1121 if (inCst) { 1122 data[offset] |= flg; 1123 *dwFlags |= cflg; 1124 inCst = FALSE; 1125 TRACE("flag=%08lx constant=%08lx\n", cflg, flg); 1126 } else { 1127 *dwFlags |= flg; 1128 if (!MCI_GetDWord(&(data[offset]), &args)) { 1129 return MCIERR_BAD_INTEGER; 1130 } 1131 TRACE("flag=%08lx int=%ld\n", flg, data[offset]); 1132 } 1133 break; 1134 case MCI_RECT: 1135 /* store rect in data (offset..offset+3) */ 1136 *dwFlags |= flg; 1137 if (!MCI_GetDWord(&(data[offset+0]), &args) || 1138 !MCI_GetDWord(&(data[offset+1]), &args) || 1139 !MCI_GetDWord(&(data[offset+2]), &args) || 1140 !MCI_GetDWord(&(data[offset+3]), &args)) { 1141 return MCIERR_BAD_INTEGER; 1142 } 1143 TRACE("flag=%08lx for rectangle\n", flg); 1144 break; 1145 case MCI_STRING: 1146 *dwFlags |= flg; 1147 if ((dwRet = MCI_GetString((LPWSTR*)&data[offset], &args))) 1148 return dwRet; 1149 TRACE("flag=%08lx string=%s\n", flg, debugstr_w(*(LPWSTR*)&data[offset])); 1150 break; 1151 default: ERR("oops\n"); 1152 } 1153 /* exit inside while loop, except if just entered in constant area definition */ 1154 if (!inCst || eid != MCI_CONSTANT) eid = MCI_END_COMMAND; 1155 } else { 1156 /* have offset incremented if needed */ 1157 switch (eid) { 1158 case MCI_COMMAND_HEAD: 1159 case MCI_RETURN: 1160 case MCI_END_COMMAND: 1161 case MCI_END_COMMAND_LIST: 1162 case MCI_CONSTANT: 1163 case MCI_FLAG: break; 1164 case MCI_HWND: 1165 case MCI_HPAL: 1166 case MCI_HDC: if (!inCst) offset += sizeof(HANDLE)/sizeof(DWORD); break; 1167 case MCI_INTEGER: if (!inCst) offset++; break; 1168 case MCI_END_CONSTANT: offset++; break; 1169 case MCI_STRING: offset += sizeof(LPWSTR)/sizeof(DWORD); break; 1170 case MCI_RECT: offset += 4; break; 1171 default: ERR("oops\n"); 1172 } 1173 } 1174 } while (eid != MCI_END_COMMAND); 1175 if (!found) { 1176 WARN("Optarg %s not found\n", debugstr_w(args)); 1177 return MCIERR_UNRECOGNIZED_COMMAND; 1178 } 1179 if (offset == MCI_DATA_SIZE) { 1180 FIXME("Internal data[] buffer overflow\n"); 1181 return MCIERR_PARSER_INTERNAL; 1182 } 1183 } 1184 return 0; 1185 } 1186 1187 /************************************************************************** 1188 * MCI_HandleReturnValues [internal] 1189 */ 1190 static DWORD MCI_HandleReturnValues(DWORD dwRet, LPWINE_MCIDRIVER wmd, DWORD retType, 1191 MCI_GENERIC_PARMS *params, LPWSTR lpstrRet, UINT uRetLen) 1192 { 1193 if (lpstrRet) { 1194 switch (retType) { 1195 case 0: /* nothing to return */ 1196 break; 1197 case MCI_INTEGER: 1198 { 1199 DWORD data = *(DWORD *)(params + 1); 1200 switch (dwRet & 0xFFFF0000ul) { 1201 case 0: 1202 case MCI_INTEGER_RETURNED: 1203 swprintf(lpstrRet, uRetLen, L"%d", data); 1204 break; 1205 case MCI_RESOURCE_RETURNED: 1206 /* return string which ID is HIWORD(data), 1207 * string is loaded from mmsystem.dll */ 1208 LoadStringW(hWinMM32Instance, HIWORD(data), lpstrRet, uRetLen); 1209 break; 1210 case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: 1211 /* return string which ID is HIWORD(data), 1212 * string is loaded from driver */ 1213 /* FIXME: this is wrong for a 16 bit handle */ 1214 LoadStringW(GetDriverModuleHandle(wmd->hDriver), 1215 HIWORD(data), lpstrRet, uRetLen); 1216 break; 1217 case MCI_COLONIZED3_RETURN: 1218 swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d", 1219 LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)), 1220 LOBYTE(HIWORD(data))); 1221 break; 1222 case MCI_COLONIZED4_RETURN: 1223 swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d:%02d", 1224 LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)), 1225 LOBYTE(HIWORD(data)), HIBYTE(HIWORD(data))); 1226 break; 1227 default: ERR("Ooops (%04X)\n", HIWORD(dwRet)); 1228 } 1229 break; 1230 } 1231 case 13: /* MCI_INTEGER64 */ 1232 { 1233 DWORD_PTR data = *(DWORD_PTR *)(params + 1); 1234 switch (dwRet & 0xFFFF0000ul) { 1235 case 0: 1236 case MCI_INTEGER_RETURNED: 1237 swprintf(lpstrRet, uRetLen, L"%Id", data); 1238 break; 1239 case MCI_RESOURCE_RETURNED: 1240 /* return string which ID is HIWORD(data), 1241 * string is loaded from mmsystem.dll */ 1242 LoadStringW(hWinMM32Instance, HIWORD(data), lpstrRet, uRetLen); 1243 break; 1244 case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: 1245 /* return string which ID is HIWORD(data), 1246 * string is loaded from driver */ 1247 /* FIXME: this is wrong for a 16 bit handle */ 1248 LoadStringW(GetDriverModuleHandle(wmd->hDriver), 1249 HIWORD(data), lpstrRet, uRetLen); 1250 break; 1251 case MCI_COLONIZED3_RETURN: 1252 swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d", 1253 LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)), 1254 LOBYTE(HIWORD(data))); 1255 break; 1256 case MCI_COLONIZED4_RETURN: 1257 swprintf(lpstrRet, uRetLen, L"%02d:%02d:%02d:%02d", 1258 LOBYTE(LOWORD(data)), HIBYTE(LOWORD(data)), 1259 LOBYTE(HIWORD(data)), HIBYTE(HIWORD(data))); 1260 break; 1261 default: ERR("Ooops (%04X)\n", HIWORD(dwRet)); 1262 } 1263 break; 1264 } 1265 case MCI_STRING: 1266 switch (dwRet & 0xFFFF0000ul) { 1267 case 0: 1268 /* nothing to do data[0] == lpstrRet */ 1269 break; 1270 case MCI_INTEGER_RETURNED: 1271 { 1272 DWORD *data = (DWORD *)(params + 1); 1273 *data = *(LPDWORD)lpstrRet; 1274 swprintf(lpstrRet, uRetLen, L"%d", *data); 1275 break; 1276 } 1277 default: 1278 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet)); 1279 break; 1280 } 1281 break; 1282 case MCI_RECT: 1283 { 1284 DWORD *data = (DWORD *)(params + 1); 1285 if (dwRet & 0xFFFF0000ul) 1286 WARN("Oooch. MCI_STRING and HIWORD(dwRet)=%04x\n", HIWORD(dwRet)); 1287 swprintf(lpstrRet, uRetLen, L"%d %d %d %d", data[0], data[1], data[2], data[3]); 1288 break; 1289 } 1290 default: FIXME("Unknown MCI return type %ld\n", retType); 1291 } 1292 } 1293 return LOWORD(dwRet); 1294 } 1295 1296 /************************************************************************** 1297 * mciSendStringW [WINMM.@] 1298 */ 1299 DWORD WINAPI mciSendStringW(LPCWSTR lpstrCommand, LPWSTR lpstrRet, 1300 UINT uRetLen, HWND hwndCallback) 1301 { 1302 LPWSTR verb, dev, args, devType = NULL; 1303 LPWINE_MCIDRIVER wmd = 0; 1304 MCIDEVICEID uDevID, auto_open = 0; 1305 DWORD dwFlags = 0, dwRet = 0; 1306 int offset = 0; 1307 DWORD retType; 1308 LPCWSTR lpCmd = 0; 1309 WORD wMsg = 0; 1310 union 1311 { 1312 MCI_GENERIC_PARMS generic; 1313 MCI_OPEN_PARMSW open; 1314 MCI_SOUND_PARMSW sound; 1315 MCI_SYSINFO_PARMSW sysinfo; 1316 DWORD dw[MCI_DATA_SIZE]; 1317 } data; 1318 1319 TRACE("(%s, %p, %d, %p)\n", 1320 debugstr_w(lpstrCommand), lpstrRet, uRetLen, hwndCallback); 1321 if (lpstrRet && uRetLen) *lpstrRet = '\0'; 1322 1323 if (!lpstrCommand[0]) 1324 return MCIERR_MISSING_COMMAND_STRING; 1325 1326 /* format is <command> <device> <optargs> */ 1327 if (!(verb = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(lpstrCommand)+1) * sizeof(WCHAR)))) 1328 return MCIERR_OUT_OF_MEMORY; 1329 lstrcpyW( verb, lpstrCommand ); 1330 CharLowerW(verb); 1331 1332 memset(&data, 0, sizeof(data)); 1333 1334 if (!(args = wcschr(verb, ' '))) { 1335 dwRet = MCIERR_MISSING_DEVICE_NAME; 1336 goto errCleanUp; 1337 } 1338 *args++ = '\0'; 1339 if ((dwRet = MCI_GetString(&dev, &args))) { 1340 goto errCleanUp; 1341 } 1342 uDevID = wcsicmp(dev, L"ALL") ? 0 : MCI_ALL_DEVICE_ID; 1343 1344 /* Determine devType from open */ 1345 if (!wcscmp(verb, L"open")) { 1346 LPWSTR tmp; 1347 WCHAR buf[128]; 1348 1349 /* case dev == 'new' has to be handled */ 1350 if (!wcscmp(dev, L"new")) { 1351 dev = 0; 1352 if ((devType = wcsstr(args, L"type ")) != NULL) { 1353 devType += 5; 1354 tmp = wcschr(devType, ' '); 1355 if (tmp) *tmp = '\0'; 1356 devType = str_dup_upper(devType); 1357 if (tmp) *tmp = ' '; 1358 /* dwFlags and data[2] will be correctly set in ParseOpt loop */ 1359 } else { 1360 WARN("open new requires device type\n"); 1361 dwRet = MCIERR_MISSING_DEVICE_NAME; 1362 goto errCleanUp; 1363 } 1364 dwFlags |= MCI_OPEN_ELEMENT; 1365 data.open.lpstrElementName = &L""[0]; 1366 } else if ((devType = wcschr(dev, '!')) != NULL) { 1367 *devType++ = '\0'; 1368 tmp = devType; devType = dev; dev = tmp; 1369 1370 dwFlags |= MCI_OPEN_TYPE; 1371 data.open.lpstrDeviceType = devType; 1372 devType = str_dup_upper(devType); 1373 dwFlags |= MCI_OPEN_ELEMENT; 1374 data.open.lpstrElementName = dev; 1375 } else if (DRIVER_GetLibName(dev, L"MCI", buf, sizeof(buf))) { 1376 /* this is the name of a mci driver's type */ 1377 tmp = wcschr(dev, ' '); 1378 if (tmp) *tmp = '\0'; 1379 data.open.lpstrDeviceType = dev; 1380 devType = str_dup_upper(dev); 1381 if (tmp) *tmp = ' '; 1382 dwFlags |= MCI_OPEN_TYPE; 1383 } else { 1384 if ((devType = wcsstr(args, L"type ")) != NULL) { 1385 devType += 5; 1386 tmp = wcschr(devType, ' '); 1387 if (tmp) *tmp = '\0'; 1388 devType = str_dup_upper(devType); 1389 if (tmp) *tmp = ' '; 1390 /* dwFlags and lpstrDeviceType will be correctly set in ParseOpt loop */ 1391 } else { 1392 if ((dwRet = MCI_GetDevTypeFromFileName(dev, buf, sizeof(buf)))) 1393 goto errCleanUp; 1394 1395 devType = str_dup_upper(buf); 1396 } 1397 dwFlags |= MCI_OPEN_ELEMENT; 1398 data.open.lpstrElementName = dev; 1399 } 1400 if (MCI_ALL_DEVICE_ID == uDevID) { 1401 dwRet = MCIERR_CANNOT_USE_ALL; 1402 goto errCleanUp; 1403 } 1404 if (!wcsstr(args, L" alias ") && !dev) { 1405 dwRet = MCIERR_NEW_REQUIRES_ALIAS; 1406 goto errCleanUp; 1407 } 1408 1409 dwRet = MCI_LoadMciDriver(devType, &wmd); 1410 if (dwRet == MCIERR_DEVICE_NOT_INSTALLED) 1411 dwRet = MCIERR_INVALID_DEVICE_NAME; 1412 if (dwRet) 1413 goto errCleanUp; 1414 } else if ((MCI_ALL_DEVICE_ID != uDevID) && !(wmd = MCI_GetDriver(mciGetDeviceIDW(dev))) 1415 && (lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb))) { 1416 /* auto-open uses the core command table */ 1417 switch (MCI_GetMessage(lpCmd)) { 1418 case MCI_SOUND: /* command does not use a device name */ 1419 case MCI_SYSINFO: 1420 break; 1421 case MCI_CLOSE: /* don't auto-open for close */ 1422 case MCI_BREAK: /* no auto-open for system commands */ 1423 dwRet = MCIERR_INVALID_DEVICE_NAME; 1424 goto errCleanUp; 1425 break; 1426 default: 1427 { 1428 WCHAR buf[138], retbuf[6]; 1429 swprintf(buf, ARRAY_SIZE(buf), L"open %s wait", dev); 1430 /* open via mciSendString handles quoting, dev!file syntax and alias creation */ 1431 if ((dwRet = mciSendStringW(buf, retbuf, ARRAY_SIZE(retbuf), 0)) != 0) 1432 goto errCleanUp; 1433 auto_open = wcstoul(retbuf, NULL, 10); 1434 TRACE("auto-opened %u for %s\n", auto_open, debugstr_w(dev)); 1435 1436 /* FIXME: test for notify flag (how to preparse?) before opening */ 1437 wmd = MCI_GetDriver(auto_open); 1438 if (!wmd) { 1439 ERR("No auto-open device %u\n", auto_open); 1440 dwRet = MCIERR_INVALID_DEVICE_ID; 1441 goto errCleanUp; 1442 } 1443 } 1444 } 1445 } 1446 1447 /* get the verb in the different command tables */ 1448 if (wmd) { 1449 /* try the device specific command table */ 1450 lpCmd = MCI_FindCommand(wmd->uSpecificCmdTable, verb); 1451 if (!lpCmd) { 1452 /* try the type specific command table */ 1453 if (wmd->uTypeCmdTable == MCI_COMMAND_TABLE_NOT_LOADED) 1454 wmd->uTypeCmdTable = MCI_GetCommandTable(wmd->wType); 1455 if (wmd->uTypeCmdTable != MCI_NO_COMMAND_TABLE) 1456 lpCmd = MCI_FindCommand(wmd->uTypeCmdTable, verb); 1457 } 1458 } 1459 /* try core command table */ 1460 if (!lpCmd) lpCmd = MCI_FindCommand(MCI_GetCommandTable(0), verb); 1461 1462 if (!lpCmd) { 1463 TRACE("Command %s not found!\n", debugstr_w(verb)); 1464 dwRet = MCIERR_UNRECOGNIZED_COMMAND; 1465 goto errCleanUp; 1466 } 1467 wMsg = MCI_GetMessage(lpCmd); 1468 1469 /* set return information */ 1470 offset = sizeof(data.generic); 1471 switch (retType = MCI_GetReturnType(lpCmd)) { 1472 case 0: 1473 break; 1474 case MCI_INTEGER: 1475 offset += sizeof(DWORD); 1476 break; 1477 case MCI_STRING: 1478 data.sysinfo.lpstrReturn = lpstrRet; 1479 data.sysinfo.dwRetSize = uRetLen; 1480 offset = FIELD_OFFSET( MCI_SYSINFO_PARMSW, dwNumber ); 1481 break; 1482 case MCI_RECT: 1483 offset += 4 * sizeof(DWORD); 1484 break; 1485 case 13: /* MCI_INTEGER64 */ 1486 offset += sizeof(DWORD_PTR); 1487 break; 1488 default: 1489 FIXME("Unknown MCI return type %ld\n", retType); 1490 dwRet = MCIERR_PARSER_INTERNAL; 1491 goto errCleanUp; 1492 } 1493 1494 TRACE("verb=%s on dev=%s; offset=%d\n", 1495 debugstr_w(verb), debugstr_w(dev), offset); 1496 1497 if ((dwRet = MCI_ParseOptArgs(data.dw, offset / sizeof(DWORD), lpCmd, args, &dwFlags))) 1498 goto errCleanUp; 1499 1500 /* set up call back */ 1501 if (auto_open) { 1502 if (dwFlags & MCI_NOTIFY) { 1503 dwRet = MCIERR_NOTIFY_ON_AUTO_OPEN; 1504 goto errCleanUp; 1505 } 1506 /* FIXME: the command should get its own notification window set up and 1507 * ask for device closing while processing the notification mechanism. 1508 * hwndCallback = ... 1509 * dwFlags |= MCI_NOTIFY; 1510 * In the meantime special-case all commands but PLAY and RECORD below. */ 1511 } 1512 if (dwFlags & MCI_NOTIFY) { 1513 data.generic.dwCallback = (DWORD_PTR)hwndCallback; 1514 } 1515 1516 switch (wMsg) { 1517 case MCI_OPEN: 1518 if (wcscmp(verb, L"open")) { 1519 FIXME("Cannot open with command %s\n", debugstr_w(verb)); 1520 dwRet = MCIERR_DRIVER_INTERNAL; 1521 wMsg = 0; 1522 goto errCleanUp; 1523 } 1524 break; 1525 case MCI_SYSINFO: 1526 /* Requirements on dev depend on the flags: 1527 * alias with INSTALLNAME, name like "digitalvideo" 1528 * with QUANTITY and NAME. */ 1529 { 1530 data.sysinfo.wDeviceType = MCI_ALL_DEVICE_ID; 1531 if (uDevID != MCI_ALL_DEVICE_ID) { 1532 if (dwFlags & MCI_SYSINFO_INSTALLNAME) 1533 wmd = MCI_GetDriver(mciGetDeviceIDW(dev)); 1534 else if (!(data.sysinfo.wDeviceType = MCI_GetDevTypeFromResource(dev))) { 1535 dwRet = MCIERR_DEVICE_TYPE_REQUIRED; 1536 goto errCleanUp; 1537 } 1538 } 1539 } 1540 break; 1541 case MCI_SOUND: 1542 /* FIXME: name is optional, "sound" is a valid command. 1543 * FIXME: Parse "sound notify" as flag, not as name. */ 1544 data.sound.lpstrSoundName = dev; 1545 dwFlags |= MCI_SOUND_NAME; 1546 break; 1547 } 1548 1549 TRACE("[%d, %s, %08lx, %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx]\n", 1550 wmd ? wmd->wDeviceID : uDevID, MCI_MessageToString(wMsg), dwFlags, 1551 data.dw[0], data.dw[1], data.dw[2], data.dw[3], data.dw[4], 1552 data.dw[5], data.dw[6], data.dw[7], data.dw[8], data.dw[9]); 1553 1554 if (wMsg == MCI_OPEN) { 1555 if ((dwRet = MCI_FinishOpen(wmd, &data.open, dwFlags))) 1556 goto errCleanUp; 1557 /* FIXME: notification is not properly shared across two opens */ 1558 } else { 1559 dwRet = MCI_SendCommand(wmd ? wmd->wDeviceID : uDevID, wMsg, dwFlags, (DWORD_PTR)&data); 1560 } 1561 if (!LOWORD(dwRet)) { 1562 TRACE("=> 1/ %lx (%s)\n", dwRet, debugstr_w(lpstrRet)); 1563 dwRet = MCI_HandleReturnValues(dwRet, wmd, retType, &data.generic, lpstrRet, uRetLen); 1564 TRACE("=> 2/ %lx (%s)\n", dwRet, debugstr_w(lpstrRet)); 1565 } else 1566 TRACE("=> %lx\n", dwRet); 1567 1568 errCleanUp: 1569 if (auto_open) { 1570 /* PLAY and RECORD are the only known non-immediate commands */ 1571 if (LOWORD(dwRet) || !(wMsg == MCI_PLAY || wMsg == MCI_RECORD)) 1572 MCI_SendCommand(auto_open, MCI_CLOSE, 0, 0); 1573 else 1574 FIXME("leaking auto-open device %u\n", auto_open); 1575 } 1576 if (wMsg == MCI_OPEN && LOWORD(dwRet) && wmd) 1577 MCI_UnLoadMciDriver(wmd); 1578 HeapFree(GetProcessHeap(), 0, devType); 1579 HeapFree(GetProcessHeap(), 0, verb); 1580 return dwRet; 1581 } 1582 1583 /************************************************************************** 1584 * mciSendStringA [WINMM.@] 1585 */ 1586 DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrRet, 1587 UINT uRetLen, HWND hwndCallback) 1588 { 1589 LPWSTR lpwstrCommand; 1590 LPWSTR lpwstrRet = NULL; 1591 UINT ret; 1592 INT len; 1593 1594 /* FIXME: is there something to do with lpstrReturnString ? */ 1595 len = MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, NULL, 0 ); 1596 lpwstrCommand = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); 1597 MultiByteToWideChar( CP_ACP, 0, lpstrCommand, -1, lpwstrCommand, len ); 1598 if (lpstrRet) 1599 { 1600 if (uRetLen) *lpstrRet = '\0'; /* NT-w2k3 use memset(lpstrRet, 0, uRetLen); */ 1601 lpwstrRet = HeapAlloc(GetProcessHeap(), 0, uRetLen * sizeof(WCHAR)); 1602 if (!lpwstrRet) { 1603 HeapFree( GetProcessHeap(), 0, lpwstrCommand ); 1604 return MCIERR_OUT_OF_MEMORY; 1605 } 1606 } 1607 ret = mciSendStringW(lpwstrCommand, lpwstrRet, uRetLen, hwndCallback); 1608 if (!ret && lpwstrRet) 1609 WideCharToMultiByte( CP_ACP, 0, lpwstrRet, -1, lpstrRet, uRetLen, NULL, NULL ); 1610 HeapFree(GetProcessHeap(), 0, lpwstrCommand); 1611 HeapFree(GetProcessHeap(), 0, lpwstrRet); 1612 return ret; 1613 } 1614 1615 /************************************************************************** 1616 * mciExecute [WINMM.@] 1617 */ 1618 BOOL WINAPI mciExecute(LPCSTR lpstrCommand) 1619 { 1620 char strRet[256]; 1621 DWORD ret; 1622 1623 TRACE("(%s)!\n", lpstrCommand); 1624 1625 ret = mciSendStringA(lpstrCommand, strRet, sizeof(strRet), 0); 1626 if (ret != 0) { 1627 if (!mciGetErrorStringA(ret, strRet, sizeof(strRet))) { 1628 sprintf(strRet, "Unknown MCI error (%ld)", ret); 1629 } 1630 MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK); 1631 } 1632 /* FIXME: what shall I return ? */ 1633 return TRUE; 1634 } 1635 1636 /************************************************************************** 1637 * mciLoadCommandResource [WINMM.@] 1638 * 1639 * Strangely, this function only exists as a UNICODE one. 1640 */ 1641 UINT WINAPI mciLoadCommandResource(HINSTANCE hInst, LPCWSTR resNameW, UINT type) 1642 { 1643 UINT ret = MCI_NO_COMMAND_TABLE; 1644 HRSRC hRsrc = 0; 1645 HGLOBAL hMem; 1646 1647 TRACE("(%p, %s, %d)!\n", hInst, debugstr_w(resNameW), type); 1648 1649 /* if a file named "resname.mci" exits, then load resource "resname" from it 1650 * otherwise directly from driver 1651 * We don't support it (who uses this feature ?), but we check anyway 1652 */ 1653 if (!type) { 1654 #if 0 1655 /* FIXME: we should put this back into order, but I never found a program 1656 * actually using this feature, so we may not need it 1657 */ 1658 char buf[128]; 1659 OFSTRUCT ofs; 1660 1661 strcat(strcpy(buf, resname), ".mci"); 1662 if (OpenFile(buf, &ofs, OF_EXIST) != HFILE_ERROR) { 1663 FIXME("NIY: command table to be loaded from '%s'\n", ofs.szPathName); 1664 } 1665 #endif 1666 } 1667 if ((hRsrc = FindResourceW(hInst, resNameW, (LPWSTR)RT_RCDATA)) && 1668 (hMem = LoadResource(hInst, hRsrc))) { 1669 ret = MCI_SetCommandTable(hMem, type); 1670 FreeResource(hMem); 1671 } 1672 else WARN("No command table found in module for %s\n", debugstr_w(resNameW)); 1673 1674 TRACE("=> %04x\n", ret); 1675 return ret; 1676 } 1677 1678 /************************************************************************** 1679 * mciFreeCommandResource [WINMM.@] 1680 */ 1681 BOOL WINAPI mciFreeCommandResource(UINT uTable) 1682 { 1683 TRACE("(%08x)!\n", uTable); 1684 1685 if (uTable >= MAX_MCICMDTABLE || !S_MciCmdTable[uTable].lpTable) 1686 return FALSE; 1687 1688 FreeResource(S_MciCmdTable[uTable].hMem); 1689 S_MciCmdTable[uTable].hMem = NULL; 1690 S_MciCmdTable[uTable].lpTable = NULL; 1691 HeapFree(GetProcessHeap(), 0, S_MciCmdTable[uTable].aVerbs); 1692 S_MciCmdTable[uTable].aVerbs = 0; 1693 S_MciCmdTable[uTable].nVerbs = 0; 1694 return TRUE; 1695 } 1696 1697 /************************************************************************** 1698 * MCI_Open [internal] 1699 */ 1700 static DWORD MCI_Open(DWORD dwParam, LPMCI_OPEN_PARMSW lpParms) 1701 { 1702 WCHAR strDevTyp[128]; 1703 DWORD dwRet; 1704 LPWINE_MCIDRIVER wmd = NULL; 1705 1706 TRACE("(%08lX, %p)\n", dwParam, lpParms); 1707 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1708 1709 /* only two low bytes are generic, the other ones are dev type specific */ 1710 #define WINE_MCIDRIVER_SUPP (0xFFFF0000|MCI_OPEN_SHAREABLE|MCI_OPEN_ELEMENT| \ 1711 MCI_OPEN_ALIAS|MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID| \ 1712 MCI_NOTIFY|MCI_WAIT) 1713 if ((dwParam & ~WINE_MCIDRIVER_SUPP) != 0) 1714 FIXME("Unsupported yet dwFlags=%08lX\n", dwParam); 1715 #undef WINE_MCIDRIVER_SUPP 1716 1717 strDevTyp[0] = 0; 1718 1719 if (dwParam & MCI_OPEN_TYPE) { 1720 if (dwParam & MCI_OPEN_TYPE_ID) { 1721 WORD uDevType = LOWORD(lpParms->lpstrDeviceType); 1722 1723 if (uDevType < MCI_DEVTYPE_FIRST || uDevType > MCI_DEVTYPE_LAST || 1724 !LoadStringW(hWinMM32Instance, uDevType, strDevTyp, ARRAY_SIZE(strDevTyp))) { 1725 dwRet = MCIERR_BAD_INTEGER; 1726 goto errCleanUp; 1727 } 1728 } else { 1729 LPWSTR ptr; 1730 if (lpParms->lpstrDeviceType == NULL) { 1731 dwRet = MCIERR_NULL_PARAMETER_BLOCK; 1732 goto errCleanUp; 1733 } 1734 lstrcpyW(strDevTyp, lpParms->lpstrDeviceType); 1735 ptr = wcschr(strDevTyp, '!'); 1736 if (ptr) { 1737 /* this behavior is not documented in windows. However, since, in 1738 * some occasions, MCI_OPEN handling is translated by WinMM into 1739 * a call to mciSendString("open <type>"); this code shall be correct 1740 */ 1741 if (dwParam & MCI_OPEN_ELEMENT) { 1742 ERR("Both MCI_OPEN_ELEMENT(%s) and %s are used\n", 1743 debugstr_w(lpParms->lpstrElementName), 1744 debugstr_w(strDevTyp)); 1745 dwRet = MCIERR_UNRECOGNIZED_KEYWORD; 1746 goto errCleanUp; 1747 } 1748 dwParam |= MCI_OPEN_ELEMENT; 1749 *ptr++ = 0; 1750 /* FIXME: not a good idea to write in user supplied buffer */ 1751 lpParms->lpstrElementName = ptr; 1752 } 1753 1754 } 1755 TRACE("devType=%s !\n", debugstr_w(strDevTyp)); 1756 } 1757 1758 if (dwParam & MCI_OPEN_ELEMENT) { 1759 TRACE("lpstrElementName=%s\n", debugstr_w(lpParms->lpstrElementName)); 1760 1761 if (dwParam & MCI_OPEN_ELEMENT_ID) { 1762 FIXME("Unsupported yet flag MCI_OPEN_ELEMENT_ID\n"); 1763 dwRet = MCIERR_UNRECOGNIZED_KEYWORD; 1764 goto errCleanUp; 1765 } 1766 1767 if (!lpParms->lpstrElementName) { 1768 dwRet = MCIERR_NULL_PARAMETER_BLOCK; 1769 goto errCleanUp; 1770 } 1771 1772 /* type, if given as a parameter, supersedes file extension */ 1773 if (!strDevTyp[0] && 1774 MCI_GetDevTypeFromFileName(lpParms->lpstrElementName, 1775 strDevTyp, sizeof(strDevTyp))) { 1776 if (GetDriveTypeW(lpParms->lpstrElementName) != DRIVE_CDROM) { 1777 dwRet = MCIERR_EXTENSION_NOT_FOUND; 1778 goto errCleanUp; 1779 } 1780 /* FIXME: this will not work if several CDROM drives are installed on the machine */ 1781 lstrcpyW(strDevTyp, L"CDAUDIO"); 1782 } 1783 } 1784 1785 if (strDevTyp[0] == 0) { 1786 FIXME("Couldn't load driver\n"); 1787 dwRet = MCIERR_INVALID_DEVICE_NAME; 1788 goto errCleanUp; 1789 } 1790 1791 if (dwParam & MCI_OPEN_ALIAS) { 1792 TRACE("Alias=%s !\n", debugstr_w(lpParms->lpstrAlias)); 1793 if (!lpParms->lpstrAlias) { 1794 dwRet = MCIERR_NULL_PARAMETER_BLOCK; 1795 goto errCleanUp; 1796 } 1797 } 1798 1799 if ((dwRet = MCI_LoadMciDriver(strDevTyp, &wmd))) { 1800 goto errCleanUp; 1801 } 1802 1803 if ((dwRet = MCI_FinishOpen(wmd, lpParms, dwParam))) { 1804 TRACE("Failed to open driver (MCI_OPEN_DRIVER) [%08lx], closing\n", dwRet); 1805 /* FIXME: is dwRet the correct ret code ? */ 1806 goto errCleanUp; 1807 } 1808 1809 /* only handled devices fall through */ 1810 TRACE("wDevID=%04X wDeviceID=%d dwRet=%ld\n", wmd->wDeviceID, lpParms->wDeviceID, dwRet); 1811 return 0; 1812 1813 errCleanUp: 1814 if (wmd) MCI_UnLoadMciDriver(wmd); 1815 return dwRet; 1816 } 1817 1818 /************************************************************************** 1819 * MCI_Close [internal] 1820 */ 1821 static DWORD MCI_Close(UINT wDevID, DWORD dwParam, LPMCI_GENERIC_PARMS lpParms) 1822 { 1823 DWORD dwRet; 1824 LPWINE_MCIDRIVER wmd; 1825 1826 TRACE("(%04x, %08lX, %p)\n", wDevID, dwParam, lpParms); 1827 1828 /* Every device must handle MCI_NOTIFY on its own. */ 1829 if ((UINT16)wDevID == (UINT16)MCI_ALL_DEVICE_ID) { 1830 while (MciDrivers) { 1831 /* Retrieve the device ID under lock, but send the message without, 1832 * the driver might be calling some winmm functions from another 1833 * thread before being fully stopped. 1834 */ 1835 EnterCriticalSection(&WINMM_cs); 1836 if (!MciDrivers) 1837 { 1838 LeaveCriticalSection(&WINMM_cs); 1839 break; 1840 } 1841 wDevID = MciDrivers->wDeviceID; 1842 LeaveCriticalSection(&WINMM_cs); 1843 MCI_Close(wDevID, dwParam, lpParms); 1844 } 1845 return 0; 1846 } 1847 1848 if (!(wmd = MCI_GetDriver(wDevID))) { 1849 return MCIERR_INVALID_DEVICE_ID; 1850 } 1851 1852 if(wmd->CreatorThread != GetCurrentThreadId()) 1853 return MCIERR_INVALID_DEVICE_NAME; 1854 1855 dwRet = MCI_SendCommandFrom32(wDevID, MCI_CLOSE_DRIVER, dwParam, (DWORD_PTR)lpParms); 1856 1857 MCI_UnLoadMciDriver(wmd); 1858 1859 return dwRet; 1860 } 1861 1862 /************************************************************************** 1863 * MCI_WriteString [internal] 1864 */ 1865 static DWORD MCI_WriteString(LPWSTR lpDstStr, DWORD dstSize, LPCWSTR lpSrcStr) 1866 { 1867 DWORD ret = 0; 1868 1869 if (lpSrcStr) { 1870 if (dstSize <= lstrlenW(lpSrcStr)) { 1871 ret = MCIERR_PARAM_OVERFLOW; 1872 } else { 1873 lstrcpyW(lpDstStr, lpSrcStr); 1874 } 1875 } else { 1876 *lpDstStr = 0; 1877 } 1878 return ret; 1879 } 1880 1881 /************************************************************************** 1882 * MCI_Sysinfo [internal] 1883 */ 1884 static DWORD MCI_SysInfo(UINT uDevID, DWORD dwFlags, LPMCI_SYSINFO_PARMSW lpParms) 1885 { 1886 DWORD ret = MCIERR_INVALID_DEVICE_ID, cnt = 0; 1887 WCHAR buf[2048], *s, *p; 1888 LPWINE_MCIDRIVER wmd; 1889 HKEY hKey; 1890 1891 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 1892 if (lpParms->lpstrReturn == NULL) return MCIERR_PARAM_OVERFLOW; 1893 1894 TRACE("(%08x, %08lX, %p[num=%ld, wDevTyp=%u])\n", 1895 uDevID, dwFlags, lpParms, lpParms->dwNumber, lpParms->wDeviceType); 1896 if ((WORD)MCI_ALL_DEVICE_ID == LOWORD(uDevID)) 1897 uDevID = MCI_ALL_DEVICE_ID; /* Be compatible with Win9x */ 1898 1899 switch (dwFlags & ~(MCI_SYSINFO_OPEN|MCI_NOTIFY|MCI_WAIT)) { 1900 case MCI_SYSINFO_QUANTITY: 1901 if (lpParms->dwRetSize < sizeof(DWORD)) 1902 return MCIERR_PARAM_OVERFLOW; 1903 /* Win9x returns 0 for 0 < uDevID < (UINT16)MCI_ALL_DEVICE_ID */ 1904 if (uDevID == MCI_ALL_DEVICE_ID) { 1905 /* wDeviceType == MCI_ALL_DEVICE_ID is not recognized. */ 1906 if (dwFlags & MCI_SYSINFO_OPEN) { 1907 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers\n"); 1908 EnterCriticalSection(&WINMM_cs); 1909 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) { 1910 cnt++; 1911 } 1912 LeaveCriticalSection(&WINMM_cs); 1913 } else { 1914 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers\n"); 1915 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci, 1916 0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) { 1917 RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0); 1918 RegCloseKey( hKey ); 1919 } 1920 if (GetPrivateProfileStringW(L"MCI", 0, L"", buf, ARRAY_SIZE(buf), L"system.ini")) 1921 for (s = buf; *s; s += lstrlenW(s) + 1) cnt++; 1922 } 1923 } else { 1924 if (dwFlags & MCI_SYSINFO_OPEN) { 1925 TRACE("MCI_SYSINFO_QUANTITY: # of open MCI drivers of type %d\n", lpParms->wDeviceType); 1926 EnterCriticalSection(&WINMM_cs); 1927 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) { 1928 if (wmd->wType == lpParms->wDeviceType) cnt++; 1929 } 1930 LeaveCriticalSection(&WINMM_cs); 1931 } else { 1932 TRACE("MCI_SYSINFO_QUANTITY: # of installed MCI drivers of type %d\n", lpParms->wDeviceType); 1933 FIXME("Don't know how to get # of MCI devices of a given type\n"); 1934 /* name = LoadStringW(hWinMM32Instance, LOWORD(lpParms->wDeviceType)) 1935 * then lookup registry and/or system.ini for name, ignoring digits suffix */ 1936 switch (LOWORD(lpParms->wDeviceType)) { 1937 case MCI_DEVTYPE_CD_AUDIO: 1938 case MCI_DEVTYPE_WAVEFORM_AUDIO: 1939 case MCI_DEVTYPE_SEQUENCER: 1940 cnt = 1; 1941 break; 1942 default: /* "digitalvideo" gets 0 because it's not in the registry */ 1943 cnt = 0; 1944 } 1945 } 1946 } 1947 *(DWORD*)lpParms->lpstrReturn = cnt; 1948 TRACE("(%ld) => '%ld'\n", lpParms->dwNumber, *(DWORD*)lpParms->lpstrReturn); 1949 ret = MCI_INTEGER_RETURNED; 1950 /* return ret; Only Win9x sends a notification in this case. */ 1951 break; 1952 case MCI_SYSINFO_INSTALLNAME: 1953 TRACE("MCI_SYSINFO_INSTALLNAME\n"); 1954 if ((wmd = MCI_GetDriver(uDevID))) { 1955 ret = MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, 1956 wmd->lpstrDeviceType); 1957 } else { 1958 ret = (uDevID == MCI_ALL_DEVICE_ID) 1959 ? MCIERR_CANNOT_USE_ALL : MCIERR_INVALID_DEVICE_NAME; 1960 } 1961 TRACE("(%ld) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn)); 1962 break; 1963 case MCI_SYSINFO_NAME: 1964 s = NULL; 1965 if (dwFlags & MCI_SYSINFO_OPEN) { 1966 /* Win9x returns 0 for 0 < uDevID < (UINT16)MCI_ALL_DEVICE_ID */ 1967 TRACE("MCI_SYSINFO_NAME: nth alias of type %d\n", 1968 uDevID == MCI_ALL_DEVICE_ID ? MCI_ALL_DEVICE_ID : lpParms->wDeviceType); 1969 EnterCriticalSection(&WINMM_cs); 1970 for (wmd = MciDrivers; wmd; wmd = wmd->lpNext) { 1971 /* wDeviceType == MCI_ALL_DEVICE_ID is not recognized. */ 1972 if (uDevID == MCI_ALL_DEVICE_ID || 1973 lpParms->wDeviceType == wmd->wType) { 1974 cnt++; 1975 if (cnt == lpParms->dwNumber) { 1976 s = wmd->lpstrAlias; 1977 break; 1978 } 1979 } 1980 } 1981 LeaveCriticalSection(&WINMM_cs); 1982 ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, s) : MCIERR_OUTOFRANGE; 1983 } else if (MCI_ALL_DEVICE_ID == uDevID) { 1984 TRACE("MCI_SYSINFO_NAME: device #%ld\n", lpParms->dwNumber); 1985 if (RegOpenKeyExW( HKEY_LOCAL_MACHINE, wszHklmMci, 0, 1986 KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS) { 1987 if (RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 1988 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS && 1989 lpParms->dwNumber <= cnt) { 1990 DWORD bufLen = ARRAY_SIZE(buf); 1991 if (RegEnumKeyExW(hKey, lpParms->dwNumber - 1, 1992 buf, &bufLen, 0, 0, 0, 0) == ERROR_SUCCESS) 1993 s = buf; 1994 } 1995 RegCloseKey( hKey ); 1996 } 1997 if (!s) { 1998 if (GetPrivateProfileStringW(L"MCI", 0, L"", buf, ARRAY_SIZE(buf), L"system.ini")) { 1999 for (p = buf; *p; p += lstrlenW(p) + 1, cnt++) { 2000 TRACE("%ld: %s\n", cnt, debugstr_w(p)); 2001 if (cnt == lpParms->dwNumber - 1) { 2002 s = p; 2003 break; 2004 } 2005 } 2006 } 2007 } 2008 ret = s ? MCI_WriteString(lpParms->lpstrReturn, lpParms->dwRetSize, s) : MCIERR_OUTOFRANGE; 2009 } else { 2010 FIXME("MCI_SYSINFO_NAME: nth device of type %d\n", lpParms->wDeviceType); 2011 /* Cheating: what is asked for is the nth device from the registry. */ 2012 if (1 != lpParms->dwNumber || /* Handle only one of each kind. */ 2013 lpParms->wDeviceType < MCI_DEVTYPE_FIRST || lpParms->wDeviceType > MCI_DEVTYPE_LAST) 2014 ret = MCIERR_OUTOFRANGE; 2015 else { 2016 LoadStringW(hWinMM32Instance, LOWORD(lpParms->wDeviceType), 2017 lpParms->lpstrReturn, lpParms->dwRetSize); 2018 ret = 0; 2019 } 2020 } 2021 TRACE("(%ld) => %s\n", lpParms->dwNumber, debugstr_w(lpParms->lpstrReturn)); 2022 break; 2023 default: 2024 TRACE("Unsupported flag value=%08lx\n", dwFlags); 2025 ret = MCIERR_UNRECOGNIZED_KEYWORD; 2026 } 2027 if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0) 2028 mciDriverNotify((HWND)lpParms->dwCallback, uDevID, MCI_NOTIFY_SUCCESSFUL); 2029 return ret; 2030 } 2031 2032 /************************************************************************** 2033 * MCI_Break [internal] 2034 */ 2035 static DWORD MCI_Break(UINT wDevID, DWORD dwFlags, LPMCI_BREAK_PARMS lpParms) 2036 { 2037 DWORD dwRet; 2038 2039 if (lpParms == NULL) 2040 return MCIERR_NULL_PARAMETER_BLOCK; 2041 2042 TRACE("(%08x, %08lX, vkey %04X, hwnd %p)\n", wDevID, dwFlags, 2043 lpParms->nVirtKey, lpParms->hwndBreak); 2044 2045 dwRet = MCI_SendCommandFrom32(wDevID, MCI_BREAK, dwFlags, (DWORD_PTR)lpParms); 2046 if (!dwRet && (dwFlags & MCI_NOTIFY)) 2047 mciDriverNotify((HWND)lpParms->dwCallback, wDevID, MCI_NOTIFY_SUCCESSFUL); 2048 return dwRet; 2049 } 2050 2051 /************************************************************************** 2052 * MCI_Sound [internal] 2053 */ 2054 static DWORD MCI_Sound(UINT wDevID, DWORD dwFlags, LPMCI_SOUND_PARMSW lpParms) 2055 { 2056 DWORD dwRet; 2057 2058 if (dwFlags & MCI_SOUND_NAME) { 2059 if (lpParms == NULL) return MCIERR_NULL_PARAMETER_BLOCK; 2060 else dwRet = PlaySoundW(lpParms->lpstrSoundName, NULL, 2061 SND_ALIAS | (dwFlags & MCI_WAIT ? SND_SYNC : SND_ASYNC)) 2062 ? 0 : MCIERR_HARDWARE; 2063 } else dwRet = PlaySoundW((LPCWSTR)SND_ALIAS_SYSTEMDEFAULT, NULL, 2064 SND_ALIAS_ID | (dwFlags & MCI_WAIT ? SND_SYNC : SND_ASYNC)) 2065 ? 0 : MCIERR_HARDWARE; 2066 2067 if (!dwRet && lpParms && (dwFlags & MCI_NOTIFY)) 2068 mciDriverNotify((HWND)lpParms->dwCallback, wDevID, MCI_NOTIFY_SUCCESSFUL); 2069 return dwRet; 2070 } 2071 2072 /************************************************************************** 2073 * MCI_SendCommand [internal] 2074 */ 2075 DWORD MCI_SendCommand(UINT wDevID, UINT16 wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 2076 { 2077 DWORD dwRet = MCIERR_UNRECOGNIZED_COMMAND; 2078 2079 switch (wMsg) { 2080 case MCI_OPEN: 2081 dwRet = MCI_Open(dwParam1, (LPMCI_OPEN_PARMSW)dwParam2); 2082 break; 2083 case MCI_CLOSE: 2084 dwRet = MCI_Close(wDevID, dwParam1, (LPMCI_GENERIC_PARMS)dwParam2); 2085 break; 2086 case MCI_SYSINFO: 2087 dwRet = MCI_SysInfo(wDevID, dwParam1, (LPMCI_SYSINFO_PARMSW)dwParam2); 2088 break; 2089 case MCI_BREAK: 2090 dwRet = MCI_Break(wDevID, dwParam1, (LPMCI_BREAK_PARMS)dwParam2); 2091 break; 2092 case MCI_SOUND: 2093 dwRet = MCI_Sound(wDevID, dwParam1, (LPMCI_SOUND_PARMSW)dwParam2); 2094 break; 2095 default: 2096 if ((UINT16)wDevID == (UINT16)MCI_ALL_DEVICE_ID) { 2097 FIXME("unhandled MCI_ALL_DEVICE_ID\n"); 2098 dwRet = MCIERR_CANNOT_USE_ALL; 2099 } else { 2100 dwRet = MCI_SendCommandFrom32(wDevID, wMsg, dwParam1, dwParam2); 2101 } 2102 break; 2103 } 2104 return dwRet; 2105 } 2106 2107 /************************************************************************** 2108 * MCI_CleanUp [internal] 2109 * 2110 * Some MCI commands need to be cleaned-up (when not called from 2111 * mciSendString), because MCI drivers return extra information for string 2112 * transformation. This function gets rid of them. 2113 */ 2114 static LRESULT MCI_CleanUp(LRESULT dwRet, UINT wMsg, DWORD_PTR dwParam2) 2115 { 2116 if (LOWORD(dwRet)) 2117 return LOWORD(dwRet); 2118 2119 switch (wMsg) { 2120 case MCI_GETDEVCAPS: 2121 switch (dwRet & 0xFFFF0000ul) { 2122 case 0: 2123 case MCI_COLONIZED3_RETURN: 2124 case MCI_COLONIZED4_RETURN: 2125 case MCI_INTEGER_RETURNED: 2126 /* nothing to do */ 2127 break; 2128 case MCI_RESOURCE_RETURNED: 2129 case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: 2130 { 2131 LPMCI_GETDEVCAPS_PARMS lmgp; 2132 2133 lmgp = (LPMCI_GETDEVCAPS_PARMS)dwParam2; 2134 TRACE("Changing %08lx to %08x\n", lmgp->dwReturn, LOWORD(lmgp->dwReturn)); 2135 lmgp->dwReturn = LOWORD(lmgp->dwReturn); 2136 } 2137 break; 2138 default: 2139 FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n", 2140 HIWORD(dwRet), MCI_MessageToString(wMsg)); 2141 } 2142 break; 2143 case MCI_STATUS: 2144 switch (dwRet & 0xFFFF0000ul) { 2145 case 0: 2146 case MCI_COLONIZED3_RETURN: 2147 case MCI_COLONIZED4_RETURN: 2148 case MCI_INTEGER_RETURNED: 2149 /* nothing to do */ 2150 break; 2151 case MCI_RESOURCE_RETURNED: 2152 case MCI_RESOURCE_RETURNED|MCI_RESOURCE_DRIVER: 2153 { 2154 LPMCI_STATUS_PARMS lsp; 2155 2156 lsp = (LPMCI_STATUS_PARMS)dwParam2; 2157 TRACE("Changing %08Ix to %08x\n", lsp->dwReturn, LOWORD(lsp->dwReturn)); 2158 lsp->dwReturn = LOWORD(lsp->dwReturn); 2159 } 2160 break; 2161 default: 2162 FIXME("Unsupported value for hiword (%04x) returned by DriverProc(%s)\n", 2163 HIWORD(dwRet), MCI_MessageToString(wMsg)); 2164 } 2165 break; 2166 case MCI_SYSINFO: 2167 switch (dwRet & 0xFFFF0000ul) { 2168 case 0: 2169 case MCI_INTEGER_RETURNED: 2170 /* nothing to do */ 2171 break; 2172 default: 2173 FIXME("Unsupported value for hiword (%04x)\n", HIWORD(dwRet)); 2174 } 2175 break; 2176 default: 2177 if (HIWORD(dwRet)) { 2178 FIXME("Got non null hiword for dwRet=0x%08Ix for command %s\n", 2179 dwRet, MCI_MessageToString(wMsg)); 2180 } 2181 break; 2182 } 2183 return LOWORD(dwRet); 2184 } 2185 2186 /************************************************************************** 2187 * mciGetErrorStringW [WINMM.@] 2188 */ 2189 BOOL WINAPI mciGetErrorStringW(MCIERROR wError, LPWSTR lpstrBuffer, UINT uLength) 2190 { 2191 BOOL ret = FALSE; 2192 2193 if (lpstrBuffer != NULL && uLength > 0 && 2194 wError >= MCIERR_BASE && wError <= MCIERR_CUSTOM_DRIVER_BASE) { 2195 2196 if (LoadStringW(hWinMM32Instance, wError, lpstrBuffer, uLength) > 0) { 2197 ret = TRUE; 2198 } 2199 } 2200 return ret; 2201 } 2202 2203 /************************************************************************** 2204 * mciGetErrorStringA [WINMM.@] 2205 */ 2206 BOOL WINAPI mciGetErrorStringA(MCIERROR dwError, LPSTR lpstrBuffer, UINT uLength) 2207 { 2208 BOOL ret = FALSE; 2209 2210 if (lpstrBuffer != NULL && uLength > 0 && 2211 dwError >= MCIERR_BASE && dwError <= MCIERR_CUSTOM_DRIVER_BASE) { 2212 2213 if (LoadStringA(hWinMM32Instance, dwError, lpstrBuffer, uLength) > 0) { 2214 ret = TRUE; 2215 } 2216 } 2217 return ret; 2218 } 2219 2220 /************************************************************************** 2221 * mciDriverNotify [WINMM.@] 2222 */ 2223 BOOL WINAPI mciDriverNotify(HWND hWndCallBack, MCIDEVICEID wDevID, UINT wStatus) 2224 { 2225 TRACE("(%p, %04x, %04X)\n", hWndCallBack, wDevID, wStatus); 2226 2227 return PostMessageW(hWndCallBack, MM_MCINOTIFY, wStatus, wDevID); 2228 } 2229 2230 /************************************************************************** 2231 * mciGetDriverData [WINMM.@] 2232 */ 2233 DWORD_PTR WINAPI mciGetDriverData(MCIDEVICEID uDeviceID) 2234 { 2235 LPWINE_MCIDRIVER wmd; 2236 2237 TRACE("(%04x)\n", uDeviceID); 2238 2239 wmd = MCI_GetDriver(uDeviceID); 2240 2241 if (!wmd) { 2242 WARN("Bad uDeviceID\n"); 2243 return 0L; 2244 } 2245 2246 return wmd->dwPrivate; 2247 } 2248 2249 /************************************************************************** 2250 * mciSetDriverData [WINMM.@] 2251 */ 2252 BOOL WINAPI mciSetDriverData(MCIDEVICEID uDeviceID, DWORD_PTR data) 2253 { 2254 LPWINE_MCIDRIVER wmd; 2255 2256 TRACE("(%04x, %08Ix)\n", uDeviceID, data); 2257 2258 wmd = MCI_GetDriver(uDeviceID); 2259 2260 if (!wmd) { 2261 WARN("Bad uDeviceID\n"); 2262 return FALSE; 2263 } 2264 2265 wmd->dwPrivate = data; 2266 return TRUE; 2267 } 2268 2269 /************************************************************************** 2270 * mciSendCommandW [WINMM.@] 2271 * 2272 */ 2273 DWORD WINAPI mciSendCommandW(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 2274 { 2275 DWORD dwRet; 2276 2277 TRACE("(%08x, %s, %08Ix, %08Ix)\n", 2278 wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2); 2279 2280 dwRet = MCI_SendCommand(wDevID, wMsg, dwParam1, dwParam2); 2281 dwRet = MCI_CleanUp(dwRet, wMsg, dwParam2); 2282 TRACE("=> %08lx\n", dwRet); 2283 return dwRet; 2284 } 2285 2286 /************************************************************************** 2287 * mciSendCommandA [WINMM.@] 2288 */ 2289 DWORD WINAPI mciSendCommandA(MCIDEVICEID wDevID, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2) 2290 { 2291 DWORD ret; 2292 int mapped; 2293 2294 TRACE("(%08x, %s, %08Ix, %08Ix)\n", 2295 wDevID, MCI_MessageToString(wMsg), dwParam1, dwParam2); 2296 2297 mapped = MCI_MapMsgAtoW(wMsg, dwParam1, &dwParam2); 2298 if (mapped == -1) 2299 { 2300 FIXME("message %04x mapping failed\n", wMsg); 2301 return MCIERR_OUT_OF_MEMORY; 2302 } 2303 ret = mciSendCommandW(wDevID, wMsg, dwParam1, dwParam2); 2304 if (mapped) 2305 MCI_UnmapMsgAtoW(wMsg, dwParam1, dwParam2, ret); 2306 return ret; 2307 } 2308 2309 /************************************************************************** 2310 * mciGetDeviceIDA [WINMM.@] 2311 */ 2312 UINT WINAPI mciGetDeviceIDA(LPCSTR lpstrName) 2313 { 2314 LPWSTR w = MCI_strdupAtoW(lpstrName); 2315 UINT ret = MCIERR_OUT_OF_MEMORY; 2316 2317 if (w) 2318 { 2319 ret = mciGetDeviceIDW(w); 2320 HeapFree(GetProcessHeap(), 0, w); 2321 } 2322 return ret; 2323 } 2324 2325 /************************************************************************** 2326 * mciGetDeviceIDW [WINMM.@] 2327 */ 2328 UINT WINAPI mciGetDeviceIDW(LPCWSTR lpwstrName) 2329 { 2330 return MCI_GetDriverFromString(lpwstrName); 2331 } 2332 2333 /************************************************************************** 2334 * MCI_DefYieldProc [internal] 2335 */ 2336 static UINT WINAPI MCI_DefYieldProc(MCIDEVICEID wDevID, DWORD data) 2337 { 2338 INT16 ret; 2339 MSG msg; 2340 2341 TRACE("(0x%04x, 0x%08lx)\n", wDevID, data); 2342 2343 if ((HIWORD(data) != 0 && HWND_16(GetActiveWindow()) != HIWORD(data)) || 2344 (GetAsyncKeyState(LOWORD(data)) & 1) == 0) { 2345 PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE); 2346 ret = 0; 2347 } else { 2348 msg.hwnd = HWND_32(HIWORD(data)); 2349 while (!PeekMessageW(&msg, msg.hwnd, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)); 2350 ret = -1; 2351 } 2352 return ret; 2353 } 2354 2355 /************************************************************************** 2356 * mciSetYieldProc [WINMM.@] 2357 */ 2358 BOOL WINAPI mciSetYieldProc(MCIDEVICEID uDeviceID, YIELDPROC fpYieldProc, DWORD dwYieldData) 2359 { 2360 LPWINE_MCIDRIVER wmd; 2361 2362 TRACE("(%u, %p, %08lx)\n", uDeviceID, fpYieldProc, dwYieldData); 2363 2364 if (!(wmd = MCI_GetDriver(uDeviceID))) { 2365 WARN("Bad uDeviceID\n"); 2366 return FALSE; 2367 } 2368 2369 wmd->lpfnYieldProc = fpYieldProc; 2370 wmd->dwYieldData = dwYieldData; 2371 2372 return TRUE; 2373 } 2374 2375 /************************************************************************** 2376 * mciGetDeviceIDFromElementIDA [WINMM.@] 2377 */ 2378 UINT WINAPI mciGetDeviceIDFromElementIDA(DWORD dwElementID, LPCSTR lpstrType) 2379 { 2380 LPWSTR w = MCI_strdupAtoW(lpstrType); 2381 UINT ret = 0; 2382 2383 if (w) 2384 { 2385 ret = mciGetDeviceIDFromElementIDW(dwElementID, w); 2386 HeapFree(GetProcessHeap(), 0, w); 2387 } 2388 return ret; 2389 } 2390 2391 /************************************************************************** 2392 * mciGetDeviceIDFromElementIDW [WINMM.@] 2393 */ 2394 UINT WINAPI mciGetDeviceIDFromElementIDW(DWORD dwElementID, LPCWSTR lpstrType) 2395 { 2396 /* FIXME: that's rather strange, there is no 2397 * mciGetDeviceIDFromElementID32A in winmm.spec 2398 */ 2399 FIXME("(%lu, %s) stub\n", dwElementID, debugstr_w(lpstrType)); 2400 return 0; 2401 } 2402 2403 /************************************************************************** 2404 * mciGetYieldProc [WINMM.@] 2405 */ 2406 YIELDPROC WINAPI mciGetYieldProc(MCIDEVICEID uDeviceID, DWORD* lpdwYieldData) 2407 { 2408 LPWINE_MCIDRIVER wmd; 2409 2410 TRACE("(%u, %p)\n", uDeviceID, lpdwYieldData); 2411 2412 if (!(wmd = MCI_GetDriver(uDeviceID))) { 2413 WARN("Bad uDeviceID\n"); 2414 return NULL; 2415 } 2416 if (!wmd->lpfnYieldProc) { 2417 WARN("No proc set\n"); 2418 return NULL; 2419 } 2420 if (lpdwYieldData) *lpdwYieldData = wmd->dwYieldData; 2421 return wmd->lpfnYieldProc; 2422 } 2423 2424 /************************************************************************** 2425 * mciGetCreatorTask [WINMM.@] 2426 */ 2427 HTASK WINAPI mciGetCreatorTask(MCIDEVICEID uDeviceID) 2428 { 2429 LPWINE_MCIDRIVER wmd; 2430 HTASK ret = 0; 2431 2432 if ((wmd = MCI_GetDriver(uDeviceID))) ret = (HTASK)(DWORD_PTR)wmd->CreatorThread; 2433 2434 TRACE("(%u) => %p\n", uDeviceID, ret); 2435 return ret; 2436 } 2437 2438 /************************************************************************** 2439 * mciDriverYield [WINMM.@] 2440 */ 2441 UINT WINAPI mciDriverYield(MCIDEVICEID uDeviceID) 2442 { 2443 LPWINE_MCIDRIVER wmd; 2444 UINT ret = 0; 2445 2446 TRACE("(%04x)\n", uDeviceID); 2447 2448 if (!(wmd = MCI_GetDriver(uDeviceID)) || !wmd->lpfnYieldProc) { 2449 MSG msg; 2450 PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE); 2451 } else { 2452 ret = wmd->lpfnYieldProc(uDeviceID, wmd->dwYieldData); 2453 } 2454 2455 return ret; 2456 } 2457