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