Cleanup header usage in USER32.
[reactos.git] / reactos / lib / user32 / windows / message.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS user32.dll
4 * FILE: lib/user32/windows/message.c
5 * PURPOSE: Messages
6 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * UPDATE HISTORY:
8 * 06-06-2001 CSH Created
9 */
10
11 #include <user32.h>
12
13 /* DDE message exchange
14 *
15 * - Session initialization
16 * Client sends a WM_DDE_INITIATE message, usually a broadcast message. lParam of
17 * this message contains a pair of global atoms, the Application and Topic atoms.
18 * The client must destroy the atoms.
19 * Server window proc handles the WM_DDE_INITIATE message and if the Application
20 * and Topic atoms are recognized sends a WM_DDE_ACK message to the client. lParam
21 * of the reply message contains another pair of global atoms (Application and
22 * Topic again), which must be destroyed by the server.
23 *
24 * - Execute
25 * Client posts a WM_DDE_EXECUTE message to the server window. lParam of that message
26 * is a global memory handle containing the string to execute. After the command has
27 * been executed the server posts a WM_DDE_ACK message to the client, which contains
28 * a packed lParam which in turn contains that global memory handle. The client takes
29 * ownership of both the packed lParam (meaning it needs to call FreeDDElParam() on
30 * it and the global memory handle.
31 * This might work nice and easy in Win3.1, but things are more complicated for NT.
32 * Global memory handles in NT are not really global, they're still local to the
33 * process. So, what happens under the hood is that PostMessage must handle the
34 * WM_DDE_EXECUTE message specially. It will obtain the contents of the global memory
35 * area, repack that into a new structure together with the original memory handle
36 * and pass that off to the win32k. Win32k will marshall that data over to the target
37 * (server) process where it will be unpacked and stored in a newly allocated global
38 * memory area. The handle of that area will then be sent to the window proc, after
39 * storing it together with the "original" (client) handle in a table.
40 * The server will eventually post the WM_DDE_ACK response, containing the global
41 * memory handle it received. PostMessage must then lookup that memory handle (only
42 * valid in the server process) and replace it with the corresponding client memory
43 * handle. To avoid memory leaks, the server-side global memory block must be freed.
44 * Also, the WM_DDE_ACK lParam (a PackDDElParam() result) is unpacked and the
45 * individual components are handed to win32k.sys to post to the client side. Since
46 * the server side app hands over ownership of the packed lParam when it calls
47 * PostMessage(), the packed lParam needs to be freed on the server side too.
48 * When the WM_DDE_ACK message (containing the client-side global memory handle)
49 * arrives at the client side a new lParam is PackDDElParam()'ed and this is handed
50 * to the client side window proc which is expected to free/reuse it.
51 */
52
53 /* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
54 * to the memory handle, we keep track (in the server side) of all pairs of handle
55 * used (the client passes its value and the content of the memory handle), and
56 * the server stored both values (the client, and the local one, created after the
57 * content). When a ACK message is generated, the list of pair is searched for a
58 * matching pair, so that the client memory handle can be returned.
59 */
60 typedef struct tagDDEPAIR
61 {
62 HGLOBAL ClientMem;
63 HGLOBAL ServerMem;
64 } DDEPAIR, *PDDEPAIR;
65
66 static PDDEPAIR DdePairs = NULL;
67 static unsigned DdeNumAlloc = 0;
68 static unsigned DdeNumUsed = 0;
69 static CRITICAL_SECTION DdeCrst;
70
71 static BOOL FASTCALL
72 DdeAddPair(HGLOBAL ClientMem, HGLOBAL ServerMem)
73 {
74 unsigned i;
75
76 EnterCriticalSection(&DdeCrst);
77
78 /* now remember the pair of hMem on both sides */
79 if (DdeNumUsed == DdeNumAlloc)
80 {
81 #define GROWBY 4
82 PDDEPAIR New;
83 if (NULL != DdePairs)
84 {
85 New = HeapReAlloc(GetProcessHeap(), 0, DdePairs,
86 (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR));
87 }
88 else
89 {
90 New = HeapAlloc(GetProcessHeap(), 0,
91 (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR));
92 }
93
94 if (NULL == New)
95 {
96 LeaveCriticalSection(&DdeCrst);
97 return FALSE;
98 }
99 DdePairs = New;
100 /* zero out newly allocated part */
101 memset(&DdePairs[DdeNumAlloc], 0, GROWBY * sizeof(DDEPAIR));
102 DdeNumAlloc += GROWBY;
103 #undef GROWBY
104 }
105
106 for (i = 0; i < DdeNumAlloc; i++)
107 {
108 if (NULL == DdePairs[i].ServerMem)
109 {
110 DdePairs[i].ClientMem = ClientMem;
111 DdePairs[i].ServerMem = ServerMem;
112 DdeNumUsed++;
113 break;
114 }
115 }
116 LeaveCriticalSection(&DdeCrst);
117
118 return TRUE;
119 }
120
121 static HGLOBAL FASTCALL
122 DdeGetPair(HGLOBAL ServerMem)
123 {
124 unsigned i;
125 HGLOBAL Ret = NULL;
126
127 EnterCriticalSection(&DdeCrst);
128 for (i = 0; i < DdeNumAlloc; i++)
129 {
130 if (DdePairs[i].ServerMem == ServerMem)
131 {
132 /* free this pair */
133 DdePairs[i].ServerMem = 0;
134 DdeNumUsed--;
135 Ret = DdePairs[i].ClientMem;
136 break;
137 }
138 }
139 LeaveCriticalSection(&DdeCrst);
140
141 return Ret;
142 }
143
144 static BOOL FASTCALL
145 MsgiUMToKMMessage(PMSG UMMsg, PMSG KMMsg, BOOL Posted)
146 {
147 *KMMsg = *UMMsg;
148
149 switch (UMMsg->message)
150 {
151 case WM_DDE_ACK:
152 {
153 PKMDDELPARAM DdeLparam;
154 DdeLparam = HeapAlloc(GetProcessHeap(), 0, sizeof(KMDDELPARAM));
155 if (NULL == DdeLparam)
156 {
157 return FALSE;
158 }
159 if (Posted)
160 {
161 DdeLparam->Packed = TRUE;
162 if (! UnpackDDElParam(UMMsg->message, UMMsg->lParam,
163 &DdeLparam->Value.Packed.uiLo,
164 &DdeLparam->Value.Packed.uiHi))
165 {
166 return FALSE;
167 }
168 if (0 != HIWORD(DdeLparam->Value.Packed.uiHi))
169 {
170 /* uiHi should contain a hMem from WM_DDE_EXECUTE */
171 HGLOBAL h = DdeGetPair((HGLOBAL) DdeLparam->Value.Packed.uiHi);
172 if (NULL != h)
173 {
174 GlobalFree((HGLOBAL) DdeLparam->Value.Packed.uiHi);
175 DdeLparam->Value.Packed.uiHi = (UINT) h;
176 }
177 }
178 FreeDDElParam(UMMsg->message, UMMsg->lParam);
179 }
180 else
181 {
182 DdeLparam->Packed = FALSE;
183 DdeLparam->Value.Unpacked = UMMsg->lParam;
184 }
185 KMMsg->lParam = (LPARAM) DdeLparam;
186 }
187 break;
188
189 case WM_DDE_EXECUTE:
190 {
191 SIZE_T Size;
192 PKMDDEEXECUTEDATA KMDdeExecuteData;
193 PVOID Data;
194
195 Size = GlobalSize((HGLOBAL) UMMsg->lParam);
196 Data = GlobalLock((HGLOBAL) UMMsg->lParam);
197 if (NULL == Data)
198 {
199 SetLastError(ERROR_INVALID_HANDLE);
200 return FALSE;
201 }
202 KMDdeExecuteData = HeapAlloc(GetProcessHeap(), 0, sizeof(KMDDEEXECUTEDATA) + Size);
203 if (NULL == KMDdeExecuteData)
204 {
205 SetLastError(ERROR_OUTOFMEMORY);
206 return FALSE;
207 }
208 KMDdeExecuteData->Sender = (HWND) UMMsg->wParam;
209 KMDdeExecuteData->ClientMem = (HGLOBAL) UMMsg->lParam;
210 memcpy((PVOID) (KMDdeExecuteData + 1), Data, Size);
211 KMMsg->wParam = sizeof(KMDDEEXECUTEDATA) + Size;
212 KMMsg->lParam = (LPARAM) KMDdeExecuteData;
213 GlobalUnlock((HGLOBAL) UMMsg->lParam);
214 }
215 break;
216
217 case WM_COPYDATA:
218 {
219 PCOPYDATASTRUCT pUMCopyData = (PCOPYDATASTRUCT)UMMsg->lParam;
220 PCOPYDATASTRUCT pKMCopyData;
221
222 pKMCopyData = HeapAlloc(GetProcessHeap(), 0,
223 sizeof(COPYDATASTRUCT) + pUMCopyData->cbData);
224 if (pKMCopyData == NULL)
225 {
226 SetLastError(ERROR_OUTOFMEMORY);
227 return FALSE;
228 }
229
230 pKMCopyData->dwData = pUMCopyData->dwData;
231 pKMCopyData->cbData = pUMCopyData->cbData;
232 pKMCopyData->lpData = pKMCopyData + 1;
233
234 RtlCopyMemory(pKMCopyData + 1, pUMCopyData->lpData,
235 pUMCopyData->cbData);
236
237 KMMsg->lParam = (LPARAM)pKMCopyData;
238 }
239 break;
240
241 default:
242 break;
243 }
244
245 return TRUE;
246 }
247
248 static VOID FASTCALL
249 MsgiUMToKMCleanup(PMSG UMMsg, PMSG KMMsg)
250 {
251 switch (KMMsg->message)
252 {
253 case WM_DDE_ACK:
254 case WM_DDE_EXECUTE:
255 case WM_COPYDATA:
256 HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam);
257 break;
258 default:
259 break;
260 }
261
262 return;
263 }
264
265 static BOOL FASTCALL
266 MsgiUMToKMReply(PMSG UMMsg, PMSG KMMsg, LRESULT *Result)
267 {
268 MsgiUMToKMCleanup(UMMsg, KMMsg);
269
270 return TRUE;
271 }
272
273 static BOOL FASTCALL
274 MsgiKMToUMMessage(PMSG KMMsg, PMSG UMMsg)
275 {
276 *UMMsg = *KMMsg;
277
278 switch (UMMsg->message)
279 {
280 case WM_CREATE:
281 case WM_NCCREATE:
282 {
283 CREATESTRUCTW *Cs = (CREATESTRUCTW *) KMMsg->lParam;
284 PCHAR Class;
285 Cs->lpszName = (LPCWSTR) ((PCHAR) Cs + (DWORD_PTR) Cs->lpszName);
286 Class = (PCHAR) Cs + (DWORD_PTR) Cs->lpszClass;
287 if (L'A' == *((WCHAR *) Class))
288 {
289 Class += sizeof(WCHAR);
290 Cs->lpszClass = (LPCWSTR)(DWORD_PTR) (*((ATOM *) Class));
291 }
292 else
293 {
294 ASSERT(L'S' == *((WCHAR *) Class));
295 Class += sizeof(WCHAR);
296 Cs->lpszClass = (LPCWSTR) Class;
297 }
298 }
299 break;
300
301 case WM_DDE_ACK:
302 {
303 PKMDDELPARAM DdeLparam = (PKMDDELPARAM) KMMsg->lParam;
304 if (DdeLparam->Packed)
305 {
306 UMMsg->lParam = PackDDElParam(KMMsg->message,
307 DdeLparam->Value.Packed.uiLo,
308 DdeLparam->Value.Packed.uiHi);
309 }
310 else
311 {
312 UMMsg->lParam = DdeLparam->Value.Unpacked;
313 }
314 }
315 break;
316
317 case WM_DDE_EXECUTE:
318 {
319 PKMDDEEXECUTEDATA KMDdeExecuteData;
320 HGLOBAL GlobalData;
321 PVOID Data;
322
323 KMDdeExecuteData = (PKMDDEEXECUTEDATA) KMMsg->lParam;
324 GlobalData = GlobalAlloc(GMEM_MOVEABLE, KMMsg->wParam - sizeof(KMDDEEXECUTEDATA));
325 if (NULL == GlobalData)
326 {
327 return FALSE;
328 }
329 Data = GlobalLock(GlobalData);
330 if (NULL == Data)
331 {
332 GlobalFree(GlobalData);
333 return FALSE;
334 }
335 memcpy(Data, (PVOID) (KMDdeExecuteData + 1), KMMsg->wParam - sizeof(KMDDEEXECUTEDATA));
336 GlobalUnlock(GlobalData);
337 if (! DdeAddPair(KMDdeExecuteData->ClientMem, GlobalData))
338 {
339 GlobalFree(GlobalData);
340 return FALSE;
341 }
342 UMMsg->wParam = (WPARAM) KMDdeExecuteData->Sender;
343 UMMsg->lParam = (LPARAM) GlobalData;
344 }
345 break;
346
347 case WM_COPYDATA:
348 {
349 PCOPYDATASTRUCT pKMCopyData = (PCOPYDATASTRUCT)KMMsg->lParam;
350 pKMCopyData->lpData = pKMCopyData + 1;
351 }
352 break;
353
354 default:
355 break;
356 }
357
358 return TRUE;
359 }
360
361 static VOID FASTCALL
362 MsgiKMToUMCleanup(PMSG KMMsg, PMSG UMMsg)
363 {
364 switch (KMMsg->message)
365 {
366 case WM_DDE_EXECUTE:
367 #ifdef TODO
368 HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam);
369 GlobalUnlock((HGLOBAL) UMMsg->lParam);
370 #endif
371 break;
372 default:
373 break;
374 }
375
376 return;
377 }
378
379 static BOOL FASTCALL
380 MsgiKMToUMReply(PMSG KMMsg, PMSG UMMsg, LRESULT *Result)
381 {
382 MsgiKMToUMCleanup(KMMsg, UMMsg);
383
384 return TRUE;
385 }
386
387 static BOOL FASTCALL
388 MsgiAnsiToUnicodeMessage(LPMSG UnicodeMsg, LPMSG AnsiMsg)
389 {
390 *UnicodeMsg = *AnsiMsg;
391 switch (AnsiMsg->message)
392 {
393 case WM_GETTEXT:
394 case WM_ASKCBFORMATNAME:
395 {
396 LPWSTR Buffer = HeapAlloc(GetProcessHeap(), 0,
397 AnsiMsg->wParam * sizeof(WCHAR));
398 if (!Buffer)
399 {
400 return FALSE;
401 }
402 UnicodeMsg->lParam = (LPARAM)Buffer;
403 break;
404 }
405
406 /* AnsiMsg->lParam is string (0-terminated) */
407 case WM_SETTEXT:
408 case WM_WININICHANGE:
409 case WM_DEVMODECHANGE:
410 case CB_DIR:
411 case LB_DIR:
412 case LB_ADDFILE:
413 case EM_REPLACESEL:
414 {
415 UNICODE_STRING UnicodeString;
416 RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam);
417 UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer;
418 break;
419 }
420
421 case WM_NCCREATE:
422 case WM_CREATE:
423 {
424 UNICODE_STRING UnicodeBuffer;
425 struct s
426 {
427 CREATESTRUCTW cs; /* new structure */
428 LPCWSTR lpszName; /* allocated Name */
429 LPCWSTR lpszClass; /* allocated Class */
430 };
431 struct s *xs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct s));
432 if (!xs)
433 {
434 return FALSE;
435 }
436 xs->cs = *(CREATESTRUCTW *)AnsiMsg->lParam;
437 if (HIWORD(xs->cs.lpszName))
438 {
439 RtlCreateUnicodeStringFromAsciiz(&UnicodeBuffer, (LPSTR)xs->cs.lpszName);
440 xs->lpszName = xs->cs.lpszName = UnicodeBuffer.Buffer;
441 }
442 if (HIWORD(xs->cs.lpszClass))
443 {
444 RtlCreateUnicodeStringFromAsciiz(&UnicodeBuffer, (LPSTR)xs->cs.lpszClass);
445 xs->lpszClass = xs->cs.lpszClass = UnicodeBuffer.Buffer;
446 }
447 UnicodeMsg->lParam = (LPARAM)xs;
448 break;
449 }
450
451 case WM_MDICREATE:
452 {
453 UNICODE_STRING UnicodeBuffer;
454 MDICREATESTRUCTW *cs =
455 (MDICREATESTRUCTW *)HeapAlloc(GetProcessHeap(), 0, sizeof(*cs));
456
457 if (!cs)
458 {
459 return FALSE;
460 }
461
462 *cs = *(MDICREATESTRUCTW *)AnsiMsg->lParam;
463
464 if (HIWORD(cs->szClass))
465 {
466 RtlCreateUnicodeStringFromAsciiz(&UnicodeBuffer, (LPSTR)cs->szClass);
467 cs->szClass = UnicodeBuffer.Buffer;
468 }
469
470 RtlCreateUnicodeStringFromAsciiz(&UnicodeBuffer, (LPSTR)cs->szTitle);
471 cs->szTitle = UnicodeBuffer.Buffer;
472
473 UnicodeMsg->lParam = (LPARAM)cs;
474 break;
475 }
476 }
477
478 return TRUE;
479 }
480
481
482 static BOOL FASTCALL
483 MsgiAnsiToUnicodeCleanup(LPMSG UnicodeMsg, LPMSG AnsiMsg)
484 {
485 switch (AnsiMsg->message)
486 {
487 case WM_GETTEXT:
488 case WM_ASKCBFORMATNAME:
489 {
490 HeapFree(GetProcessHeap(), 0, (PVOID) UnicodeMsg->lParam);
491 break;
492 }
493
494 case WM_SETTEXT:
495 case WM_WININICHANGE:
496 case WM_DEVMODECHANGE:
497 case CB_DIR:
498 case LB_DIR:
499 case LB_ADDFILE:
500 case EM_REPLACESEL:
501 {
502 UNICODE_STRING UnicodeString;
503 RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam);
504 RtlFreeUnicodeString(&UnicodeString);
505 break;
506 }
507
508 case WM_NCCREATE:
509 case WM_CREATE:
510 {
511 UNICODE_STRING UnicodeString;
512 struct s
513 {
514 CREATESTRUCTW cs; /* new structure */
515 LPWSTR lpszName; /* allocated Name */
516 LPWSTR lpszClass; /* allocated Class */
517 };
518 struct s *xs = (struct s *)UnicodeMsg->lParam;
519 if (xs->lpszName)
520 {
521 RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszName);
522 RtlFreeUnicodeString(&UnicodeString);
523 }
524 if (xs->lpszClass)
525 {
526 RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszClass);
527 RtlFreeUnicodeString(&UnicodeString);
528 }
529 HeapFree(GetProcessHeap(), 0, xs);
530 }
531 break;
532
533 case WM_MDICREATE:
534 {
535 UNICODE_STRING UnicodeString;
536 MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)UnicodeMsg->lParam;
537 if (HIWORD(cs->szTitle))
538 {
539 RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szTitle);
540 RtlFreeUnicodeString(&UnicodeString);
541 }
542 if (HIWORD(cs->szClass))
543 {
544 RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szClass);
545 RtlFreeUnicodeString(&UnicodeString);
546 }
547 HeapFree(GetProcessHeap(), 0, cs);
548 }
549 break;
550 }
551 return(TRUE);
552 }
553
554
555 static BOOL FASTCALL
556 MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result)
557 {
558 switch (AnsiMsg->message)
559 {
560 case WM_GETTEXT:
561 case WM_ASKCBFORMATNAME:
562 {
563 LPWSTR Buffer = (LPWSTR)UnicodeMsg->lParam;
564 LPSTR AnsiBuffer = (LPSTR)AnsiMsg->lParam;
565 if (UnicodeMsg->wParam > 0 &&
566 !WideCharToMultiByte(CP_ACP, 0, Buffer, -1,
567 AnsiBuffer, UnicodeMsg->wParam, NULL, NULL))
568 {
569 AnsiBuffer[UnicodeMsg->wParam - 1] = 0;
570 }
571 break;
572 }
573 }
574
575 MsgiAnsiToUnicodeCleanup(UnicodeMsg, AnsiMsg);
576
577 return TRUE;
578 }
579
580
581 static BOOL FASTCALL
582 MsgiUnicodeToAnsiMessage(LPMSG AnsiMsg, LPMSG UnicodeMsg)
583 {
584 *AnsiMsg = *UnicodeMsg;
585
586 switch(UnicodeMsg->message)
587 {
588 case WM_CREATE:
589 case WM_NCCREATE:
590 {
591 CREATESTRUCTA* CsA;
592 CREATESTRUCTW* CsW;
593 UNICODE_STRING UString;
594 ANSI_STRING AString;
595 NTSTATUS Status;
596
597 CsW = (CREATESTRUCTW*)(UnicodeMsg->lParam);
598 CsA = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CREATESTRUCTA));
599 if (NULL == CsA)
600 {
601 return FALSE;
602 }
603 memcpy(CsA, CsW, sizeof(CREATESTRUCTW));
604
605 RtlInitUnicodeString(&UString, CsW->lpszName);
606 Status = RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
607 if (! NT_SUCCESS(Status))
608 {
609 RtlFreeHeap(GetProcessHeap(), 0, CsA);
610 return FALSE;
611 }
612 CsA->lpszName = AString.Buffer;
613 if (HIWORD((ULONG)CsW->lpszClass) != 0)
614 {
615 RtlInitUnicodeString(&UString, CsW->lpszClass);
616 Status = RtlUnicodeStringToAnsiString(&AString, &UString, TRUE);
617 if (! NT_SUCCESS(Status))
618 {
619 RtlInitAnsiString(&AString, CsA->lpszName);
620 RtlFreeAnsiString(&AString);
621 RtlFreeHeap(GetProcessHeap(), 0, CsA);
622 return FALSE;
623 }
624 CsA->lpszClass = AString.Buffer;
625 }
626 AnsiMsg->lParam = (LPARAM)CsA;
627 break;
628 }
629 case WM_GETTEXT:
630 {
631 /* Ansi string might contain MBCS chars so we need 2 * the number of chars */
632 AnsiMsg->wParam = UnicodeMsg->wParam * 2;
633 AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), 0, AnsiMsg->wParam);
634 if (NULL == (PVOID) AnsiMsg->lParam)
635 {
636 return FALSE;
637 }
638 break;
639 }
640 case WM_SETTEXT:
641 {
642 ANSI_STRING AnsiString;
643 UNICODE_STRING UnicodeString;
644 RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam);
645 if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString,
646 &UnicodeString,
647 TRUE)))
648 {
649 return FALSE;
650 }
651 AnsiMsg->lParam = (LPARAM) AnsiString.Buffer;
652 break;
653 }
654 }
655
656 return TRUE;
657 }
658
659
660 static BOOL FASTCALL
661 MsgiUnicodeToAnsiCleanup(LPMSG AnsiMsg, LPMSG UnicodeMsg)
662 {
663 switch(UnicodeMsg->message)
664 {
665 case WM_GETTEXT:
666 {
667 RtlFreeHeap(GetProcessHeap(), 0, (PVOID) AnsiMsg->lParam);
668 break;
669 }
670 case WM_SETTEXT:
671 {
672 ANSI_STRING AString;
673 RtlInitAnsiString(&AString, (PSTR) AnsiMsg->lParam);
674 RtlFreeAnsiString(&AString);
675 break;
676 }
677 case WM_CREATE:
678 case WM_NCCREATE:
679 {
680 CREATESTRUCTA* Cs;
681 ANSI_STRING AString;
682
683 Cs = (CREATESTRUCTA*) AnsiMsg->lParam;
684 RtlInitAnsiString(&AString, Cs->lpszName);
685 RtlFreeAnsiString(&AString);
686 if (HIWORD((ULONG)Cs->lpszClass) != 0)
687 {
688 RtlInitAnsiString(&AString, Cs->lpszClass);
689 RtlFreeAnsiString(&AString);
690 }
691 RtlFreeHeap(GetProcessHeap(), 0, Cs);
692 break;
693 }
694 }
695
696 return TRUE;
697 }
698
699
700 static BOOL FASTCALL
701 MsgiUnicodeToAnsiReply(LPMSG AnsiMsg, LPMSG UnicodeMsg, LRESULT *Result)
702 {
703 switch (UnicodeMsg->message)
704 {
705 case WM_GETTEXT:
706 case WM_ASKCBFORMATNAME:
707 {
708 LPSTR Buffer = (LPSTR) AnsiMsg->lParam;
709 LPWSTR UBuffer = (LPWSTR) UnicodeMsg->lParam;
710 if (0 < AnsiMsg->wParam &&
711 ! MultiByteToWideChar(CP_ACP, 0, Buffer, -1, UBuffer, UnicodeMsg->wParam))
712 {
713 UBuffer[UnicodeMsg->wParam - 1] = L'\0';
714 }
715 break;
716 }
717 }
718
719 MsgiUnicodeToAnsiCleanup(AnsiMsg, UnicodeMsg);
720
721 return TRUE;
722 }
723
724 typedef struct tagMSGCONVERSION
725 {
726 BOOL InUse;
727 BOOL Ansi;
728 MSG KMMsg;
729 MSG UnicodeMsg;
730 MSG AnsiMsg;
731 PMSG FinalMsg;
732 ULONG LParamSize;
733 } MSGCONVERSION, *PMSGCONVERSION;
734
735 static PMSGCONVERSION MsgConversions = NULL;
736 static unsigned MsgConversionNumAlloc = 0;
737 static unsigned MsgConversionNumUsed = 0;
738 static CRITICAL_SECTION MsgConversionCrst;
739
740 static BOOL FASTCALL
741 MsgConversionAdd(PMSGCONVERSION Conversion)
742 {
743 unsigned i;
744
745 EnterCriticalSection(&MsgConversionCrst);
746
747 if (MsgConversionNumUsed == MsgConversionNumAlloc)
748 {
749 #define GROWBY 4
750 PMSGCONVERSION New;
751 if (NULL != MsgConversions)
752 {
753 New = HeapReAlloc(GetProcessHeap(), 0, MsgConversions,
754 (MsgConversionNumAlloc + GROWBY) * sizeof(MSGCONVERSION));
755 }
756 else
757 {
758 New = HeapAlloc(GetProcessHeap(), 0,
759 (MsgConversionNumAlloc + GROWBY) * sizeof(MSGCONVERSION));
760 }
761
762 if (NULL == New)
763 {
764 LeaveCriticalSection(&MsgConversionCrst);
765 return FALSE;
766 }
767 MsgConversions = New;
768 /* zero out newly allocated part */
769 memset(MsgConversions + MsgConversionNumAlloc, 0, GROWBY * sizeof(MSGCONVERSION));
770 MsgConversionNumAlloc += GROWBY;
771 #undef GROWBY
772 }
773
774 for (i = 0; i < MsgConversionNumAlloc; i++)
775 {
776 if (! MsgConversions[i].InUse)
777 {
778 MsgConversions[i] = *Conversion;
779 MsgConversions[i].InUse = TRUE;
780 MsgConversionNumUsed++;
781 break;
782 }
783 }
784 LeaveCriticalSection(&MsgConversionCrst);
785
786 return TRUE;
787 }
788
789 static void FASTCALL
790 MsgConversionCleanup(CONST MSG *Msg, BOOL Ansi, BOOL CheckMsgContents, LRESULT *Result)
791 {
792 BOOL Found;
793 PMSGCONVERSION Conversion;
794 LRESULT Dummy;
795
796 EnterCriticalSection(&MsgConversionCrst);
797 for (Conversion = MsgConversions;
798 Conversion < MsgConversions + MsgConversionNumAlloc;
799 Conversion++)
800 {
801 if (Conversion->InUse &&
802 ((Ansi && Conversion->Ansi) ||
803 (! Ansi && ! Conversion->Ansi)))
804 {
805 Found = (Conversion->FinalMsg == Msg);
806 if (! Found && CheckMsgContents)
807 {
808 if (Ansi)
809 {
810 Found = (0 == memcmp(Msg, &Conversion->AnsiMsg, sizeof(MSG)));
811 }
812 else
813 {
814 Found = (0 == memcmp(Msg, &Conversion->UnicodeMsg, sizeof(MSG)));
815 }
816 }
817 if (Found)
818 {
819 if (Ansi)
820 {
821 MsgiUnicodeToAnsiReply(&Conversion->AnsiMsg, &Conversion->UnicodeMsg,
822 NULL == Result ? &Dummy : Result);
823 }
824 MsgiKMToUMReply(&Conversion->KMMsg, &Conversion->UnicodeMsg,
825 NULL == Result ? &Dummy : Result);
826 if (0 != Conversion->LParamSize)
827 {
828 NtFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &Conversion->KMMsg.lParam,
829 &Conversion->LParamSize, MEM_DECOMMIT);
830 }
831 Conversion->InUse = FALSE;
832 MsgConversionNumUsed--;
833 }
834 }
835 }
836 LeaveCriticalSection(&MsgConversionCrst);
837 }
838
839 /*
840 * @implemented
841 */
842 LPARAM
843 STDCALL
844 GetMessageExtraInfo(VOID)
845 {
846 return (LPARAM)NtUserCallNoParam(NOPARAM_ROUTINE_GETMESSAGEEXTRAINFO);
847 }
848
849
850 /*
851 * @implemented
852 */
853 DWORD
854 STDCALL
855 GetMessagePos(VOID)
856 {
857 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
858 return(MAKELONG(ThreadData->LastMessage.pt.x, ThreadData->LastMessage.pt.y));
859 }
860
861
862 /*
863 * @implemented
864 */
865 LONG STDCALL
866 GetMessageTime(VOID)
867 {
868 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
869 return(ThreadData->LastMessage.time);
870 }
871
872
873 /*
874 * @unimplemented
875 */
876 BOOL
877 STDCALL
878 InSendMessage(VOID)
879 {
880 static DWORD ShowNotImplemented = TRUE;
881 if (ShowNotImplemented)
882 {
883 DbgPrint("InSendMessage is unimplemented\n");
884 ShowNotImplemented = FALSE;
885 }
886 /* return(NtUserGetThreadState(THREADSTATE_INSENDMESSAGE) != ISMEX_NOSEND); */
887 return FALSE;
888 }
889
890
891 /*
892 * @unimplemented
893 */
894 DWORD
895 STDCALL
896 InSendMessageEx(
897 LPVOID lpReserved)
898 {
899 /* return NtUserGetThreadState(THREADSTATE_INSENDMESSAGE); */
900 UNIMPLEMENTED;
901 return 0;
902 }
903
904
905 /*
906 * @unimplemented
907 */
908 BOOL
909 STDCALL
910 ReplyMessage(
911 LRESULT lResult)
912 {
913 UNIMPLEMENTED;
914 return FALSE;
915 }
916
917
918 /*
919 * @implemented
920 */
921 LPARAM
922 STDCALL
923 SetMessageExtraInfo(
924 LPARAM lParam)
925 {
926 return NtUserSetMessageExtraInfo(lParam);
927 }
928
929 LRESULT FASTCALL
930 IntCallWindowProcW(BOOL IsAnsiProc,
931 WNDPROC WndProc,
932 HWND hWnd,
933 UINT Msg,
934 WPARAM wParam,
935 LPARAM lParam)
936 {
937 MSG AnsiMsg;
938 MSG UnicodeMsg;
939 LRESULT Result;
940
941 if (IsAnsiProc)
942 {
943 UnicodeMsg.hwnd = hWnd;
944 UnicodeMsg.message = Msg;
945 UnicodeMsg.wParam = wParam;
946 UnicodeMsg.lParam = lParam;
947 if (! MsgiUnicodeToAnsiMessage(&AnsiMsg, &UnicodeMsg))
948 {
949 return FALSE;
950 }
951 Result = WndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam);
952 if (! MsgiUnicodeToAnsiReply(&AnsiMsg, &UnicodeMsg, &Result))
953 {
954 return FALSE;
955 }
956 return Result;
957 }
958 else
959 {
960 return WndProc(hWnd, Msg, wParam, lParam);
961 }
962 }
963
964 STATIC LRESULT FASTCALL
965 IntCallWindowProcA(BOOL IsAnsiProc,
966 WNDPROC WndProc,
967 HWND hWnd,
968 UINT Msg,
969 WPARAM wParam,
970 LPARAM lParam)
971 {
972 MSG AnsiMsg;
973 MSG UnicodeMsg;
974 LRESULT Result;
975
976 if (IsAnsiProc)
977 {
978 return WndProc(hWnd, Msg, wParam, lParam);
979 }
980 else
981 {
982 AnsiMsg.hwnd = hWnd;
983 AnsiMsg.message = Msg;
984 AnsiMsg.wParam = wParam;
985 AnsiMsg.lParam = lParam;
986 if (! MsgiAnsiToUnicodeMessage(&UnicodeMsg, &AnsiMsg))
987 {
988 return FALSE;
989 }
990 Result = WndProc(UnicodeMsg.hwnd, UnicodeMsg.message,
991 UnicodeMsg.wParam, UnicodeMsg.lParam);
992 if (! MsgiAnsiToUnicodeReply(&UnicodeMsg, &AnsiMsg, &Result))
993 {
994 return FALSE;
995 }
996 return Result;
997 }
998 }
999
1000
1001 /*
1002 * @implemented
1003 */
1004 LRESULT STDCALL
1005 CallWindowProcA(WNDPROC lpPrevWndFunc,
1006 HWND hWnd,
1007 UINT Msg,
1008 WPARAM wParam,
1009 LPARAM lParam)
1010 {
1011 BOOL IsHandle;
1012 WndProcHandle wphData;
1013
1014 if (lpPrevWndFunc == NULL)
1015 lpPrevWndFunc = (WNDPROC)NtUserGetWindowLong(hWnd, GWL_WNDPROC, FALSE);
1016
1017 IsHandle = NtUserDereferenceWndProcHandle(lpPrevWndFunc,&wphData);
1018 if (! IsHandle)
1019 {
1020 return IntCallWindowProcA(TRUE, lpPrevWndFunc, hWnd, Msg, wParam, lParam);
1021 }
1022 else
1023 {
1024 return IntCallWindowProcA(! wphData.IsUnicode, wphData.WindowProc,
1025 hWnd, Msg, wParam, lParam);
1026 }
1027 }
1028
1029
1030 /*
1031 * @implemented
1032 */
1033 LRESULT STDCALL
1034 CallWindowProcW(WNDPROC lpPrevWndFunc,
1035 HWND hWnd,
1036 UINT Msg,
1037 WPARAM wParam,
1038 LPARAM lParam)
1039 {
1040 BOOL IsHandle;
1041 WndProcHandle wphData;
1042
1043 IsHandle = NtUserDereferenceWndProcHandle(lpPrevWndFunc,&wphData);
1044 if (! IsHandle)
1045 {
1046 return IntCallWindowProcW(FALSE, lpPrevWndFunc, hWnd, Msg, wParam, lParam);
1047 }
1048 else
1049 {
1050 return IntCallWindowProcW(! wphData.IsUnicode, wphData.WindowProc,
1051 hWnd, Msg, wParam, lParam);
1052 }
1053 }
1054
1055
1056 /*
1057 * @implemented
1058 */
1059 LRESULT STDCALL
1060 DispatchMessageA(CONST MSG *lpmsg)
1061 {
1062 NTUSERDISPATCHMESSAGEINFO Info;
1063 LRESULT Result;
1064
1065 Info.Ansi = TRUE;
1066 Info.Msg = *lpmsg;
1067 Result = NtUserDispatchMessage(&Info);
1068 if (! Info.HandledByKernel)
1069 {
1070 /* We need to send the message ourselves */
1071 Result = IntCallWindowProcA(Info.Ansi, Info.Proc, Info.Msg.hwnd,
1072 Info.Msg.message, Info.Msg.wParam, Info.Msg.lParam);
1073 }
1074 MsgConversionCleanup(lpmsg, TRUE, TRUE, &Result);
1075
1076 return Result;
1077 }
1078
1079
1080 /*
1081 * @implemented
1082 */
1083 LRESULT STDCALL
1084 DispatchMessageW(CONST MSG *lpmsg)
1085 {
1086 NTUSERDISPATCHMESSAGEINFO Info;
1087 LRESULT Result;
1088
1089 Info.Ansi = FALSE;
1090 Info.Msg = *lpmsg;
1091 Result = NtUserDispatchMessage(&Info);
1092 if (! Info.HandledByKernel)
1093 {
1094 /* We need to send the message ourselves */
1095 Result = IntCallWindowProcW(Info.Ansi, Info.Proc, Info.Msg.hwnd,
1096 Info.Msg.message, Info.Msg.wParam, Info.Msg.lParam);
1097 }
1098 MsgConversionCleanup(lpmsg, FALSE, TRUE, &Result);
1099
1100 return Result;
1101 }
1102
1103
1104 /*
1105 * @implemented
1106 */
1107 BOOL STDCALL
1108 GetMessageA(LPMSG lpMsg,
1109 HWND hWnd,
1110 UINT wMsgFilterMin,
1111 UINT wMsgFilterMax)
1112 {
1113 BOOL Res;
1114 MSGCONVERSION Conversion;
1115 NTUSERGETMESSAGEINFO Info;
1116 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
1117
1118 MsgConversionCleanup(lpMsg, TRUE, FALSE, NULL);
1119 Res = NtUserGetMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax);
1120 if (-1 == (int) Res)
1121 {
1122 return Res;
1123 }
1124 Conversion.LParamSize = Info.LParamSize;
1125 Conversion.KMMsg = Info.Msg;
1126
1127 if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
1128 {
1129 return (BOOL) -1;
1130 }
1131 if (! MsgiUnicodeToAnsiMessage(&Conversion.AnsiMsg, &Conversion.UnicodeMsg))
1132 {
1133 MsgiKMToUMCleanup(&Info.Msg, &Conversion.UnicodeMsg);
1134 return (BOOL) -1;
1135 }
1136 *lpMsg = Conversion.AnsiMsg;
1137 Conversion.Ansi = TRUE;
1138 Conversion.FinalMsg = lpMsg;
1139 MsgConversionAdd(&Conversion);
1140 if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
1141 {
1142 ThreadData->LastMessage = Info.Msg;
1143 }
1144
1145 return Res;
1146 }
1147
1148
1149 /*
1150 * @implemented
1151 */
1152 BOOL STDCALL
1153 GetMessageW(LPMSG lpMsg,
1154 HWND hWnd,
1155 UINT wMsgFilterMin,
1156 UINT wMsgFilterMax)
1157 {
1158 BOOL Res;
1159 MSGCONVERSION Conversion;
1160 NTUSERGETMESSAGEINFO Info;
1161 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
1162
1163 MsgConversionCleanup(lpMsg, FALSE, FALSE, NULL);
1164 Res = NtUserGetMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax);
1165 if (-1 == (int) Res)
1166 {
1167 return Res;
1168 }
1169 Conversion.LParamSize = Info.LParamSize;
1170 Conversion.KMMsg = Info.Msg;
1171
1172 if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
1173 {
1174 return (BOOL) -1;
1175 }
1176 *lpMsg = Conversion.UnicodeMsg;
1177 Conversion.Ansi = FALSE;
1178 Conversion.FinalMsg = lpMsg;
1179 MsgConversionAdd(&Conversion);
1180 if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
1181 {
1182 ThreadData->LastMessage = Info.Msg;
1183 }
1184
1185 return Res;
1186 }
1187
1188
1189 /*
1190 * @implemented
1191 */
1192 BOOL STDCALL
1193 PeekMessageA(LPMSG lpMsg,
1194 HWND hWnd,
1195 UINT wMsgFilterMin,
1196 UINT wMsgFilterMax,
1197 UINT wRemoveMsg)
1198 {
1199 BOOL Res;
1200 MSGCONVERSION Conversion;
1201 NTUSERGETMESSAGEINFO Info;
1202 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
1203
1204 MsgConversionCleanup(lpMsg, TRUE, FALSE, NULL);
1205 Res = NtUserPeekMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
1206 if (-1 == (int) Res || ! Res)
1207 {
1208 return Res;
1209 }
1210 Conversion.LParamSize = Info.LParamSize;
1211 Conversion.KMMsg = Info.Msg;
1212
1213 if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
1214 {
1215 return (BOOL) -1;
1216 }
1217 if (! MsgiUnicodeToAnsiMessage(&Conversion.AnsiMsg, &Conversion.UnicodeMsg))
1218 {
1219 MsgiKMToUMCleanup(&Info.Msg, &Conversion.UnicodeMsg);
1220 return (BOOL) -1;
1221 }
1222 *lpMsg = Conversion.AnsiMsg;
1223 Conversion.Ansi = TRUE;
1224 Conversion.FinalMsg = lpMsg;
1225 MsgConversionAdd(&Conversion);
1226 if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
1227 {
1228 ThreadData->LastMessage = Info.Msg;
1229 }
1230
1231 return Res;
1232 }
1233
1234
1235 /*
1236 * @implemented
1237 */
1238 BOOL
1239 STDCALL
1240 PeekMessageW(
1241 LPMSG lpMsg,
1242 HWND hWnd,
1243 UINT wMsgFilterMin,
1244 UINT wMsgFilterMax,
1245 UINT wRemoveMsg)
1246 {
1247 BOOL Res;
1248 MSGCONVERSION Conversion;
1249 NTUSERGETMESSAGEINFO Info;
1250 PUSER32_THREAD_DATA ThreadData = User32GetThreadData();
1251
1252 MsgConversionCleanup(lpMsg, FALSE, FALSE, NULL);
1253 Res = NtUserPeekMessage(&Info, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
1254 if (-1 == (int) Res || ! Res)
1255 {
1256 return Res;
1257 }
1258 Conversion.LParamSize = Info.LParamSize;
1259 Conversion.KMMsg = Info.Msg;
1260
1261 if (! MsgiKMToUMMessage(&Conversion.KMMsg, &Conversion.UnicodeMsg))
1262 {
1263 return (BOOL) -1;
1264 }
1265 *lpMsg = Conversion.UnicodeMsg;
1266 Conversion.Ansi = FALSE;
1267 Conversion.FinalMsg = lpMsg;
1268 MsgConversionAdd(&Conversion);
1269 if (Res && lpMsg->message != WM_PAINT && lpMsg->message != WM_QUIT)
1270 {
1271 ThreadData->LastMessage = Info.Msg;
1272 }
1273
1274 return Res;
1275 }
1276
1277
1278 /*
1279 * @implemented
1280 */
1281 BOOL
1282 STDCALL
1283 PostMessageA(
1284 HWND Wnd,
1285 UINT Msg,
1286 WPARAM wParam,
1287 LPARAM lParam)
1288 {
1289 MSG AnsiMsg, UcMsg;
1290 MSG KMMsg;
1291 LRESULT Result;
1292
1293 AnsiMsg.hwnd = Wnd;
1294 AnsiMsg.message = Msg;
1295 AnsiMsg.wParam = wParam;
1296 AnsiMsg.lParam = lParam;
1297 if (! MsgiAnsiToUnicodeMessage(&UcMsg, &AnsiMsg))
1298 {
1299 return FALSE;
1300 }
1301
1302 if (! MsgiUMToKMMessage(&UcMsg, &KMMsg, TRUE))
1303 {
1304 MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
1305 return FALSE;
1306 }
1307 Result = NtUserPostMessage(KMMsg.hwnd, KMMsg.message,
1308 KMMsg.wParam, KMMsg.lParam);
1309 MsgiUMToKMCleanup(&UcMsg, &KMMsg);
1310 MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
1311
1312 return Result;
1313 }
1314
1315
1316 /*
1317 * @implemented
1318 */
1319 BOOL
1320 STDCALL
1321 PostMessageW(
1322 HWND Wnd,
1323 UINT Msg,
1324 WPARAM wParam,
1325 LPARAM lParam)
1326 {
1327 MSG UMMsg, KMMsg;
1328 LRESULT Result;
1329
1330 UMMsg.hwnd = Wnd;
1331 UMMsg.message = Msg;
1332 UMMsg.wParam = wParam;
1333 UMMsg.lParam = lParam;
1334 if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, TRUE))
1335 {
1336 return FALSE;
1337 }
1338 Result = NtUserPostMessage(KMMsg.hwnd, KMMsg.message,
1339 KMMsg.wParam, KMMsg.lParam);
1340 MsgiUMToKMCleanup(&UMMsg, &KMMsg);
1341
1342 return Result;
1343 }
1344
1345
1346 /*
1347 * @implemented
1348 */
1349 VOID
1350 STDCALL
1351 PostQuitMessage(
1352 int nExitCode)
1353 {
1354 (void) NtUserPostMessage(NULL, WM_QUIT, nExitCode, 0);
1355 }
1356
1357
1358 /*
1359 * @implemented
1360 */
1361 BOOL
1362 STDCALL
1363 PostThreadMessageA(
1364 DWORD idThread,
1365 UINT Msg,
1366 WPARAM wParam,
1367 LPARAM lParam)
1368 {
1369 return NtUserPostThreadMessage(idThread, Msg, wParam, lParam);
1370 }
1371
1372
1373 /*
1374 * @implemented
1375 */
1376 BOOL
1377 STDCALL
1378 PostThreadMessageW(
1379 DWORD idThread,
1380 UINT Msg,
1381 WPARAM wParam,
1382 LPARAM lParam)
1383 {
1384 return NtUserPostThreadMessage(idThread, Msg, wParam, lParam);
1385 }
1386
1387
1388 /*
1389 * @implemented
1390 */
1391 LRESULT STDCALL
1392 SendMessageW(HWND Wnd,
1393 UINT Msg,
1394 WPARAM wParam,
1395 LPARAM lParam)
1396 {
1397 MSG UMMsg, KMMsg;
1398 NTUSERSENDMESSAGEINFO Info;
1399 LRESULT Result;
1400
1401 UMMsg.hwnd = Wnd;
1402 UMMsg.message = Msg;
1403 UMMsg.wParam = wParam;
1404 UMMsg.lParam = lParam;
1405 if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE))
1406 {
1407 return FALSE;
1408 }
1409 Info.Ansi = FALSE;
1410 Result = NtUserSendMessage(KMMsg.hwnd, KMMsg.message,
1411 KMMsg.wParam, KMMsg.lParam, &Info);
1412 if (! Info.HandledByKernel)
1413 {
1414 MsgiUMToKMCleanup(&UMMsg, &KMMsg);
1415 /* We need to send the message ourselves */
1416 Result = IntCallWindowProcW(Info.Ansi, Info.Proc, UMMsg.hwnd, UMMsg.message,
1417 UMMsg.wParam, UMMsg.lParam);
1418 }
1419 else if (! MsgiUMToKMReply(&UMMsg, &KMMsg, &Result))
1420 {
1421 return FALSE;
1422 }
1423
1424 return Result;
1425 }
1426
1427
1428 /*
1429 * @implemented
1430 */
1431 LRESULT STDCALL
1432 SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1433 {
1434 MSG AnsiMsg, UcMsg;
1435 MSG KMMsg;
1436 LRESULT Result;
1437 NTUSERSENDMESSAGEINFO Info;
1438
1439 AnsiMsg.hwnd = Wnd;
1440 AnsiMsg.message = Msg;
1441 AnsiMsg.wParam = wParam;
1442 AnsiMsg.lParam = lParam;
1443 if (! MsgiAnsiToUnicodeMessage(&UcMsg, &AnsiMsg))
1444 {
1445 return FALSE;
1446 }
1447
1448 if (! MsgiUMToKMMessage(&UcMsg, &KMMsg, FALSE))
1449 {
1450 MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
1451 return FALSE;
1452 }
1453 Info.Ansi = TRUE;
1454 Result = NtUserSendMessage(KMMsg.hwnd, KMMsg.message,
1455 KMMsg.wParam, KMMsg.lParam, &Info);
1456 if (! Info.HandledByKernel)
1457 {
1458 /* We need to send the message ourselves */
1459 if (Info.Ansi)
1460 {
1461 /* Ansi message and Ansi window proc, that's easy. Clean up
1462 the Unicode message though */
1463 MsgiUMToKMCleanup(&UcMsg, &KMMsg);
1464 MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
1465 Result = IntCallWindowProcA(Info.Ansi, Info.Proc, Wnd, Msg, wParam, lParam);
1466 }
1467 else
1468 {
1469 /* Unicode winproc. Although we started out with an Ansi message we
1470 already converted it to Unicode for the kernel call. Reuse that
1471 message to avoid another conversion */
1472 Result = IntCallWindowProcW(Info.Ansi, Info.Proc, UcMsg.hwnd,
1473 UcMsg.message, UcMsg.wParam, UcMsg.lParam);
1474 if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
1475 {
1476 return FALSE;
1477 }
1478 }
1479 }
1480 /* Message sent by kernel. Convert back to Ansi */
1481 else if (! MsgiUMToKMReply(&UcMsg, &KMMsg, &Result) ||
1482 ! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
1483 {
1484 return FALSE;
1485 }
1486
1487 return Result;
1488 }
1489
1490
1491 /*
1492 * @implemented
1493 */
1494 BOOL
1495 STDCALL
1496 SendMessageCallbackA(
1497 HWND hWnd,
1498 UINT Msg,
1499 WPARAM wParam,
1500 LPARAM lParam,
1501 SENDASYNCPROC lpCallBack,
1502 ULONG_PTR dwData)
1503 {
1504 return NtUserSendMessageCallback(
1505 hWnd,
1506 Msg,
1507 wParam,
1508 lParam,
1509 lpCallBack,
1510 dwData);
1511 }
1512
1513
1514 /*
1515 * @implemented
1516 */
1517 BOOL
1518 STDCALL
1519 SendMessageCallbackW(
1520 HWND hWnd,
1521 UINT Msg,
1522 WPARAM wParam,
1523 LPARAM lParam,
1524 SENDASYNCPROC lpCallBack,
1525 ULONG_PTR dwData)
1526 {
1527 return NtUserSendMessageCallback(
1528 hWnd,
1529 Msg,
1530 wParam,
1531 lParam,
1532 lpCallBack,
1533 dwData);
1534 }
1535
1536
1537 /*
1538 * @implemented
1539 */
1540 LRESULT
1541 STDCALL
1542 SendMessageTimeoutA(
1543 HWND hWnd,
1544 UINT Msg,
1545 WPARAM wParam,
1546 LPARAM lParam,
1547 UINT fuFlags,
1548 UINT uTimeout,
1549 PDWORD_PTR lpdwResult)
1550 {
1551 MSG AnsiMsg;
1552 MSG UcMsg;
1553 LRESULT Result;
1554 NTUSERSENDMESSAGEINFO Info;
1555
1556 AnsiMsg.hwnd = hWnd;
1557 AnsiMsg.message = Msg;
1558 AnsiMsg.wParam = wParam;
1559 AnsiMsg.lParam = lParam;
1560 if (! MsgiAnsiToUnicodeMessage(&UcMsg, &AnsiMsg))
1561 {
1562 return FALSE;
1563 }
1564
1565 Info.Ansi = TRUE;
1566 Result = NtUserSendMessageTimeout(UcMsg.hwnd, UcMsg.message,
1567 UcMsg.wParam, UcMsg.lParam,
1568 fuFlags, uTimeout, (ULONG_PTR*)lpdwResult, &Info);
1569 if(!Result)
1570 {
1571 return FALSE;
1572 }
1573 if (! Info.HandledByKernel)
1574 {
1575 /* We need to send the message ourselves */
1576 if (Info.Ansi)
1577 {
1578 /* Ansi message and Ansi window proc, that's easy. Clean up
1579 the Unicode message though */
1580 MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg);
1581 Result = IntCallWindowProcA(Info.Ansi, Info.Proc, hWnd, Msg, wParam, lParam);
1582 }
1583 else
1584 {
1585 /* Unicode winproc. Although we started out with an Ansi message we
1586 already converted it to Unicode for the kernel call. Reuse that
1587 message to avoid another conversion */
1588 Result = IntCallWindowProcW(Info.Ansi, Info.Proc, UcMsg.hwnd,
1589 UcMsg.message, UcMsg.wParam, UcMsg.lParam);
1590 if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
1591 {
1592 return FALSE;
1593 }
1594 }
1595 if(lpdwResult)
1596 *lpdwResult = Result;
1597 Result = TRUE;
1598 }
1599 else
1600 {
1601 /* Message sent by kernel. Convert back to Ansi */
1602 if (! MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result))
1603 {
1604 return FALSE;
1605 }
1606 }
1607
1608 return Result;
1609 }
1610
1611
1612 /*
1613 * @implemented
1614 */
1615 LRESULT
1616 STDCALL
1617 SendMessageTimeoutW(
1618 HWND hWnd,
1619 UINT Msg,
1620 WPARAM wParam,
1621 LPARAM lParam,
1622 UINT fuFlags,
1623 UINT uTimeout,
1624 PDWORD_PTR lpdwResult)
1625 {
1626 NTUSERSENDMESSAGEINFO Info;
1627 LRESULT Result;
1628
1629 Info.Ansi = FALSE;
1630 Result = NtUserSendMessageTimeout(hWnd, Msg, wParam, lParam, fuFlags, uTimeout,
1631 lpdwResult, &Info);
1632 if (! Info.HandledByKernel)
1633 {
1634 /* We need to send the message ourselves */
1635 Result = IntCallWindowProcW(Info.Ansi, Info.Proc, hWnd, Msg, wParam, lParam);
1636 if(lpdwResult)
1637 *lpdwResult = Result;
1638 return TRUE;
1639 }
1640
1641 return Result;
1642 }
1643
1644
1645 /*
1646 * @unimplemented
1647 */
1648 BOOL
1649 STDCALL
1650 SendNotifyMessageA(
1651 HWND hWnd,
1652 UINT Msg,
1653 WPARAM wParam,
1654 LPARAM lParam)
1655 {
1656 UNIMPLEMENTED;
1657 return FALSE;
1658 }
1659
1660
1661 /*
1662 * @unimplemented
1663 */
1664 BOOL
1665 STDCALL
1666 SendNotifyMessageW(
1667 HWND hWnd,
1668 UINT Msg,
1669 WPARAM wParam,
1670 LPARAM lParam)
1671 {
1672 UNIMPLEMENTED;
1673 return FALSE;
1674 }
1675
1676
1677 /*
1678 * @implemented
1679 */
1680 BOOL STDCALL
1681 TranslateMessageEx(CONST MSG *lpMsg, DWORD unk)
1682 {
1683 return(NtUserTranslateMessage((LPMSG)lpMsg, (HKL)unk));
1684 }
1685
1686
1687 /*
1688 * @implemented
1689 */
1690 BOOL STDCALL
1691 TranslateMessage(CONST MSG *lpMsg)
1692 {
1693 return(TranslateMessageEx((LPMSG)lpMsg, 0));
1694 }
1695
1696
1697 /*
1698 * @implemented
1699 */
1700 BOOL
1701 STDCALL
1702 WaitMessage(VOID)
1703 {
1704 return NtUserWaitMessage();
1705 }
1706
1707
1708 /*
1709 * @implemented
1710 */
1711 UINT STDCALL
1712 RegisterWindowMessageA(LPCSTR lpString)
1713 {
1714 UNICODE_STRING String;
1715 BOOLEAN Result;
1716 UINT Atom;
1717
1718 Result = RtlCreateUnicodeStringFromAsciiz(&String, (PCSZ)lpString);
1719 if (!Result)
1720 {
1721 return(0);
1722 }
1723 Atom = NtUserRegisterWindowMessage(&String);
1724 RtlFreeUnicodeString(&String);
1725 return(Atom);
1726 }
1727
1728
1729 /*
1730 * @implemented
1731 */
1732 UINT STDCALL
1733 RegisterWindowMessageW(LPCWSTR lpString)
1734 {
1735 UNICODE_STRING String;
1736
1737 RtlInitUnicodeString(&String, lpString);
1738 return(NtUserRegisterWindowMessage(&String));
1739 }
1740
1741 /*
1742 * @implemented
1743 */
1744 HWND STDCALL
1745 SetCapture(HWND hWnd)
1746 {
1747 return(NtUserSetCapture(hWnd));
1748 }
1749
1750 /*
1751 * @implemented
1752 */
1753 HWND STDCALL
1754 GetCapture(VOID)
1755 {
1756 return(NtUserGetCapture());
1757 }
1758
1759 /*
1760 * @implemented
1761 */
1762 BOOL STDCALL
1763 ReleaseCapture(VOID)
1764 {
1765 NtUserSetCapture(NULL);
1766 return(TRUE);
1767 }
1768
1769
1770 /*
1771 * @unimplemented
1772 */
1773 DWORD
1774 STDCALL
1775 RealGetQueueStatus(UINT flags)
1776 {
1777 DWORD ret;
1778 WORD changed_bits, wake_bits;
1779
1780 #if 0 /* wine stuff. don't know what it does... */
1781
1782 /* check for pending X events */
1783 if (USER_Driver.pMsgWaitForMultipleObjectsEx)
1784 USER_Driver.pMsgWaitForMultipleObjectsEx( 0, NULL, 0, 0, 0 );
1785 #endif
1786
1787 ret = NtUserGetQueueStatus(TRUE /*ClearChanges*/);
1788
1789 changed_bits = LOWORD(ret);
1790 wake_bits = HIWORD(ret);
1791
1792 return MAKELONG(changed_bits & flags, wake_bits & flags);
1793 }
1794
1795
1796 /*
1797 * @unimplemented
1798 */
1799 BOOL STDCALL GetInputState(VOID)
1800 {
1801 DWORD ret;
1802 WORD wake_bits;
1803
1804 #if 0 /* wine stuff. don't know what it does... */
1805
1806 /* check for pending X events */
1807 if (USER_Driver.pMsgWaitForMultipleObjectsEx)
1808 USER_Driver.pMsgWaitForMultipleObjectsEx( 0, NULL, 0, 0, 0 );
1809 #endif
1810
1811 ret = NtUserGetQueueStatus(FALSE /*ClearChanges*/);
1812
1813 wake_bits = HIWORD(ret);
1814
1815 return wake_bits & (QS_KEY | QS_MOUSEBUTTON);
1816 }
1817
1818
1819 NTSTATUS STDCALL
1820 User32CallWindowProcFromKernel(PVOID Arguments, ULONG ArgumentLength)
1821 {
1822 PWINDOWPROC_CALLBACK_ARGUMENTS CallbackArgs;
1823 MSG KMMsg, UMMsg;
1824
1825 /* Make sure we don't try to access mem beyond what we were given */
1826 if (ArgumentLength < sizeof(WINDOWPROC_CALLBACK_ARGUMENTS))
1827 {
1828 return STATUS_INFO_LENGTH_MISMATCH;
1829 }
1830
1831 CallbackArgs = (PWINDOWPROC_CALLBACK_ARGUMENTS) Arguments;
1832 KMMsg.hwnd = CallbackArgs->Wnd;
1833 KMMsg.message = CallbackArgs->Msg;
1834 KMMsg.wParam = CallbackArgs->wParam;
1835 /* Check if lParam is really a pointer and adjust it if it is */
1836 if (0 <= CallbackArgs->lParamBufferSize)
1837 {
1838 if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS)
1839 + CallbackArgs->lParamBufferSize)
1840 {
1841 return STATUS_INFO_LENGTH_MISMATCH;
1842 }
1843 KMMsg.lParam = (LPARAM) ((char *) CallbackArgs + sizeof(WINDOWPROC_CALLBACK_ARGUMENTS));
1844 }
1845 else
1846 {
1847 if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS))
1848 {
1849 return STATUS_INFO_LENGTH_MISMATCH;
1850 }
1851 KMMsg.lParam = CallbackArgs->lParam;
1852 }
1853
1854 if (WM_NCCALCSIZE == CallbackArgs->Msg && CallbackArgs->wParam)
1855 {
1856 NCCALCSIZE_PARAMS *Params = (NCCALCSIZE_PARAMS *) KMMsg.lParam;
1857 Params->lppos = (PWINDOWPOS) (Params + 1);
1858 }
1859
1860 if (! MsgiKMToUMMessage(&KMMsg, &UMMsg))
1861 {
1862 }
1863
1864 CallbackArgs->Result = IntCallWindowProcW(CallbackArgs->IsAnsiProc, CallbackArgs->Proc,
1865 UMMsg.hwnd, UMMsg.message,
1866 UMMsg.wParam, UMMsg.lParam);
1867
1868 if (! MsgiKMToUMReply(&KMMsg, &UMMsg, &CallbackArgs->Result))
1869 {
1870 }
1871
1872 return ZwCallbackReturn(CallbackArgs, ArgumentLength, STATUS_SUCCESS);
1873 }
1874
1875 /*
1876 * @implemented
1877 */
1878 BOOL STDCALL SetMessageQueue(int cMessagesMax)
1879 {
1880 /* Function does nothing on 32 bit windows */
1881 return TRUE;
1882 }
1883 typedef DWORD (WINAPI * RealGetQueueStatusProc)(UINT flags);
1884 typedef DWORD (WINAPI * RealMsgWaitForMultipleObjectsExProc)(DWORD nCount, CONST HANDLE *lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
1885
1886 typedef struct _USER_MESSAGE_PUMP_ADDRESSES {
1887 DWORD cbSize;
1888 //NtUserRealInternalGetMessageProc NtUserRealInternalGetMessage;
1889 //NtUserRealWaitMessageExProc NtUserRealWaitMessageEx;
1890 RealGetQueueStatusProc RealGetQueueStatus;
1891 RealMsgWaitForMultipleObjectsExProc RealMsgWaitForMultipleObjectsEx;
1892 } USER_MESSAGE_PUMP_ADDRESSES, * PUSER_MESSAGE_PUMP_ADDRESSES;
1893
1894 DWORD
1895 STDCALL
1896 RealMsgWaitForMultipleObjectsEx(
1897 DWORD nCount,
1898 CONST HANDLE *pHandles,
1899 DWORD dwMilliseconds,
1900 DWORD dwWakeMask,
1901 DWORD dwFlags);
1902
1903 typedef BOOL (WINAPI * MESSAGEPUMPHOOKPROC)(BOOL Unregistering,PUSER_MESSAGE_PUMP_ADDRESSES MessagePumpAddresses);
1904
1905 CRITICAL_SECTION gcsMPH;
1906 MESSAGEPUMPHOOKPROC gpfnInitMPH;
1907 DWORD gcLoadMPH = 0;
1908 USER_MESSAGE_PUMP_ADDRESSES gmph = {sizeof(USER_MESSAGE_PUMP_ADDRESSES),
1909 //NtUserRealInternalGetMessage,
1910 //NtUserRealInternalWaitMessageEx,
1911 RealGetQueueStatus,
1912 RealMsgWaitForMultipleObjectsEx
1913 };
1914
1915 DWORD gfMessagePumpHook = 0;
1916
1917 BOOL WINAPI IsInsideMessagePumpHook()
1918 {
1919 if(!gfMessagePumpHook)
1920 return FALSE;
1921
1922 /* This code checks if we're inside SendMessage. */
1923 #if 0
1924 /* Since our TEB doesnt match that of real windows, testing this value is useless until we know what it does
1925 PUCHAR NtTeb = (PUCHAR)NtCurrentTeb();
1926
1927 if(!*(PLONG*)&NtTeb[0x708])
1928 return FALSE;
1929
1930 if(**(PLONG*)&NtTeb[0x708] <= 0)
1931 return FALSE;*/
1932 #endif
1933
1934 return TRUE;
1935 }
1936
1937 void WINAPI ResetMessagePumpHook(PUSER_MESSAGE_PUMP_ADDRESSES Addresses)
1938 {
1939 Addresses->cbSize = sizeof(USER_MESSAGE_PUMP_ADDRESSES);
1940 //Addresses->NtUserRealInternalGetMessage = (NtUserRealInternalGetMessageProc)NtUserRealInternalGetMessage;
1941 //Addresses->NtUserRealWaitMessageEx = (NtUserRealWaitMessageExProc)NtUserRealInternalWaitMessageEx;
1942 Addresses->RealGetQueueStatus = RealGetQueueStatus;
1943 Addresses->RealMsgWaitForMultipleObjectsEx = RealMsgWaitForMultipleObjectsEx;
1944 }
1945
1946 BOOL WINAPI RegisterMessagePumpHook(MESSAGEPUMPHOOKPROC Hook)
1947 {
1948 EnterCriticalSection(&gcsMPH);
1949 if(!Hook) {
1950 SetLastError(ERROR_INVALID_PARAMETER);
1951 LeaveCriticalSection(&gcsMPH);
1952 return FALSE;
1953 }
1954 if(!gcLoadMPH) {
1955 USER_MESSAGE_PUMP_ADDRESSES Addresses;
1956 gpfnInitMPH = Hook;
1957 ResetMessagePumpHook(&Addresses);
1958 if(!Hook(FALSE, &Addresses) || !Addresses.cbSize) {
1959 LeaveCriticalSection(&gcsMPH);
1960 return FALSE;
1961 }
1962 memcpy(&gmph, &Addresses, Addresses.cbSize);
1963 } else {
1964 if(gpfnInitMPH != Hook) {
1965 LeaveCriticalSection(&gcsMPH);
1966 return FALSE;
1967 }
1968 }
1969 if(NtUserCallNoParam(NOPARAM_ROUTINE_INIT_MESSAGE_PUMP)) {
1970 LeaveCriticalSection(&gcsMPH);
1971 return FALSE;
1972 }
1973 if (!gcLoadMPH++) {
1974 InterlockedExchange((PLONG)&gfMessagePumpHook, 1);
1975 }
1976 LeaveCriticalSection(&gcsMPH);
1977 return TRUE;
1978 }
1979
1980 BOOL WINAPI UnregisterMessagePumpHook(VOID)
1981 {
1982 EnterCriticalSection(&gcsMPH);
1983 if(gcLoadMPH > 0) {
1984 if(NtUserCallNoParam(NOPARAM_ROUTINE_UNINIT_MESSAGE_PUMP)) {
1985 gcLoadMPH--;
1986 if(!gcLoadMPH) {
1987 InterlockedExchange((PLONG)&gfMessagePumpHook, 0);
1988 gpfnInitMPH(TRUE, NULL);
1989 ResetMessagePumpHook(&gmph);
1990 gpfnInitMPH = 0;
1991 }
1992 LeaveCriticalSection(&gcsMPH);
1993 return TRUE;
1994 }
1995 }
1996 LeaveCriticalSection(&gcsMPH);
1997 return FALSE;
1998 }
1999
2000 DWORD WINAPI GetQueueStatus(UINT flags)
2001 {
2002 return IsInsideMessagePumpHook() ? gmph.RealGetQueueStatus(flags) : RealGetQueueStatus(flags);
2003 }
2004
2005 /**
2006 * @name RealMsgWaitForMultipleObjectsEx
2007 *
2008 * Wait either for either message arrival or for one of the passed events
2009 * to be signalled.
2010 *
2011 * @param nCount
2012 * Number of handles in the pHandles array.
2013 * @param pHandles
2014 * Handles of events to wait for.
2015 * @param dwMilliseconds
2016 * Timeout interval.
2017 * @param dwWakeMask
2018 * Mask specifying on which message events we should wakeup.
2019 * @param dwFlags
2020 * Wait type (see MWMO_* constants).
2021 *
2022 * @implemented
2023 */
2024
2025 DWORD STDCALL
2026 RealMsgWaitForMultipleObjectsEx(
2027 DWORD nCount,
2028 const HANDLE *pHandles,
2029 DWORD dwMilliseconds,
2030 DWORD dwWakeMask,
2031 DWORD dwFlags)
2032 {
2033 LPHANDLE RealHandles;
2034 HANDLE MessageQueueHandle;
2035 DWORD Result;
2036
2037 if (dwFlags & ~(MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE))
2038 {
2039 SetLastError(ERROR_INVALID_PARAMETER);
2040 return WAIT_FAILED;
2041 }
2042
2043 /*
2044 if (dwFlags & MWMO_INPUTAVAILABLE)
2045 {
2046 RealGetQueueStatus(dwWakeMask);
2047 }
2048 */
2049
2050 MessageQueueHandle = NtUserMsqSetWakeMask(dwWakeMask);
2051 if (MessageQueueHandle == NULL)
2052 {
2053 SetLastError(0); /* ? */
2054 return WAIT_FAILED;
2055 }
2056
2057 RealHandles = HeapAlloc(GetProcessHeap(), 0, (nCount + 1) * sizeof(HANDLE));
2058 if (RealHandles == NULL)
2059 {
2060 NtUserMsqClearWakeMask();
2061 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2062 return WAIT_FAILED;
2063 }
2064
2065 RtlCopyMemory(RealHandles, pHandles, nCount * sizeof(HANDLE));
2066 RealHandles[nCount] = MessageQueueHandle;
2067
2068 Result = WaitForMultipleObjectsEx(nCount + 1, RealHandles,
2069 dwFlags & MWMO_WAITALL,
2070 dwMilliseconds, dwFlags & MWMO_ALERTABLE);
2071
2072 HeapFree(GetProcessHeap(), 0, RealHandles);
2073 NtUserMsqClearWakeMask();
2074
2075 return Result;
2076 }
2077
2078 /*
2079 * @implemented
2080 */
2081 DWORD WINAPI
2082 MsgWaitForMultipleObjectsEx(
2083 DWORD nCount,
2084 CONST HANDLE *lpHandles,
2085 DWORD dwMilliseconds,
2086 DWORD dwWakeMask,
2087 DWORD dwFlags)
2088 {
2089 return IsInsideMessagePumpHook() ? gmph.RealMsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds, dwWakeMask, dwFlags) : RealMsgWaitForMultipleObjectsEx(nCount, lpHandles,dwMilliseconds, dwWakeMask, dwFlags);
2090 }
2091
2092 /*
2093 * @implemented
2094 */
2095 DWORD STDCALL
2096 MsgWaitForMultipleObjects(
2097 DWORD nCount,
2098 CONST HANDLE *lpHandles,
2099 BOOL fWaitAll,
2100 DWORD dwMilliseconds,
2101 DWORD dwWakeMask)
2102 {
2103 return MsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds,
2104 dwWakeMask, fWaitAll ? MWMO_WAITALL : 0);
2105 }
2106
2107
2108 BOOL FASTCALL MessageInit()
2109 {
2110 InitializeCriticalSection(&DdeCrst);
2111 InitializeCriticalSection(&MsgConversionCrst);
2112
2113 return TRUE;
2114 }