Well, I did a _bit_ of the test, and things are not looking
good. I just did the {Create,Show,Destroy}Window tests for
overlapped and child windows, and I get quite a bit of failures.
Before we go around wine tweaking things to match the tests,
I'd like to have the test run on a bunch of Windows versions
to make sure things are right. 

Any suggestions on what I should include/exclude/avoid/etc.?

This is what I get now:

msg.c:403: Test failed: CreateWindow:overlapped: msg 0x24 should have been sent
msg.c:403: Test failed: CreateWindow:overlapped: msg 0x81 should have been sent
msg.c:403: Test failed: CreateWindow:overlapped: msg 0x83 should have been sent
msg.c:403: Test failed: CreateWindow:overlapped: msg 0x1 should have been sent
msg.c:403: Test failed: ShowWindow:overlapped: msg 0x18 should have been sent
msg.c:396: Test failed: ShowWindow:overlapped: in msg 0x46 expecting wParam 0x43 got 
0x0
msg.c:403: Test failed: ShowWindow:overlapped: msg 0x46 should have been sent
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x83
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x30f
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x1c
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x86
msg.c:403: Test failed: ShowWindow:overlapped: msg 0x6 should have been sent
msg.c:403: Test failed: ShowWindow:overlapped: msg 0x7 should have been sent
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x47
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x5
msg.c:407: Test failed: ShowWindow:overlapped: unexpected msg 0x3
msg.c:338: Test failed: ShowWindow:overlapped: message 0x46 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x1c was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x86 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0xd was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x85 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0xd was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x14 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x47 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x5 was not received
msg.c:338: Test failed: ShowWindow:overlapped: message 0x3 was not received
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x8
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x18
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x46
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x47
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x86
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x6
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x1c
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x2
msg.c:407: Test failed: DestroyWindow:overlapped: unexpected msg 0x82
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x46 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x47 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x86 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x6 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x1c was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x8 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x2 was not received
msg.c:338: Test failed: DestroyWindow:overlapped: message 0x82 was not received
msg.c:403: Test failed: CreateWindow:child: msg 0x81 should have been sent
msg.c:403: Test failed: CreateWindow:child: msg 0x83 should have been sent
msg.c:403: Test failed: CreateWindow:child: msg 0x1 should have been sent
msg.c:403: Test failed: CreateWindow:child: msg 0x5 should have been sent
msg.c:403: Test failed: CreateWindow:child: msg 0x3 should have been sent
msg.c:403: Test failed: ShowWindow:child: msg 0x18 should have been sent
msg.c:403: Test failed: ShowWindow:child: msg 0x46 should have been sent
msg.c:407: Test failed: ShowWindow:child: unexpected msg 0x83
msg.c:407: Test failed: ShowWindow:child: unexpected msg 0x47
msg.c:338: Test failed: ShowWindow:child: message 0x47 was not received
msg.c:403: Test failed: DestroyWindow:child: msg 0x18 should have been

-- 
Dimi.
/*
 * Unit tests for window message handling
 *
 * Copyright 2003 Dimitrie O. Paun
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"

#include "wine/test.h"


/*
My tests show that Windows window management events behave like described
in this file, at least under Win98.
The (16/32) or (16) or (32) at the end shows whether the sequence has been
confirmed for win16, win32, or both.

Window Edge Styles (Win31/Win95/98 look), in order of precedence:
 WS_EX_DLGMODALFRAME: double border, WS_CAPTION allowed
 WS_THICKFRAME: thick border
 WS_DLGFRAME: double border, WS_CAPTION not allowed (but possibly shown anyway)
 WS_BORDER (default for overlapped windows): single black border
 none (default for child (and popup?) windows): no border

Win31 look, system metrics relations:
 CYFRAME    = 5, thick border, includes both edges (the colored inside is thus 3 pixels)
 CYDLGFRAME = 4, double border
 CYBORDER   = 1, thin border
 CYCAPTION  = 20, includes both borders (the colored inside is thus 18 pixels)
 CYMENU     = 18, does not include any borders
 CYHSCROLL  = 17, includes both borders (the colored inside is thus 15 pixels)

Moving the mouse:
Messages sent:
 WM_NCHITTEST
 WM_SETCURSOR
Messages posted:
 WM_MOUSEMOVE, if WM_NCHITTEST returns HTCLIENT
 WM_NCMOUSEMOVE, if WM_NCHITTEST returns somewhere in nonclient area (not HTNOWHERE)

Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move):
 WM_NCLBUTTONDOWN (wParam=2=HTCAPTION)
  DefWindowProc:
   WM_SYSCOMMAND (wParam=0xf012=SC_MOVE+2)
    DefWindowProc:
     WM_GETMINMAXINFO
     WM_ENTERSIZEMOVE
     WM_WINDOWPOSCHANGING
     WM_WINDOWPOSCHANGED
      DefWindowProc:
       WM_MOVE
     WM_EXITSIZEMOVE

Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move):
 WM_NCLBUTTONDOWN (wParam=0xd)
  DefWindowProc:
   WM_SYSCOMMAND (wParam=0xf004)
    DefWindowProc:
     WM_GETMINMAXINFO
     WM_ENTERSIZEMOVE
     WM_SIZING (wParam=4) (many times, probably one for each mouse movement)
     WM_WINDOWPOSCHANGING
      DefWindowProc:
       WM_GETMINMAXINFO
     WM_NCCALCSIZE (wParam=1)
     WM_NCPAINT (wParam=1)
      DefWindowProc:
       WM_GETTEXT
     WM_ERASEBKGND
     WM_WINDOWPOSCHANGED
      DefWindowProc:
       WM_MOVE
       WM_SIZE
     WM_EXITSIZEMOVE

Resizing child window with MoveWindow (32):
 WM_WINDOWPOSCHANGING
 WM_NCCALCSIZE (wParam=1)
 WM_ERASEBKGND
 WM_WINDOWPOSCHANGED
  DefWindowProc:
   WM_MOVE
   WM_SIZE

Clicking on inactive button:
Messages sent:
 WM_NCHITTEST
 (to parent window) WM_PARENTNOTIFY (wParam=0x201=WM_LBUTTONDOWN)
 WM_MOUSEACTIVATE
  ButtonProc->DefWindowProc:
   (to parent window) WM_MOUSEACTIVATE
 WM_SETCURSOR
  ButtonProc->DefWindowProc:
   (to parent window) WM_SETCURSOR
Messages posted:
 WM_LBUTTONDOWN
  (to parent window) WM_KILLFOCUS
  WM_SETFOCUS
   (to parent window) WM_CTLCOLORBTN
  BM_SETSTATE32
   (to parent window) WM_CTLCOLORBTN
 WM_LBUTTONUP
  BM_SETSTATE32
   (to parent window) WM_CTLCOLORBTN
  (to parent window) WM_COMMAND

Reparenting a button (16/32):
 WM_SHOWWINDOW (wParam=0)
 WM_WINDOWPOSCHANGING (HIDEWINDOW|NOACTIVATE|NOMOVE|NOSIZE|NOZORDER)
 (to parent window) WM_ERASEBKGND
 WM_WINDOWPOSCHANGED (HIDEWINDOW|NOACTIVATE|NOMOVE|NOSIZE|NOZORDER)
 WM_WINDOWPOSCHANGING (NOSIZE|NOZORDER)
 WM_CHILDACTIVATE
 WM_WINDOWPOSCHANGED (NOSIZE|NOREDRAW|NOZORDER)
  DefWindowProc:
   WM_MOVE
 WM_SHOWWINDOW (wParam=1)
The last child (button) reparented gets topmost for its new parent.

Creation of a modal dialog (32):
 (to parent window) WM_CANCELMODE
 (to parent window) WM_KILLFOCUS
 (to parent window) WM_ENABLE (wParam=0)
 (window proc creation messages not tracked yet, because...)
 (dlgproc) WM_SETFONT
 (dlgproc) WM_INITDIALOG
 (...the window proc message hook was installed here, IsVisible still FALSE)
 (to parent window) WM_NCACTIVATE (wParam=0)
  DefWindowProc:
   WM_GETTEXT
 (to parent window) WM_ACTIVATE (wParam=0)
 WM_WINDOWPOSCHANGING
 (to parent window) WM_WINDOWPOSCHANGING
 WM_NCACTIVATE (wParam=1)
 WM_ACTIVATE (wParam=1)
 (setting focus)
 WM_SHOWWINDOW (wParam=1)
 WM_WINDOWPOSCHANGING
 WM_NCPAINT
  DefWindowProc:
   WM_GETTEXT
 WM_ERASEBKGND
  DialogWindowProc(?):
   WM_CTLCOLORDLG
 WM_WINDOWPOSCHANGED
 WM_PAINT
 (bunch of WM_CTLCOLOR* for each control)
 (to parent window) WM_PAINT
 (to parent window) WM_ENTERIDLE (wParam=0)
 (to parent window) WM_SETCURSOR

Destruction of a modal dialog (32):
 (inside dialog proc: EndDialog is called)
  (to parent window) WM_ENABLE (wParam=1)
  WM_SETFOCUS
  WM_WINDOWPOSCHANGING
  (to parent window) WM_NCPAINT
   DefWindowProc:
    WM_GETTEXT
  (to parent window) WM_ERASEBKGND
  WM_WINDOWPOSCHANGED
  WM_NCACTIVATE (wParam=0)
  WM_ACTIVATE (wParam=0)
  WM_WINDOWPOSCHANGING
  (to parent window) WM_WINDOWPOSCHANGING
  (to parent window) WM_NCACTIVATE (wParam=1)
   DefWindowProc:
    WM_GETTEXT
  (to parent window) WM_ACTIVATE (wParam=1)
   (to dialog) WM_KILLFOCUS
   (to parent window) WM_SETFOCUS
 WM_DESTROY
 WM_NCDESTROY

Creation of a modal dialog that is resized inside WM_INITDIALOG (32):
 (inside dialog proc, handling WM_INITDIALOG)
  WM_WINDOWPOSCHANGING
  WM_NCCALCSIZE
  (to parent window) WM_NCACTIVATE (wParam=0)
   DefWindowProc:
    WM_GETTEXT
  (to parent window) WM_ACTIVATE (wParam=0)
  WM_WINDOWPOSCHANGING
  (to parent window) WM_WINDOWPOSCHANGING
  WM_NCACTIVATE (wParam=1)
  WM_ACTIVATE (wParam=1)
  WM_WINDOWPOSCHANGED
   DefWindowProc:
    WM_SIZE
 (setting focus)
 WM_SHOWWINDOW (wParam=1)
 WM_WINDOWPOSCHANGING
 WM_NCPAINT
  DefWindowProc:
   WM_GETTEXT
 WM_ERASEBKGND
  DialogWindowProc(?):
   WM_CTLCOLORDLG
 WM_WINDOWPOSCHANGED
 WM_PAINT
 (bunch of WM_CTLCOLOR* for each control)
 (to parent window) WM_PAINT
 (to parent window) WM_ENTERIDLE (wParam=0)
 (to parent window) WM_SETCURSOR
*/

struct message {
    UINT message;          /* the WM_* code */
    BOOL posted;           /* if false, message was sent */
    BOOL check_wParam;     /* if false, do not check wParam */
    WPARAM wParam;         /* expacted value of wParam */
    BOOL check_lParam;     /* if false, do not check lParam */
    LPARAM lParam;         /* expacted value of lParam */
    BOOL received;         /* set to true if it was received OK */
};

struct message_sequence {
    const char *context;
    struct message *messages;
    struct message *current;
};

/* CreateWindow (for overlapped window, not initially visible) (16/32) */
static struct message WmCreateOverlappedSeq[] = {
    { WM_GETMINMAXINFO },
    { WM_NCCREATE },
    { WM_NCCALCSIZE, FALSE, TRUE, 0 },
    { WM_CREATE },
    { 0 }
};
/* ShowWindow (for overlapped window) (16/32) */
static struct message WmShowOverlappedSeq[] = {
    { WM_SHOWWINDOW, FALSE, TRUE, 1 },
    { WM_WINDOWPOSCHANGING, FALSE, TRUE, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW },
    /* FIXME: WM_QUERYNEWPALETTE, if in 256-color mode */
    { WM_WINDOWPOSCHANGING, FALSE, TRUE, SWP_NOMOVE|SWP_NOSIZE },
    { WM_ACTIVATEAPP, FALSE, TRUE, 1 },
    { WM_NCACTIVATE, FALSE, TRUE, 1 },
    { WM_GETTEXT },                  /* from DefWindowProc */
    { WM_ACTIVATE, FALSE, TRUE, 1 },
    { WM_SETFOCUS, FALSE, TRUE, 0 }, /* from DefWindowProc */
    { WM_NCPAINT, FALSE, TRUE, 1 },
    { WM_GETTEXT },                  /* from DefWindowProc */
    { WM_ERASEBKGND },
    { WM_WINDOWPOSCHANGED, FALSE, TRUE, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_SHOWWINDOW },
    { WM_SIZE }, /* (NOT from DefWindowProc) */
    { WM_MOVE }, /* (NOT from DefWindowProc) */
    { 0 }
};

/* DestroyWindow (for overlapped window) (32) */
static struct message WmDestroyOverlappedSeq[] = {
    { WM_WINDOWPOSCHANGING, FALSE, TRUE, 0 },
    { WM_WINDOWPOSCHANGED, FALSE, TRUE, 0 },
    { WM_NCACTIVATE, FALSE, TRUE, 0 },
    { WM_ACTIVATE, FALSE, TRUE, 0 },
    { WM_ACTIVATEAPP, FALSE, TRUE, 0 },
    { WM_KILLFOCUS, FALSE, TRUE, 0 },
    { WM_DESTROY },
    { WM_NCDESTROY },
    { 0 }
};
/* CreateWindow (for child window, not initially visible) */
static struct message WmCreateChildSeq[] = {
    { WM_NCCREATE }, 
    /* child is inserted into parent's child list after WM_NCCREATE returns */
    { WM_NCCALCSIZE, FALSE, TRUE, 0 },
    { WM_CREATE },
    { WM_SIZE },
    { WM_MOVE },
    /* FIXME: (to parent window) WM_PARENTNOTIFY (wParam=1) */
    { 0 }
};
/* ShowWindow (for child window) */
static struct message WmShowChildSeq[] = {
    { WM_SHOWWINDOW, FALSE, TRUE, 1 },
    { WM_WINDOWPOSCHANGING, FALSE, TRUE, 0 },
    /* FIXME: (to parent window) WM_ERASEBKGND */
    { WM_WINDOWPOSCHANGED, FALSE, TRUE, 0 },
    { 0 }
};
/* DestroyWindow (for child window) */
static struct message WmDestroyChildSeq[] = {
    /* FIXME: (to parent window) WM_PARENTNOTIFY (wParam=2) */
    { WM_SHOWWINDOW, FALSE, TRUE, 0 },
    { WM_WINDOWPOSCHANGING, FALSE, TRUE, 0 },
    /* FIXME: (to parent window) WM_ERASEBKGND */
    { WM_WINDOWPOSCHANGED, FALSE, TRUE, 0 },
    { WM_DESTROY },
    { WM_NCDESTROY },
    { 0 }
};

static struct message_sequence sequence;;

static void set_message_sequence(struct message *msgs, const char *context)
{
    sequence.context = context;
    sequence.messages = msgs;
    sequence.current = msgs;
}

static void ok_sequence()
{
    struct message *current;

    for (current = sequence.messages; current->message; current++)
    {
	ok (current->received, "%s: message 0x%x was not received\n", 
	    sequence.context, current->message);
    }
}

/* test if we receive the right sequence of messages */
static void test_messages(void)
{
    HWND hwnd, hparent, hchild;

    set_message_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped");
    hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
                           100, 100, 200, 200, 0, 0, 0, NULL);
    ok (hwnd != 0, "Failed to create test overlapped window\n");
    ok_sequence();
    
    set_message_sequence(WmShowOverlappedSeq, "ShowWindow:overlapped");
    ShowWindow(hwnd, TRUE);
    ok_sequence();

    set_message_sequence(WmDestroyOverlappedSeq, "DestroyWindow:overlapped");
    DestroyWindow(hwnd);
    ok_sequence();

    hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW,
                              100, 100, 200, 200, 0, 0, 0, NULL);
    ok (hparent != 0, "Failed to create test parent window\n");

    set_message_sequence(WmCreateChildSeq, "CreateWindow:child");
    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILDWINDOW,
                             100, 100, 200, 200, hparent, 0, 0, NULL);
    ok (hchild != 0, "Failed to create test child window\n");
    ok_sequence();
    
    set_message_sequence(WmShowChildSeq, "ShowWindow:child");
    ShowWindow(hchild, TRUE);
    ok_sequence();

    set_message_sequence(WmDestroyChildSeq, "DestroyWindow:child");
    DestroyWindow(hchild);
    ok_sequence();
}

static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    /* check ahead a bit if we don't get what we expect */
    if (sequence.current->message != message && sequence.current->message)
    {
	if ((sequence.current + 1)->message == message)
	{
	    ok(FALSE, "%s: missing msg 0x%x\n", sequence.context, sequence.current->message);
	    sequence.current++;
	}
    }
    if (sequence.current->message == message)
    {
	sequence.current->received = TRUE;
	if (sequence.current->check_wParam)
	    ok (sequence.current->wParam == wParam, 
		"%s: in msg 0x%x expecting wParam 0x%x got 0x%x\n", 
		sequence.context, message, sequence.current->wParam, wParam);
	if (sequence.current->check_lParam)
	    ok (sequence.current->lParam == lParam, 
		"%s: in msg 0x%x expecting lParam 0x%lx got 0x%lx\n", 
		sequence.context, message, sequence.current->lParam, lParam);
	ok (sequence.current->posted == !InSendMessage(),
		"%s: msg 0x%x should have been %s\n",
		sequence.context, message, (sequence.current->posted ? "posted" : "sent"));
    }
    else ok (FALSE, "%s: unexpected msg 0x%x\n", sequence.context, message);

    if (sequence.current->message) sequence.current++;
    return DefWindowProcA(hwnd, message, wParam, lParam);
}

static BOOL RegisterWindowClasses(void)
{
    WNDCLASSA cls;

    cls.style = 0;
    cls.lpfnWndProc = MsgCheckProcA;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = GetModuleHandleA(0);
    cls.hIcon = 0;
    cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
    cls.lpszMenuName = NULL;
    cls.lpszClassName = "TestWindowClass";

    if(!RegisterClassA(&cls)) return FALSE;

    cls.style = 0;
    cls.lpfnWndProc = DefWindowProcA;
    cls.cbClsExtra = 0;
    cls.cbWndExtra = 0;
    cls.hInstance = GetModuleHandleA(0);
    cls.hIcon = 0;
    cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
    cls.lpszMenuName = NULL;
    cls.lpszClassName = "TestParentClass";

    if(!RegisterClassA(&cls)) return FALSE;

    return TRUE;
}

START_TEST(msg)
{
    if (!RegisterWindowClasses()) assert(0);

    test_messages();
}

Reply via email to