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