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