- Honour WM_SETREDRAW.
[wine] / dlls / user / tests / msg.c
1 /*
2  * Unit tests for window message handling
3  *
4  * Copyright 1999 Ove Kaaven
5  * Copyright 2003 Dimitrie O. Paun
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <assert.h>
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31
32 #include "wine/test.h"
33
34
35 /*
36 FIXME: add tests for these
37 Window Edge Styles (Win31/Win95/98 look), in order of precedence:
38  WS_EX_DLGMODALFRAME: double border, WS_CAPTION allowed
39  WS_THICKFRAME: thick border
40  WS_DLGFRAME: double border, WS_CAPTION not allowed (but possibly shown anyway)
41  WS_BORDER (default for overlapped windows): single black border
42  none (default for child (and popup?) windows): no border
43 */
44
45 typedef enum { 
46     sent=0x1, posted=0x2, parent=0x4, wparam=0x8, lparam=0x10,
47     defwinproc=0x20
48 } msg_flags_t;
49
50 struct message {
51     UINT message;          /* the WM_* code */
52     msg_flags_t flags;     /* message props */
53     WPARAM wParam;         /* expacted value of wParam */
54     LPARAM lParam;         /* expacted value of lParam */
55 };
56
57 /* CreateWindow (for overlapped window, not initially visible) (16/32) */
58 static struct message WmCreateOverlappedSeq[] = {
59     { WM_GETMINMAXINFO, sent },
60     { WM_NCCREATE, sent },
61     { WM_NCCALCSIZE, sent|wparam, 0 },
62     { WM_CREATE, sent },
63     { 0 }
64 };
65 /* ShowWindow (for overlapped window) (16/32) */
66 static struct message WmShowOverlappedSeq[] = {
67     { WM_SHOWWINDOW, sent|wparam, 1 },
68     { WM_WINDOWPOSCHANGING, sent|wparam, /*FIXME: SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW*/ 0 },
69     /* FIXME: WM_QUERYNEWPALETTE, if in 256-color mode */
70     { WM_WINDOWPOSCHANGING, sent|wparam, /*FIXME: SWP_NOMOVE|SWP_NOSIZE*/ 0 },
71     { WM_ACTIVATEAPP, sent|wparam, 1 },
72     { WM_NCACTIVATE, sent|wparam, 1 },
73     { WM_GETTEXT, sent|defwinproc },
74     { WM_ACTIVATE, sent|wparam, 1 },
75     { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
76     { WM_NCPAINT, sent|wparam, 1 },
77     { WM_GETTEXT, sent|defwinproc },
78     { WM_ERASEBKGND, sent },
79     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW },
80     { WM_SIZE, sent },
81     { WM_MOVE, sent },
82     { 0 }
83 };
84
85 /* DestroyWindow (for overlapped window) (32) */
86 static struct message WmDestroyOverlappedSeq[] = {
87     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
88     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
89     { WM_NCACTIVATE, sent|wparam, 0 },
90     { WM_ACTIVATE, sent|wparam, 0 },
91     { WM_ACTIVATEAPP, sent|wparam, 0 },
92     { WM_KILLFOCUS, sent|wparam, 0 },
93     { WM_DESTROY, sent },
94     { WM_NCDESTROY, sent },
95     { 0 }
96 };
97 /* CreateWindow (for child window, not initially visible) */
98 static struct message WmCreateChildSeq[] = {
99     { WM_NCCREATE, sent }, 
100     /* child is inserted into parent's child list after WM_NCCREATE returns */
101     { WM_NCCALCSIZE, sent|wparam, 0 },
102     { WM_CREATE, sent },
103     { WM_SIZE, sent },
104     { WM_MOVE, sent },
105     { WM_PARENTNOTIFY, sent|parent|wparam, 1 },
106     { 0 }
107 };
108 /* ShowWindow (for child window) */
109 static struct message WmShowChildSeq[] = {
110     { WM_SHOWWINDOW, sent|wparam, 1 },
111     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
112     { WM_ERASEBKGND, sent|parent },
113     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
114     { 0 }
115 };
116 /* DestroyWindow (for child window) */
117 static struct message WmDestroyChildSeq[] = {
118     { WM_PARENTNOTIFY, sent|parent|wparam, 2 },
119     { WM_SHOWWINDOW, sent|wparam, 0 },
120     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
121     { WM_ERASEBKGND, sent|parent },
122     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
123     { WM_DESTROY, sent },
124     { WM_NCDESTROY, sent },
125     { 0 }
126 };
127 /* Moving the mouse in nonclient area */
128 static struct message WmMouseMoveInNonClientAreaSeq[] = { /* FIXME: add */
129     { WM_NCHITTEST, sent },
130     { WM_SETCURSOR, sent },
131     { WM_NCMOUSEMOVE, posted },
132     { 0 }
133 };
134 /* Moving the mouse in client area */
135 static struct message WmMouseMoveInClientAreaSeq[] = { /* FIXME: add */
136     { WM_NCHITTEST, sent },
137     { WM_SETCURSOR, sent },
138     { WM_MOUSEMOVE, posted },
139     { 0 }
140 };
141 /* Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
142 static struct message WmDragTitleBarSeq[] = { /* FIXME: add */
143     { WM_NCLBUTTONDOWN, sent|wparam, HTCAPTION },
144     { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_MOVE+2 },
145     { WM_GETMINMAXINFO, sent|defwinproc },
146     { WM_ENTERSIZEMOVE, sent|defwinproc },
147     { WM_WINDOWPOSCHANGING, sent|defwinproc },
148     { WM_WINDOWPOSCHANGED, sent|defwinproc },
149     { WM_MOVE, sent|defwinproc },
150     { WM_EXITSIZEMOVE, sent|defwinproc },
151     { 0 }
152 };
153 /* Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
154 static struct message WmDragThinkBordersBarSeq[] = { /* FIXME: add */
155     { WM_NCLBUTTONDOWN, sent|wparam, 0xd },
156     { WM_SYSCOMMAND, sent|defwinproc|wparam, 0xf004 },
157     { WM_GETMINMAXINFO, sent|defwinproc },
158     { WM_ENTERSIZEMOVE, sent|defwinproc },
159     { WM_SIZING, sent|defwinproc|wparam, 4}, /* one for each mouse movement */
160     { WM_WINDOWPOSCHANGING, sent|defwinproc },
161     { WM_GETMINMAXINFO, sent|defwinproc },
162     { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
163     { WM_NCPAINT, sent|defwinproc|wparam, 1 },
164     { WM_GETTEXT, sent|defwinproc },
165     { WM_ERASEBKGND, sent|defwinproc },
166     { WM_WINDOWPOSCHANGED, sent|defwinproc },
167     { WM_MOVE, sent|defwinproc },
168     { WM_SIZE, sent|defwinproc },
169     { WM_EXITSIZEMOVE, sent|defwinproc },
170     { 0 }
171 };
172 /* Resizing child window with MoveWindow (32) */
173 static struct message WmResizingChildWithMoveWindowSeq[] = {
174     { WM_WINDOWPOSCHANGING, sent },
175     { WM_NCCALCSIZE, sent|wparam, 1 },
176     { WM_ERASEBKGND, sent },
177     { WM_WINDOWPOSCHANGED, sent },
178     { WM_MOVE, sent|defwinproc },
179     { WM_SIZE, sent|defwinproc },
180     { 0 }
181 };
182 /* Clicking on inactive button */
183 static struct message WmClickInactiveButtonSeq[] = { /* FIXME: add */
184     { WM_NCHITTEST, sent },
185     { WM_PARENTNOTIFY, sent|parent|wparam, WM_LBUTTONDOWN },
186     { WM_MOUSEACTIVATE, sent },
187     { WM_MOUSEACTIVATE, sent|parent|defwinproc },
188     { WM_SETCURSOR, sent },
189     { WM_SETCURSOR, sent|parent|defwinproc },
190     { WM_LBUTTONDOWN, posted },
191     { WM_KILLFOCUS, posted|parent },
192     { WM_SETFOCUS, posted },
193     { WM_CTLCOLORBTN, posted|parent },
194     { BM_SETSTATE, posted },
195     { WM_CTLCOLORBTN, posted|parent },
196     { WM_LBUTTONUP, posted },
197     { BM_SETSTATE, posted },
198     { WM_CTLCOLORBTN, posted|parent },
199     { WM_COMMAND, posted|parent },
200     { 0 }
201 };
202 /* Reparenting a button (16/32) */
203 /* The last child (button) reparented gets topmost for its new parent. */
204 static struct message WmReparentButtonSeq[] = { /* FIXME: add */
205     { WM_SHOWWINDOW, sent|wparam, 0 },
206     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
207     { WM_ERASEBKGND, sent|parent },
208     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
209     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOZORDER },
210     { WM_CHILDACTIVATE, sent },
211     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER },
212     { WM_MOVE, sent|defwinproc },
213     { WM_SHOWWINDOW, sent|wparam, 1 },
214     { 0 }
215 };
216 /* Creation of a modal dialog (32) */
217 static struct message WmCreateModalDialogSeq[] = { /* FIXME: add */
218     { WM_CANCELMODE, sent|parent },
219     { WM_KILLFOCUS, sent|parent },
220     { WM_ENABLE, sent|parent|wparam, 0 },
221     /* (window proc creation messages not tracked yet, because...) */
222     { WM_SETFONT, sent },
223     { WM_INITDIALOG, sent },
224     /* (...the window proc message hook was installed here, IsVisible still FALSE) */
225     { WM_NCACTIVATE, sent|parent|wparam, 0 },
226     { WM_GETTEXT, sent|defwinproc },
227     { WM_ACTIVATE, sent|parent|wparam, 0 },
228     { WM_WINDOWPOSCHANGING, sent },
229     { WM_WINDOWPOSCHANGING, sent|parent },
230     { WM_NCACTIVATE, sent|wparam, 1 },
231     { WM_ACTIVATE, sent|wparam, 1 },
232     /* (setting focus) */
233     { WM_SHOWWINDOW, sent|wparam, 1 },
234     { WM_WINDOWPOSCHANGING, sent },
235     { WM_NCPAINT, sent },
236     { WM_GETTEXT, sent|defwinproc },
237     { WM_ERASEBKGND, sent },
238     { WM_CTLCOLORDLG, sent|defwinproc },
239     { WM_WINDOWPOSCHANGED, sent },
240     { WM_PAINT, sent },
241     /* FIXME: (bunch of WM_CTLCOLOR* for each control) */
242     { WM_PAINT, sent|parent },
243     { WM_ENTERIDLE, sent|parent|wparam, 0},
244     { WM_SETCURSOR, sent|parent },
245     { 0 }
246 };
247 /* Destruction of a modal dialog (32) */
248 static struct message WmDestroyModalDialogSeq[] = { /* FIXME: add */
249     /* (inside dialog proc: EndDialog is called) */
250     { WM_ENABLE, sent|parent|wparam, 1 },
251     { WM_SETFOCUS, sent },
252     { WM_WINDOWPOSCHANGING, sent },
253     { WM_NCPAINT, sent|parent },
254     { WM_GETTEXT, sent|defwinproc },
255     { WM_ERASEBKGND, sent|parent },
256     { WM_WINDOWPOSCHANGED, sent },
257     { WM_NCACTIVATE, sent|wparam, 0 },
258     { WM_ACTIVATE, sent|wparam, 0 },
259     { WM_WINDOWPOSCHANGING, sent },
260     { WM_WINDOWPOSCHANGING, sent|parent },
261     { WM_NCACTIVATE, sent|parent|wparam, 1 },
262     { WM_GETTEXT, sent|defwinproc },
263     { WM_ACTIVATE, sent|parent|wparam, 1 },
264     { WM_KILLFOCUS, sent },
265     { WM_SETFOCUS, sent|parent },
266     { WM_DESTROY, sent },
267     { WM_NCDESTROY, sent },
268     { 0 }
269 };
270 /* Creation of a modal dialog that is resized inside WM_INITDIALOG (32) */
271 static struct message WmCreateModalDialogResizeSeq[] = { /* FIXME: add */
272     /* (inside dialog proc, handling WM_INITDIALOG) */
273     { WM_WINDOWPOSCHANGING, sent },
274     { WM_NCCALCSIZE, sent },
275     { WM_NCACTIVATE, sent|parent|wparam, 0 },
276     { WM_GETTEXT, sent|defwinproc },
277     { WM_ACTIVATE, sent|parent|wparam, 0 },
278     { WM_WINDOWPOSCHANGING, sent },
279     { WM_WINDOWPOSCHANGING, sent|parent },
280     { WM_NCACTIVATE, sent|wparam, 1 },
281     { WM_ACTIVATE, sent|wparam, 1 },
282     { WM_WINDOWPOSCHANGED, sent },
283     { WM_SIZE, sent|defwinproc },
284     /* (setting focus) */
285     { WM_SHOWWINDOW, sent|wparam, 1 },
286     { WM_WINDOWPOSCHANGING, sent },
287     { WM_NCPAINT, sent },
288     { WM_GETTEXT, sent|defwinproc },
289     { WM_ERASEBKGND, sent },
290     { WM_CTLCOLORDLG, sent|defwinproc },
291     { WM_WINDOWPOSCHANGED, sent },
292     { WM_PAINT, sent },
293     /* (bunch of WM_CTLCOLOR* for each control) */
294     { WM_PAINT, sent|parent },
295     { WM_ENTERIDLE, sent|parent|wparam, 0 },
296     { WM_SETCURSOR, sent|parent },
297     { 0 }
298 };
299 /* SetMenu for NonVisible windows with size change*/
300 static struct message WmSetMenuNonVisibleSizeChangeSeq[] = {
301     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
302     { WM_NCCALCSIZE, sent|wparam, 1 },
303     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
304     { WM_MOVE, sent },
305     { WM_SIZE, sent },
306     { 0 }
307 };
308 /* SetMenu for NonVisible windows with no size change */
309 static struct message WmSetMenuNonVisibleNoSizeChangeSeq[] = {
310     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
311     { WM_NCCALCSIZE, sent|wparam, 1 },
312     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
313     { 0 }
314 };
315 /* SetMenu for Visible windows with size change */
316 static struct message WmSetMenuVisibleSizeChangeSeq[] = {
317     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
318     { WM_NCCALCSIZE, sent|wparam, 1 },
319     { WM_NCPAINT, sent|wparam, 1 },
320     { WM_GETTEXT, sent },
321     { WM_ACTIVATE, sent },
322     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
323     { WM_MOVE, sent },
324     { WM_SIZE, sent },
325     { 0 }
326 };
327 /* SetMenu for Visible windows with no size change */
328 static struct message WmSetMenuVisibleNoSizeChangeSeq[] = {
329     { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
330     { WM_NCCALCSIZE, sent|wparam, 1 },
331     { WM_NCPAINT, sent|wparam, 1 },
332     { WM_GETTEXT, sent },
333     { WM_ACTIVATE, sent },
334     { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
335     { 0 }
336 };
337
338 static int sequence_cnt, sequence_size;
339 static struct message* sequence;
340
341 static void add_message(struct message msg)
342 {
343     if (!sequence) 
344         sequence = malloc ( (sequence_size = 10) * sizeof (struct message) );
345     if (sequence_cnt == sequence_size) 
346         sequence = realloc ( sequence, (sequence_size *= 2) * sizeof (struct message) );
347     assert(sequence);
348     sequence[sequence_cnt++] = msg;
349 }
350
351 static void flush_sequence()
352 {
353     free(sequence);
354     sequence = 0;
355     sequence_cnt = sequence_size = 0;
356 }
357
358 static void ok_sequence(struct message *expected, const char *context)
359 {
360     static struct message end_of_sequence = { 0, 0, 0, 0 };
361     struct message *actual = sequence;
362     
363     add_message(end_of_sequence);
364
365     /* naive sequence comparison. Would be nice to use a regexp engine here */
366     while (expected->message || actual->message)
367     {
368         if (expected->message == actual->message)
369         {
370             if (expected->flags & wparam)
371                  ok (expected->wParam == actual->wParam,
372                      "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n",
373                      context, expected->message, expected->wParam, actual->wParam);
374             if (expected->flags & lparam)
375                  ok (expected->lParam == actual->lParam,
376                      "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
377                      context, expected->message, expected->lParam, actual->lParam);
378             /* FIXME: should we check defwinproc? */
379             ok ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
380                 "%s: the msg 0x%04x should have been %s\n",
381                 context, expected->message, (expected->flags & posted) ? "posted" : "sent");
382             ok ((expected->flags & parent) == (actual->flags & parent),
383                 "%s: the msg 0x%04x was expected in %s\n",
384                 context, expected->message, (expected->flags & parent) ? "parent" : "child");
385             expected++;
386             actual++;
387         }
388         else if (expected->message && ((expected + 1)->message == actual->message) )
389         {
390           todo_wine {
391             ok (FALSE, "%s: the msg 0x%04x was not received\n", context, expected->message);
392             expected++;
393           }
394         }
395         else if (actual->message && (expected->message == (actual + 1)->message) )
396         {
397           todo_wine {
398             ok (FALSE, "%s: the msg 0x%04x was not expected\n", context, actual->message);
399             actual++;
400           }
401         }
402         else
403         {
404           todo_wine {
405             ok (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
406                 context, expected->message, actual->message);
407             expected++;
408             actual++;
409           }
410         }
411     }
412
413     flush_sequence();
414 }
415
416 /* test if we receive the right sequence of messages */
417 static void test_messages(void)
418 {
419     HWND hwnd, hparent, hchild;
420     HWND hchild2, hbutton;
421     HMENU hmenu;
422
423     hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
424                            100, 100, 200, 200, 0, 0, 0, NULL);
425     ok (hwnd != 0, "Failed to create overlapped window\n");
426     ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped");
427     
428     ShowWindow(hwnd, TRUE);
429     ok_sequence(WmShowOverlappedSeq, "ShowWindow:overlapped");
430
431     DestroyWindow(hwnd);
432     ok_sequence(WmDestroyOverlappedSeq, "DestroyWindow:overlapped");
433
434     hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW,
435                               100, 100, 200, 200, 0, 0, 0, NULL);
436     ok (hparent != 0, "Failed to create parent window\n");
437     flush_sequence();
438
439     hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILDWINDOW,
440                              0, 0, 10, 10, hparent, 0, 0, NULL);
441     ok (hchild != 0, "Failed to create child window\n");
442     ok_sequence(WmCreateChildSeq, "CreateWindow:child");
443     
444     hchild2 = CreateWindowExA(0, "SimpleWindowClass", "Test child2", WS_CHILDWINDOW,
445                                100, 100, 50, 50, hparent, 0, 0, NULL);
446     ok (hchild2 != 0, "Failed to create child2 window\n");
447     flush_sequence();
448
449     hbutton = CreateWindowExA(0, "TestWindowClass", "Test button", WS_CHILDWINDOW,
450                               0, 100, 50, 50, hchild, 0, 0, NULL);
451     ok (hbutton != 0, "Failed to create button window\n");
452     flush_sequence();
453
454     ShowWindow(hchild, TRUE);
455     ok_sequence(WmShowChildSeq, "ShowWindow:child");
456
457     MoveWindow(hchild, 10, 10, 20, 20, TRUE);
458     ok_sequence(WmResizingChildWithMoveWindowSeq, "MoveWindow:child");
459
460     DestroyWindow(hchild);
461     ok_sequence(WmDestroyChildSeq, "DestroyWindow:child");
462     DestroyWindow(hchild2);
463     DestroyWindow(hbutton);
464     DestroyWindow(hparent);
465     flush_sequence();
466
467     /* Message sequence for SetMenu */
468     hmenu = CreateMenu();
469     ok (hmenu != 0, "Failed to create menu\n");
470     ok (InsertMenuA(hmenu, -1, MF_BYPOSITION, 0x1000, "foo"), "InsertMenu failed\n");
471     hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
472                            100, 100, 200, 200, 0, hmenu, 0, NULL);
473     ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped");
474     ok (SetMenu(hwnd, 0), "SetMenu");
475     ok_sequence(WmSetMenuNonVisibleSizeChangeSeq, "SetMenu:NonVisibleSizeChange");    
476     ok (SetMenu(hwnd, 0), "SetMenu");
477     ok_sequence(WmSetMenuNonVisibleNoSizeChangeSeq, "SetMenu:NonVisibleNoSizeChange");    
478     ShowWindow(hwnd, TRUE);
479     flush_sequence();
480     ok (SetMenu(hwnd, 0), "SetMenu");
481     ok_sequence(WmSetMenuVisibleNoSizeChangeSeq, "SetMenu:VisibleNoSizeChange");    
482     ok (SetMenu(hwnd, hmenu), "SetMenu");
483     ok_sequence(WmSetMenuVisibleSizeChangeSeq, "SetMenu:VisibleSizeChange");    
484     DestroyWindow(hwnd);
485
486 }
487
488 static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
489 {
490     struct message msg = { message, sent|wparam|lparam, wParam, lParam };
491
492     add_message(msg);
493     return DefWindowProcA(hwnd, message, wParam, lParam);
494 }
495
496 static BOOL RegisterWindowClasses(void)
497 {
498     WNDCLASSA cls;
499
500     cls.style = 0;
501     cls.lpfnWndProc = MsgCheckProcA;
502     cls.cbClsExtra = 0;
503     cls.cbWndExtra = 0;
504     cls.hInstance = GetModuleHandleA(0);
505     cls.hIcon = 0;
506     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
507     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
508     cls.lpszMenuName = NULL;
509     cls.lpszClassName = "TestWindowClass";
510
511     if(!RegisterClassA(&cls)) return FALSE;
512
513     cls.style = 0;
514     cls.lpfnWndProc = DefWindowProcA;
515     cls.cbClsExtra = 0;
516     cls.cbWndExtra = 0;
517     cls.hInstance = GetModuleHandleA(0);
518     cls.hIcon = 0;
519     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
520     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
521     cls.lpszMenuName = NULL;
522     cls.lpszClassName = "TestParentClass";
523
524     if(!RegisterClassA(&cls)) return FALSE;
525
526     cls.style = 0;
527     cls.lpfnWndProc = DefWindowProcA;
528     cls.cbClsExtra = 0;
529     cls.cbWndExtra = 0;
530     cls.hInstance = GetModuleHandleA(0);
531     cls.hIcon = 0;
532     cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
533     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
534     cls.lpszMenuName = NULL;
535     cls.lpszClassName = "SimpleWindowClass";
536
537     if(!RegisterClassA(&cls)) return FALSE;
538
539     return TRUE;
540 }
541
542 START_TEST(msg)
543 {
544     if (!RegisterWindowClasses()) assert(0);
545
546     test_messages();
547 }