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

562 lines
15 KiB

  1. /******************************************************************************
  2. Copyright (c) 1985-1998 Microsoft Corporation
  3. Title: mmwnd.c - contains the window procedure for the WINMM 'global'
  4. window
  5. the global window is used by sndPlaySound and MCI for
  6. reciving notification messages.
  7. Version: 1.00
  8. Date: 04-Sep-1990
  9. Author: ToddLa
  10. Changes: SteveDav Jan 92 Ported to NT
  11. *****************************************************************************/
  12. #include "winmmi.h"
  13. #include "mci.h"
  14. // WINMMI.H includes WINDOWS.H which will eventually include WINMM.H
  15. //#ifdef DBG
  16. // #include "netname.h"
  17. //#endif // DBG
  18. #define CLASS_NAME MAKEINTATOM(43) // 42 clashes with 16-bit mmsystem
  19. DWORD mciWindowThreadId;
  20. STATICFN LRESULT mmWndProc(HWND hwnd, MMMESSAGE msg, WPARAM wParam, LPARAM lParam);
  21. STATICFN BOOL WaitForWaitMsg(void);
  22. typedef struct SentMsg {
  23. LRESULT Result;
  24. MMMESSAGE msg;
  25. WPARAM wParam;
  26. LPARAM lParam;
  27. UINT SendingThread;
  28. } SENTMSG, * PSENTMSG;
  29. /*
  30. ** Client notification stuff
  31. */
  32. HWND hwndNotify = NULL;
  33. /*
  34. ** Server notification stuff
  35. */
  36. PGLOBALMCI base;
  37. CRITICAL_SECTION mciGlobalCritSec;
  38. HANDLE hEvent;
  39. /***************************************************************************/
  40. STATICDT BOOL classcreated = FALSE;
  41. STATICFN BOOL PASCAL FAR CreateMMClass(
  42. void)
  43. {
  44. WNDCLASS cls;
  45. if (classcreated) {
  46. return(TRUE);
  47. }
  48. ZeroMemory(&cls, sizeof(WNDCLASS));
  49. cls.hCursor = NULL;
  50. cls.hIcon = LoadIcon(ghInst, MAKEINTRESOURCE(IDI_MCIHWND));
  51. cls.lpszMenuName = NULL;
  52. cls.lpszClassName = CLASS_NAME;
  53. cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  54. cls.hInstance = ghInst;
  55. cls.style = CS_GLOBALCLASS;
  56. cls.lpfnWndProc = (WNDPROC)mmWndProc;
  57. cls.cbWndExtra = 0;
  58. cls.cbClsExtra = 0;
  59. classcreated = RegisterClass(&cls);
  60. return classcreated;
  61. }
  62. STATICDT CHAR mciWndName[] = "MCI command handling window";
  63. //
  64. //
  65. //
  66. BOOL mciGlobalInit(
  67. void)
  68. {
  69. return TRUE;
  70. }
  71. #if _MSC_FULL_VER >= 13008827
  72. #pragma warning(push)
  73. #pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
  74. #endif
  75. //
  76. //
  77. //
  78. STATICFN DWORD mciwnd2(LPVOID lpParams)
  79. {
  80. UINT msg;
  81. LPCWSTR lszSound;
  82. DWORD wFlags;
  83. DWORD n;
  84. WCHAR soundname[MAX_PATH];
  85. while (TRUE) {
  86. LockMCIGlobal;
  87. if (!base->msg) {
  88. #ifdef LATER
  89. This still needs to be tidied up. The intention is to have a
  90. list of sounds that should be played. This will also make it
  91. easier to STOP all sound playing by clearing out the list.
  92. #endif
  93. // We have no work to do; reset the event and wait for
  94. // more work to be posted. By setting the event within
  95. // the lock we are safe from timing windows.
  96. ResetMCIEvent(hEvent);
  97. UnlockMCIGlobal;
  98. dprintf2(("MCIWND2 thread waiting for next event..."));
  99. n = WaitForSingleObject(hEvent, WAIT_FOREVER);
  100. #if DBG
  101. if ((DWORD)-1 == n) {
  102. n = GetLastError();
  103. dprintf2(("Error %d waiting on event in worker thread", n));
  104. }
  105. #endif
  106. LockMCIGlobal;
  107. }
  108. msg = base->msg;
  109. wFlags = base->dwFlags;
  110. lszSound = base->lszSound;
  111. base->msg=0;
  112. if (wFlags & SND_FILENAME) {
  113. // Have to copy the file name
  114. wcscpy(soundname, base->szSound);
  115. lszSound = soundname;
  116. dprintf3(("Copying the soundfile name to a local variable: %ls", lszSound));
  117. } else {
  118. dprintf3(("Playing a system sound"));
  119. }
  120. UnlockMCIGlobal;
  121. PlaySoundW(lszSound, NULL, (wFlags & ~SND_ASYNC)); // Play sync
  122. }
  123. #if DBG
  124. dprintf(("MCIWND2 thread ending...!!"));
  125. #endif
  126. return(0);
  127. }
  128. #if _MSC_FULL_VER >= 13008827
  129. #pragma warning(pop)
  130. #endif
  131. /***************************************************************************
  132. *
  133. * @doc INTERNAL WINMM
  134. *
  135. * @api void | WndTerminate | called when WINMM is terminating
  136. *
  137. ***************************************************************************/
  138. STATICFN void NEAR PASCAL WndTerminate(
  139. void)
  140. {
  141. dprintf1(("hwndNotify terminating"));
  142. if (hwndNotify)
  143. {
  144. dprintf1(("sending Close\n"));
  145. SendMessage(hwndNotify, WM_CLOSE, 0, 0L);
  146. UnregisterClass(CLASS_NAME, ghInst);
  147. }
  148. }
  149. /***************************************************************************
  150. *
  151. * @doc INTERNAL WINMM
  152. *
  153. * @api LRESULT | mmWndProc | The Window procedure for the WINMM window
  154. *
  155. * @comm mmWndProc calls DefWindowProc for all messages except:
  156. *
  157. * MM_MCINOTIFY: calls MciNotify() in MCI.C
  158. * MM_WOM_DONE: calls WaveOutNotify() in PLAYWAV.C
  159. *
  160. * @xref sndPlaySound
  161. *
  162. ***************************************************************************/
  163. STATICFN LRESULT mmWndProc(
  164. HWND hwnd,
  165. MMMESSAGE msg,
  166. WPARAM wParam,
  167. LPARAM lParam)
  168. {
  169. #if DBG
  170. dprintf4(("MMWNDPROC: Msg %5x Hwnd=%8x\r\n wParam=%8x lParam=%8x", msg, hwnd, wParam, lParam));
  171. #endif
  172. switch (msg)
  173. {
  174. case WM_CREATE:
  175. hwndNotify = hwnd;
  176. break;
  177. case MM_MCINOTIFY:
  178. MciNotify((DWORD)wParam, (LONG)lParam);
  179. break;
  180. #define NODELAY
  181. #ifdef NODELAY
  182. case MM_WOM_DONE:
  183. /*
  184. The sound started with sndPlaySound has completed
  185. so we should call the cleanup routine. On NT we do NOT
  186. delay as the wave really has finished playing.
  187. */
  188. dprintf2(("Received MM_WOM_DONE, calling WaveOutNotify"));
  189. WaveOutNotify(0,0);
  190. break;
  191. #else
  192. /*
  193. SOUND_DELAY is the number of ms to delay before closing the wave device
  194. after the buffer is done.
  195. */
  196. #define SOUND_DELAY 300
  197. case WM_TIMER:
  198. KillTimer(hwnd, (UINT)wParam);
  199. WaveOutNotify(0,0);
  200. break;
  201. case MM_WOM_DONE:
  202. /*
  203. The sound started with sndPlaySound has completed
  204. so we should call the cleanup routine. We delay
  205. this call for several hundred milliseconds because
  206. some sound drivers have a nasty characteristic - they
  207. will notify before the final DMA transfer is complete
  208. because the app. supplied buffer is no longer required.
  209. This means that they may have to spin inside a close
  210. request until the dma transfer completes. This hangs
  211. the system for hundreds of milliseconds.
  212. */
  213. dprintf2(("Received MM_WOM_DONE, setting timer delay"));
  214. SetTimer(hwndNotify, 1, SOUND_DELAY, NULL);
  215. break;
  216. #endif
  217. case MM_SND_ABORT: /* Do not need to do anything */
  218. break;
  219. case MM_SND_PLAY:
  220. {
  221. // There is a critical section problem as we have one global, and
  222. // sounds being played on separate threads.
  223. MSG abortmsg;
  224. if (SND_ALIAS_ID == (wParam & SND_ALIAS_ID)) {
  225. return((LRESULT)PlaySound((LPCSTR)lParam, NULL, (DWORD)wParam & ~SND_ASYNC));
  226. }
  227. if (!PeekMessage(&abortmsg, hwnd, MM_SND_ABORT, MM_SND_ABORT, PM_NOREMOVE)) {
  228. // There is no pending synchronous sound
  229. return (LRESULT)(LONG)sndMessage((LPWSTR)lParam, (UINT)wParam);
  230. }
  231. // We must free the sound definition. Note that this does not close
  232. // the critical section as we may be past this check point when the
  233. // synchronous sound causes the abort message to be posted. But it
  234. // will prevent spurious code being run. It is perfectly valid for
  235. // an asynchronous sound to be after the abort message, which is
  236. // why the message is not removed at this point.
  237. dprintf3(("Aborting sound..."));
  238. if (!(wParam & SND_MEMORY)) {
  239. LocalFree((HANDLE)lParam);
  240. }
  241. break;
  242. }
  243. case MM_SND_SEND:
  244. ((PSENTMSG)wParam)->Result =
  245. mmWndProc(NULL, ((PSENTMSG)wParam)->msg,
  246. ((PSENTMSG)wParam)->wParam,
  247. ((PSENTMSG)wParam)->lParam);
  248. PostThreadMessage(((PSENTMSG)wParam)->SendingThread, MCIWAITMSG, 0, 0);
  249. break;
  250. case MM_POLYMSGBUFRDONE:
  251. --(((PMIDIEMU)wParam)->cPostedBuffers);
  252. midiOutNukePMBuffer((PMIDIEMU)wParam, (LPMIDIHDR)lParam);
  253. return (0L);
  254. case MM_MCISYSTEM_STRING:
  255. // In MCI.C
  256. return (LRESULT)mciRelaySystemString ((LPMCI_SYSTEM_MESSAGE)lParam);
  257. default:
  258. return DefWindowProc(hwnd, msg, wParam,lParam);
  259. }
  260. return (LRESULT)0L;
  261. }
  262. void mciwindow(HANDLE hEvent);
  263. /*
  264. ** Initialize all the bits for creating sound. For non-server apps this
  265. ** means initializing our hwnd. For the server we set up a thread et
  266. */
  267. BOOL InitAsyncSound(VOID)
  268. {
  269. if (!WinmmRunningInServer) {
  270. return CreatehwndNotify();
  271. } else {
  272. LockMCIGlobal;
  273. if (base == NULL) {
  274. HANDLE hThread;
  275. PGLOBALMCI pBase;
  276. /*
  277. ** We need a thread, an event (we already have the crit sec) and
  278. ** some memory
  279. */
  280. pBase = mciAlloc(sizeof(GLOBALMCI));
  281. if (pBase == NULL) {
  282. UnlockMCIGlobal;
  283. return FALSE;
  284. }
  285. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  286. if (hEvent == NULL) {
  287. mciFree((PVOID)pBase);
  288. UnlockMCIGlobal;
  289. return FALSE;
  290. }
  291. /*
  292. ** We have to create a thread by a special method inside the
  293. ** server and register it with CSR
  294. */
  295. if (!CreateServerPlayingThread((PVOID)mciwnd2)) {
  296. mciFree((PVOID)pBase);
  297. CloseHandle(hEvent);
  298. hEvent = NULL;
  299. UnlockMCIGlobal;
  300. return FALSE;
  301. }
  302. base = pBase;
  303. }
  304. UnlockMCIGlobal;
  305. return base != NULL;
  306. }
  307. }
  308. BOOL CreatehwndNotify(VOID)
  309. {
  310. HANDLE hWindowThread;
  311. BOOL ReturnCode;
  312. HANDLE hEventForCreate;
  313. mciEnter("CreatehwndNotify");
  314. if (hwndNotify != NULL) {
  315. mciLeave("CreatehwndNotify");
  316. return TRUE;
  317. }
  318. if (!CreateMMClass()) {
  319. dprintf1(("Failed to create the MCI global window class, rc=%d", GetLastError()));
  320. mciLeave("CreatehwndNotify");
  321. return FALSE;
  322. } else {
  323. dprintf4(("Created global window class"));
  324. }
  325. // We create our new thread then suspend ourselves until the new
  326. // thread has called CreateWindow. We are then triggered to run
  327. // and passed the results of the CreateWindow call. NOTE: Any
  328. // messages that arrive for this thread that are not destined for
  329. // a specific window will be DISCARDED until the one message we
  330. // are waiting for arrives. We could create an event and wait
  331. // for that event to be triggered. This was slightly quicker to
  332. // code and involves less creation/destruction of resources.
  333. hEventForCreate = CreateEvent(NULL, FALSE, FALSE, NULL);
  334. if (hEventForCreate != NULL) {
  335. hWindowThread = CreateThread(NULL, // attributes
  336. 0, // same stack size as thread 1
  337. (LPTHREAD_START_ROUTINE)mciwindow,
  338. (LPVOID) hEventForCreate,
  339. 0, // Thread runs immediately
  340. &mciWindowThreadId
  341. );
  342. CloseHandle(hWindowThread);
  343. if (!hWindowThread) {
  344. dprintf1(("Failed to create window thread. Error: %XH", GetLastError()));
  345. } else {
  346. dprintf3(("Window thread is %x", mciWindowThreadId));
  347. WaitForSingleObject(hEventForCreate, INFINITE);
  348. dprintf3(("hwndNotify now %x", hwndNotify));
  349. }
  350. CloseHandle(hEventForCreate);
  351. }
  352. ReturnCode = hwndNotify != NULL;
  353. mciLeave("CreatehwndNotify");
  354. return ReturnCode;
  355. }
  356. void mciwindow(
  357. HANDLE hEvent)
  358. {
  359. BOOL fResult = TRUE;
  360. //
  361. // Higher priority so we hear the sound at once!
  362. // This seems to work better than calling SetThreadPriority
  363. // on the handle just after creation (?).
  364. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
  365. if (!(hwndNotify = CreateWindowEx(0, CLASS_NAME, mciWndName, WS_OVERLAPPED,
  366. 0, 0, 0, 0, NULL, NULL, ghInst, NULL))) {
  367. dprintf1(("Failed to create the MCI global window, rc=%d", GetLastError()));
  368. UnregisterClass(CLASS_NAME, ghInst);
  369. fResult = FALSE;
  370. }
  371. //
  372. // Let our creator thread know we are up and running
  373. //
  374. SetEvent(hEvent);
  375. if (fResult) {
  376. MSG msg;
  377. HWND hwndTemp;
  378. while (GetMessage(&msg, NULL, 0, 0)) {
  379. /*
  380. * If the message is for a window dispatch it
  381. */
  382. dprintf3(("mciwindow - Msg %5x hwnd %8x (%8x %8x)", msg.message, msg.hwnd, msg.wParam, msg.lParam));
  383. if (msg.hwnd != NULL) {
  384. DispatchMessage(&msg);
  385. }
  386. }
  387. hwndTemp = hwndNotify;
  388. hwndNotify = NULL; // Clear the global before destroying the window
  389. DestroyWindow(hwndTemp);
  390. }
  391. ExitThread(0);
  392. }
  393. #if 0 //LATER - not currently used
  394. //
  395. // Routine to SEND (synchronous) a message to another thread. Currently
  396. // the standard API allows you to send a message to a window, or post to
  397. // a thread. There are circumstances when it would be helpful to send
  398. // to a thread.
  399. //
  400. STATICFN LRESULT SendThreadMessage(
  401. UINT tid,
  402. MMMESSAGE msg,
  403. WPARAM wParam,
  404. LPARAM lParam)
  405. {
  406. SENTMSG smsg;
  407. smsg.msg = msg;
  408. smsg.wParam = wParam;
  409. smsg.lParam = lParam;
  410. smsg.SendingThread = GetCurrentThreadId();
  411. PostThreadMessage(tid, MM_SND_SEND, (WPARAM)&smsg, 0);
  412. WaitForWaitMsg();
  413. return(smsg.Result);
  414. }
  415. #endif
  416. /*********************************************************************\
  417. * WaitForWaitMsg: *
  418. * *
  419. * This routine waits until a specific message is returned to this *
  420. * thread. While waiting NO posted messages are processed, but sent *
  421. * messages will be handled within GetMessage. The routine is used *
  422. * to synchronise two threads of execution, and to implement a *
  423. * synchronous PostMessage operation between threads. *
  424. * *
  425. \*********************************************************************/
  426. STATICFN BOOL WaitForWaitMsg() {
  427. for (;;) {
  428. MSG msg;
  429. /*
  430. * Retrieve our particular message
  431. */
  432. GetMessage(&msg, NULL, MCIWAITMSG, MCIWAITMSG);
  433. /*
  434. * If the message is for a window dispatch it
  435. */
  436. WinAssert(msg.hwnd == NULL);
  437. #if 0
  438. if (msg.hwnd != NULL) { // This should not be executed.
  439. DispatchMessage(&msg); // MCIWAITMSG is not sent to a window
  440. } else
  441. #endif
  442. /*
  443. * MCIWAITMSG is the signal message
  444. */
  445. if (msg.message == MCIWAITMSG) {
  446. break;
  447. }
  448. }
  449. return(TRUE);
  450. }