added user32 wine regression test
authorSteven Edwards <winehacker@gmail.com>
Sat, 6 Aug 2005 23:41:45 +0000 (23:41 +0000)
committerSteven Edwards <winehacker@gmail.com>
Sat, 6 Aug 2005 23:41:45 +0000 (23:41 +0000)
svn path=/trunk/; revision=17125

19 files changed:
reactos/regtests/winetests/user32/class.c [new file with mode: 0755]
reactos/regtests/winetests/user32/clipboard.c [new file with mode: 0755]
reactos/regtests/winetests/user32/dce.c [new file with mode: 0755]
reactos/regtests/winetests/user32/dde.c [new file with mode: 0755]
reactos/regtests/winetests/user32/dialog.c [new file with mode: 0755]
reactos/regtests/winetests/user32/edit.c [new file with mode: 0755]
reactos/regtests/winetests/user32/input.c [new file with mode: 0755]
reactos/regtests/winetests/user32/listbox.c [new file with mode: 0644]
reactos/regtests/winetests/user32/menu.c [new file with mode: 0755]
reactos/regtests/winetests/user32/msg.c [new file with mode: 0755]
reactos/regtests/winetests/user32/resource.c [new file with mode: 0755]
reactos/regtests/winetests/user32/resource.rc [new file with mode: 0755]
reactos/regtests/winetests/user32/sysparams.c [new file with mode: 0755]
reactos/regtests/winetests/user32/testlist.c [new file with mode: 0644]
reactos/regtests/winetests/user32/text.c [new file with mode: 0755]
reactos/regtests/winetests/user32/user32_test.xml [new file with mode: 0644]
reactos/regtests/winetests/user32/win.c [new file with mode: 0644]
reactos/regtests/winetests/user32/winstation.c [new file with mode: 0755]
reactos/regtests/winetests/user32/wsprintf.c [new file with mode: 0755]

diff --git a/reactos/regtests/winetests/user32/class.c b/reactos/regtests/winetests/user32/class.c
new file mode 100755 (executable)
index 0000000..250b6b6
--- /dev/null
@@ -0,0 +1,613 @@
+/* Unit test suite for window classes.
+ *
+ * Copyright 2002 Mike McCormack
+ * Copyright 2003 Alexandre Julliard
+ *
+ * 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
+ */
+
+/* To get CS_DROPSHADOW with the MSVC headers */
+#define _WIN32_WINNT 0x0501
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#define NUMCLASSWORDS 4
+
+static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    return DefWindowProcW (hWnd, msg, wParam, lParam);
+}
+
+/***********************************************************************
+ */
+static void ClassTest(HINSTANCE hInstance, BOOL global)
+{
+    WNDCLASSW cls, wc;
+    static const WCHAR className[] = {'T','e','s','t','C','l','a','s','s',0};
+    static const WCHAR winName[]   = {'W','i','n','C','l','a','s','s','T','e','s','t',0};
+    ATOM test_atom;
+    HWND hTestWnd;
+    LONG i;
+    WCHAR str[20];
+    ATOM classatom;
+
+    cls.style         = CS_HREDRAW | CS_VREDRAW | (global?CS_GLOBALCLASS:0);
+    cls.lpfnWndProc   = ClassTest_WndProc;
+    cls.cbClsExtra    = NUMCLASSWORDS*sizeof(DWORD);
+    cls.cbWndExtra    = 12;
+    cls.hInstance     = hInstance;
+    cls.hIcon         = LoadIconW (0, (LPWSTR)IDI_APPLICATION);
+    cls.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
+    cls.hbrBackground = GetStockObject (WHITE_BRUSH);
+    cls.lpszMenuName  = 0;
+    cls.lpszClassName = className;
+
+    classatom=RegisterClassW(&cls);
+    if (!classatom && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(classatom, "failed to register class\n");
+
+    ok(!RegisterClassW (&cls),
+        "RegisterClass of the same class should fail for the second time\n");
+
+    /* Setup windows */
+    hTestWnd = CreateWindowW (className, winName,
+       WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
+       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0,
+       0, hInstance, 0);
+
+    ok(hTestWnd!=0, "Failed to create window\n");
+
+    /* test initial values of valid classwords */
+    for(i=0; i<NUMCLASSWORDS; i++)
+    {
+        SetLastError(0);
+        ok(!GetClassLongW(hTestWnd,i*sizeof (DWORD)),
+            "GetClassLongW initial value nonzero!\n");
+        ok(!GetLastError(),
+            "GetClassLongW failed!\n");
+    }
+
+#if 0
+    /*
+     *  GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD))
+     *  does not fail on Win 98, though MSDN says it should
+     */
+    SetLastError(0);
+    GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD));
+    ok(GetLastError(),
+        "GetClassLongW() with invalid offset did not fail\n");
+#endif
+
+    /* set values of valid class words */
+    for(i=0; i<NUMCLASSWORDS; i++)
+    {
+        SetLastError(0);
+        ok(!SetClassLongW(hTestWnd,i*sizeof(DWORD),i+1),
+            "GetClassLongW(%ld) initial value nonzero!\n",i*sizeof(DWORD));
+        ok(!GetLastError(),
+            "SetClassLongW(%ld) failed!\n",i*sizeof(DWORD));
+    }
+
+    /* test values of valid classwords that we set */
+    for(i=0; i<NUMCLASSWORDS; i++)
+    {
+        SetLastError(0);
+        ok( (i+1) == GetClassLongW(hTestWnd,i*sizeof (DWORD)),
+            "GetClassLongW value doesn't match what was set!\n");
+        ok(!GetLastError(),
+            "GetClassLongW failed!\n");
+    }
+
+    /* check GetClassName */
+    i = GetClassNameW(hTestWnd, str, sizeof(str));
+    ok(i == lstrlenW(className),
+        "GetClassName returned incorrect length\n");
+    ok(!lstrcmpW(className,str),
+        "GetClassName returned incorrect name for this window's class\n");
+
+    /* check GetClassInfo with our hInstance */
+    if((test_atom = GetClassInfoW(hInstance, str, &wc)))
+    {
+        ok(test_atom == classatom,
+            "class atom did not match\n");
+        ok(wc.cbClsExtra == cls.cbClsExtra,
+            "cbClsExtra did not match\n");
+        ok(wc.cbWndExtra == cls.cbWndExtra,
+            "cbWndExtra did not match\n");
+        ok(wc.hbrBackground == cls.hbrBackground,
+            "hbrBackground did not match\n");
+        ok(wc.hCursor== cls.hCursor,
+            "hCursor did not match\n");
+        ok(wc.hInstance== cls.hInstance,
+            "hInstance did not match\n");
+    }
+    else
+        ok(FALSE,"GetClassInfo (hinstance) failed!\n");
+
+    /* check GetClassInfo with zero hInstance */
+    if(global)
+    {
+        if((test_atom = GetClassInfoW(0, str, &wc)))
+        {
+            ok(test_atom == classatom,
+                "class atom did not match %x != %x\n", test_atom, classatom);
+            ok(wc.cbClsExtra == cls.cbClsExtra,
+                "cbClsExtra did not match %x!=%x\n",wc.cbClsExtra,cls.cbClsExtra);
+            ok(wc.cbWndExtra == cls.cbWndExtra,
+                "cbWndExtra did not match %x!=%x\n",wc.cbWndExtra,cls.cbWndExtra);
+            ok(wc.hbrBackground == cls.hbrBackground,
+                "hbrBackground did not match %p!=%p\n",wc.hbrBackground,cls.hbrBackground);
+            ok(wc.hCursor== cls.hCursor,
+                "hCursor did not match %p!=%p\n",wc.hCursor,cls.hCursor);
+            ok(!wc.hInstance,
+                "hInstance not zero for global class %p\n",wc.hInstance);
+        }
+        else
+            ok(FALSE,"GetClassInfo (0) failed for global class!\n");
+    }
+    else
+    {
+        ok(!GetClassInfoW(0, str, &wc),
+            "GetClassInfo (0) succeeded for local class!\n");
+    }
+
+    ok(!UnregisterClassW(className, hInstance),
+        "Unregister class succeeded with window existing\n");
+
+    ok(DestroyWindow(hTestWnd),
+        "DestroyWindow() failed!\n");
+
+    ok(UnregisterClassW(className, hInstance),
+        "UnregisterClass() failed\n");
+
+    return;
+}
+
+static void check_style( const char *name, int must_exist, UINT style, UINT ignore )
+{
+    WNDCLASS wc;
+
+    if (GetClassInfo( 0, name, &wc ))
+    {
+        ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n",
+            name, ~wc.style & style, wc.style, style );
+        ok( !(wc.style & ~style), "System class %s has extra bits %x (%08x/%08x)\n",
+            name, wc.style & ~style, wc.style, style );
+    }
+    else
+        ok( !must_exist, "System class %s does not exist\n", name );
+}
+
+/* test styles of system classes */
+static void test_styles(void)
+{
+    /* check style bits */
+    check_style( "Button",     1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+    check_style( "ComboBox",   1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+    check_style( "Edit",       1, CS_PARENTDC | CS_DBLCLKS, 0 );
+    check_style( "ListBox",    1, CS_PARENTDC | CS_DBLCLKS, CS_PARENTDC /*FIXME*/ );
+    check_style( "MDIClient",  1, 0, 0 );
+    check_style( "ScrollBar",  1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+    check_style( "Static",     1, CS_PARENTDC | CS_DBLCLKS, 0 );
+    check_style( "ComboLBox",  1, CS_SAVEBITS | CS_DBLCLKS, 0 );
+    check_style( "DDEMLEvent", 0, 0, 0 );
+    check_style( "Message",    0, 0, 0 );
+    check_style( "#32768",     1, CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, CS_DROPSHADOW );  /* menu */
+    check_style( "#32769",     1, CS_DBLCLKS, 0 );  /* desktop */
+    check_style( "#32770",     1, CS_SAVEBITS | CS_DBLCLKS, 0 );  /* dialog */
+    todo_wine { check_style( "#32771",     1, CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW, 0 ); } /* task switch */
+    check_style( "#32772",     1, 0, 0 );  /* icon title */
+}
+
+static void check_class(HINSTANCE inst, const char *name, const char *menu_name)
+{
+    WNDCLASS wc;
+    UINT atom = GetClassInfo(inst,name,&wc);
+    ok( atom, "Class %s %p not found\n", name, inst );
+    if (atom)
+    {
+        if (wc.lpszMenuName && menu_name)
+            ok( !strcmp( menu_name, wc.lpszMenuName ), "Wrong name %s/%s for class %s %p\n",
+                wc.lpszMenuName, menu_name, name, inst );
+        else
+            ok( !menu_name == !wc.lpszMenuName, "Wrong name %p/%p for class %s %p\n",
+                wc.lpszMenuName, menu_name, name, inst );
+    }
+}
+
+static void check_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst )
+{
+    WNDCLASSA wc;
+    HWND hwnd;
+
+    ok( GetClassInfo( inst, name, &wc ), "Couldn't find class %s inst %p\n", name, inst );
+    ok( wc.hInstance == info_inst, "Wrong info instance %p/%p for class %s\n",
+        wc.hInstance, info_inst, name );
+    hwnd = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, inst, 0 );
+    ok( hwnd != NULL, "Couldn't create window for class %s inst %p\n", name, inst );
+    ok( (HINSTANCE)GetClassLongA( hwnd, GCL_HMODULE ) == gcl_inst,
+        "Wrong GCL instance %p/%p for class %s\n",
+        (HINSTANCE)GetClassLongA( hwnd, GCL_HMODULE ), gcl_inst, name );
+    ok( (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ) == inst,
+        "Wrong GWL instance %p/%p for window %s\n",
+        (HINSTANCE)GetWindowLongA( hwnd, GWL_HINSTANCE ), inst, name );
+    ok(!UnregisterClassA(name, inst), "UnregisterClassA should fail while exists a class window\n");
+    ok(GetLastError() == ERROR_CLASS_HAS_WINDOWS, "GetLastError() should be set to ERROR_CLASS_HAS_WINDOWS not %ld\n", GetLastError());
+    DestroyWindow(hwnd);
+}
+
+struct class_info
+{
+    const char *name;
+    HINSTANCE inst, info_inst, gcl_inst;
+};
+
+static DWORD WINAPI thread_proc(void *param)
+{
+    struct class_info *class_info = (struct class_info *)param;
+
+    check_instance(class_info->name, class_info->inst, class_info->info_inst, class_info->gcl_inst);
+
+    return 0;
+}
+
+static void check_thread_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst )
+{
+    HANDLE hThread;
+    DWORD tid;
+    struct class_info class_info;
+
+    class_info.name = name;
+    class_info.inst = inst;
+    class_info.info_inst = info_inst;
+    class_info.gcl_inst = gcl_inst;
+
+    hThread = CreateThread(NULL, 0, thread_proc, &class_info, 0, &tid);
+    ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+    ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+    CloseHandle(hThread);
+}
+
+/* test various instance parameters */
+static void test_instances(void)
+{
+    WNDCLASSA cls, wc;
+    HWND hwnd, hwnd2;
+    const char *name = "__test__";
+    HINSTANCE kernel32 = GetModuleHandleA("kernel32");
+    HINSTANCE user32 = GetModuleHandleA("user32");
+    HINSTANCE main_module = GetModuleHandleA(NULL);
+
+    memset( &cls, 0, sizeof(cls) );
+    cls.style         = CS_HREDRAW | CS_VREDRAW;
+    cls.lpfnWndProc   = ClassTest_WndProc;
+    cls.cbClsExtra    = 0;
+    cls.cbWndExtra    = 0;
+    cls.lpszClassName = name;
+
+    cls.lpszMenuName  = "main_module";
+    cls.hInstance = main_module;
+
+    ok( RegisterClassA( &cls ), "Failed to register local class for main module\n" );
+    check_class( main_module, name, "main_module" );
+    check_instance( name, main_module, main_module, main_module );
+    check_thread_instance( name, main_module, main_module, main_module );
+
+    cls.lpszMenuName  = "kernel32";
+    cls.hInstance = kernel32;
+    ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    check_class( kernel32, name, "kernel32" );
+    check_class( main_module, name, "main_module" );
+    check_instance( name, kernel32, kernel32, kernel32 );
+    check_thread_instance( name, kernel32, kernel32, kernel32 );
+    ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+    /* Bug 2631 - Supplying an invalid number of bytes fails */
+    cls.cbClsExtra    = 0;
+    cls.cbWndExtra    = -1;
+    SetLastError(0xdeadbeef);
+    ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+          "Failed with invalid number of WndExtra bytes\n");
+
+    cls.cbClsExtra    = -1;
+    cls.cbWndExtra    = 0;
+    SetLastError(0xdeadbeef);
+    ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+          "Failed with invalid number of ClsExtra bytes\n");
+
+    cls.cbClsExtra    = -1;
+    cls.cbWndExtra    = -1;
+    SetLastError(0xdeadbeef);
+    ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+          "Failed with invalid number of ClsExtra and cbWndExtra bytes\n");
+
+    cls.cbClsExtra    = 0;
+    cls.cbWndExtra    = 0;
+    SetLastError(0xdeadbeef);
+
+    /* setting global flag doesn't change status of class */
+    hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+    SetClassLongA( hwnd, GCL_STYLE, CS_GLOBALCLASS );
+    cls.lpszMenuName  = "kernel32";
+    cls.hInstance = kernel32;
+    ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    check_class( kernel32, name, "kernel32" );
+    check_class( main_module, name, "main_module" );
+    check_instance( name, kernel32, kernel32, kernel32 );
+    check_instance( name, main_module, main_module, main_module );
+    check_thread_instance( name, kernel32, kernel32, kernel32 );
+    check_thread_instance( name, main_module, main_module, main_module );
+    ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+    /* changing the instance doesn't make it global */
+    SetClassLongA( hwnd, GCL_HMODULE, 0 );
+    ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    check_class( kernel32, name, "kernel32" );
+    check_instance( name, kernel32, kernel32, kernel32 );
+    check_thread_instance( name, kernel32, kernel32, kernel32 );
+    ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+    ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+    /* GetClassInfo with instance 0 finds user32 instance */
+    SetClassLongA( hwnd, GCL_HMODULE, (LONG)user32 );
+    ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    check_class( kernel32, name, "kernel32" );
+    check_class( user32, name, "main_module" );
+    check_class( 0, name, "main_module" );
+    check_instance( name, kernel32, kernel32, kernel32 );
+    check_instance( name, user32, 0, user32 );
+    check_instance( name, 0, 0, kernel32 );
+    check_thread_instance( name, kernel32, kernel32, kernel32 );
+    check_thread_instance( name, user32, 0, user32 );
+    check_thread_instance( name, 0, 0, kernel32 );
+    ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+    SetClassLongA( hwnd, GCL_HMODULE, 0x12345678 );
+    ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    check_class( kernel32, name, "kernel32" );
+    check_class( (HINSTANCE)0x12345678, name, "main_module" );
+    check_instance( name, kernel32, kernel32, kernel32 );
+    check_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 );
+    check_thread_instance( name, kernel32, kernel32, kernel32 );
+    check_thread_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 );
+    ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+
+    /* creating a window with instance 0 uses the first class found */
+    cls.hInstance = (HINSTANCE)0xdeadbeef;
+    cls.lpszMenuName = "deadbeef";
+    cls.style = 3;
+    ok( RegisterClassA( &cls ), "Failed to register local class for deadbeef\n" );
+    hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+    ok( (HINSTANCE)GetClassLong( hwnd2, GCL_HMODULE ) == (HINSTANCE)0xdeadbeef,
+        "Didn't get deadbeef class for null instance\n" );
+    DestroyWindow( hwnd2 );
+    ok( UnregisterClassA( name, (HINSTANCE)0xdeadbeef ), "Unregister failed for deadbeef\n" );
+
+    hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+    ok( (HINSTANCE)GetClassLong( hwnd2, GCL_HMODULE ) == kernel32,
+        "Didn't get kernel32 class for null instance\n" );
+    DestroyWindow( hwnd2 );
+
+    ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+    hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+    ok( GetClassLong( hwnd2, GCL_HMODULE ) == 0x12345678,
+        "Didn't get 12345678 class for null instance\n" );
+    DestroyWindow( hwnd2 );
+
+    SetClassLongA( hwnd, GCL_HMODULE, (LONG)main_module );
+    DestroyWindow( hwnd );
+
+    /* null handle means the same thing as main module */
+    cls.lpszMenuName  = "null";
+    cls.hInstance = 0;
+    ok( !RegisterClassA( &cls ), "Succeeded registering local class for null instance\n" );
+    ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+    ok( UnregisterClassA( name, main_module ), "Unregister failed for main module\n" );
+
+    ok( RegisterClassA( &cls ), "Failed to register local class for null instance\n" );
+    /* must be found with main module handle */
+    check_class( main_module, name, "null" );
+    check_instance( name, main_module, main_module, main_module );
+    check_thread_instance( name, main_module, main_module, main_module );
+    ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+    ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+    ok( UnregisterClassA( name, 0 ), "Unregister failed for null instance\n" );
+
+    /* registering for user32 always fails */
+    cls.lpszMenuName = "user32";
+    cls.hInstance = user32;
+    ok( !RegisterClassA( &cls ), "Succeeded registering local class for user32\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() );
+    cls.style |= CS_GLOBALCLASS;
+    ok( !RegisterClassA( &cls ), "Succeeded registering global class for user32\n" );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() );
+
+    /* unregister is OK though */
+    cls.hInstance = main_module;
+    ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" );
+    ok( UnregisterClassA( name, user32 ), "Unregister failed for user32\n" );
+
+    /* instance doesn't matter for global class */
+    cls.style |= CS_GLOBALCLASS;
+    cls.lpszMenuName  = "main_module";
+    cls.hInstance = main_module;
+    ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" );
+    cls.lpszMenuName  = "kernel32";
+    cls.hInstance = kernel32;
+    ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" );
+    ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+    /* even if global flag is cleared */
+    hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+    SetClassLongA( hwnd, GCL_STYLE, 0 );
+    ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" );
+    ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+
+    check_class( main_module, name, "main_module" );
+    check_class( kernel32, name, "main_module" );
+    check_class( 0, name, "main_module" );
+    check_class( (HINSTANCE)0x12345678, name, "main_module" );
+    check_instance( name, main_module, main_module, main_module );
+    check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module );
+    check_thread_instance( name, main_module, main_module, main_module );
+    check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module );
+
+    /* changing the instance for global class doesn't make much difference */
+    SetClassLongA( hwnd, GCL_HMODULE, 0xdeadbeef );
+    check_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef );
+    check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef );
+    check_thread_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef );
+    check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef );
+
+    DestroyWindow( hwnd );
+    ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" );
+    ok( !UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister succeeded the second time\n" );
+    ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+
+    cls.hInstance = (HINSTANCE)0x12345678;
+    ok( RegisterClassA( &cls ), "Failed to register global class for dummy instance\n" );
+    check_instance( name, main_module, main_module, (HINSTANCE)0x12345678 );
+    check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 );
+    check_thread_instance( name, main_module, main_module, (HINSTANCE)0x12345678 );
+    check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 );
+    ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" );
+
+    /* check system classes */
+
+    /* we cannot register a global class with the name of a system class */
+    cls.style |= CS_GLOBALCLASS;
+    cls.lpszMenuName  = "button_main_module";
+    cls.lpszClassName = "BUTTON";
+    cls.hInstance = main_module;
+    ok( !RegisterClassA( &cls ), "Succeeded registering global button class for main module\n" );
+    ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+    cls.hInstance = kernel32;
+    ok( !RegisterClassA( &cls ), "Succeeded registering global button class for kernel32\n" );
+    ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+
+    /* local class is OK however */
+    cls.style &= ~CS_GLOBALCLASS;
+    cls.lpszMenuName  = "button_main_module";
+    cls.hInstance = main_module;
+    ok( RegisterClassA( &cls ), "Failed to register local button class for main module\n" );
+    check_class( main_module, "BUTTON", "button_main_module" );
+    cls.lpszMenuName  = "button_kernel32";
+    cls.hInstance = kernel32;
+    ok( RegisterClassA( &cls ), "Failed to register local button class for kernel32\n" );
+    check_class( kernel32, "BUTTON", "button_kernel32" );
+    check_class( main_module, "BUTTON", "button_main_module" );
+    ok( UnregisterClassA( "BUTTON", kernel32 ), "Unregister failed for kernel32 button\n" );
+    ok( UnregisterClassA( "BUTTON", main_module ), "Unregister failed for main module button\n" );
+    /* GetClassInfo sets instance to passed value for global classes */
+    check_instance( "BUTTON", 0, 0, user32 );
+    check_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+    check_instance( "BUTTON", user32, 0, user32 );
+    check_thread_instance( "BUTTON", 0, 0, user32 );
+    check_thread_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+    check_thread_instance( "BUTTON", user32, 0, user32 );
+
+    /* we can unregister system classes */
+    ok( GetClassInfo( 0, "BUTTON", &wc ), "Button class not found with null instance\n" );
+    ok( GetClassInfo( kernel32, "BUTTON", &wc ), "Button class not found with kernel32\n" );
+    ok( UnregisterClass( "BUTTON", (HINSTANCE)0x12345678 ), "Failed to unregister button\n" );
+    ok( !UnregisterClass( "BUTTON", (HINSTANCE)0x87654321 ), "Unregistered button a second time\n" );
+    ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+    ok( !GetClassInfo( 0, "BUTTON", &wc ), "Button still exists\n" );
+    ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+
+    /* we can change the instance of a system class */
+    check_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+    check_thread_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+    hwnd = CreateWindowExA( 0, "EDIT", "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+    SetClassLongA( hwnd, GCL_HMODULE, 0xdeadbeef );
+    check_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef );
+    check_thread_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef );
+}
+
+static LRESULT WINAPI TestDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+static BOOL RegisterTestDialog(HINSTANCE hInstance)
+{
+    WNDCLASSEX wcx;
+    ATOM atom = 0;
+
+    ZeroMemory(&wcx, sizeof(WNDCLASSEX));
+    wcx.cbSize = sizeof(wcx);
+    wcx.lpfnWndProc = TestDlgProc;
+    wcx.cbClsExtra = 0;
+    wcx.cbWndExtra = DLGWINDOWEXTRA;
+    wcx.hInstance = hInstance;
+    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+    wcx.lpszClassName = "TestDialog";
+    wcx.lpszMenuName =  "TestDialog";
+    wcx.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(5),
+        IMAGE_ICON,
+        GetSystemMetrics(SM_CXSMICON),
+        GetSystemMetrics(SM_CYSMICON),
+        LR_DEFAULTCOLOR);
+
+    atom = RegisterClassEx(&wcx);
+    ok(atom != 0, "RegisterClassEx returned 0\n");
+
+    return atom;
+}
+
+/* test registering a dialog box created by using the CLASS directive in a
+   resource file, then test creating the dialog using CreateDialogParam. */
+static void WINAPI CreateDialogParamTest(HINSTANCE hInstance)
+{
+    HWND hWndMain;
+
+    if (RegisterTestDialog(hInstance))
+    {
+        hWndMain = CreateDialogParam(hInstance, "CLASS_TEST_DIALOG", NULL, 0, 0);
+        ok(hWndMain != NULL, "CreateDialogParam returned NULL\n");
+        ShowWindow(hWndMain, SW_SHOW);
+        DestroyWindow(hWndMain);
+    }
+}
+
+START_TEST(class)
+{
+    HANDLE hInstance = GetModuleHandleA( NULL );
+
+    if (!GetModuleHandleW(0))
+    {
+        trace("Class test is incompatible with Win9x implementation, skipping\n");
+        return;
+    }
+
+    ClassTest(hInstance,FALSE);
+    ClassTest(hInstance,TRUE);
+    CreateDialogParamTest(hInstance);
+    test_styles();
+    test_instances();
+}
diff --git a/reactos/regtests/winetests/user32/clipboard.c b/reactos/regtests/winetests/user32/clipboard.c
new file mode 100755 (executable)
index 0000000..5f90e7b
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Unit test suite for clipboard functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * 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 "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winuser.h"
+
+static BOOL is_win9x = FALSE;
+
+#define test_last_error(expected_error) \
+    do \
+    { \
+        if (!is_win9x) \
+            ok(GetLastError() == expected_error, \
+               "Last error should be set to %d, not %ld\n", \
+                expected_error, GetLastError()); \
+    } while (0)
+
+static void test_ClipboardOwner(void)
+{
+    HWND hWnd1, hWnd2;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef,
+       "could not perform clipboard test: clipboard already owned\n");
+
+    hWnd1 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                                 0, 0, 10, 10, 0, 0, 0, NULL);
+    ok(hWnd1 != 0, "CreateWindowExA error %ld\n", GetLastError());
+    trace("hWnd1 = %p\n", hWnd1);
+
+    hWnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+                                 0, 0, 10, 10, 0, 0, 0, NULL);
+    ok(hWnd2 != 0, "CreateWindowExA error %ld\n", GetLastError());
+    trace("hWnd2 = %p\n", hWnd2);
+
+    SetLastError(0xdeadbeef);
+    ok(!CloseClipboard(), "CloseClipboard should fail if clipboard wasn't open\n");
+    test_last_error(ERROR_CLIPBOARD_NOT_OPEN);
+
+    ok(OpenClipboard(0), "OpenClipboard failed\n");
+    ok(!GetClipboardOwner(), "clipboard should still be not owned\n");
+    ok(!OpenClipboard(hWnd1), "OpenClipboard should fail since clipboard already opened\n");
+    ret = CloseClipboard();
+    ok( ret, "CloseClipboard error %ld\n", GetLastError());
+
+    ok(OpenClipboard(hWnd1), "OpenClipboard failed\n");
+
+    SetLastError(0xdeadbeef);
+    ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef,
+       "OpenClipboard should fail without setting last error value\n");
+
+    SetLastError(0xdeadbeef);
+    ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should still be not owned\n");
+    ret = EmptyClipboard();
+    ok( ret, "EmptyClipboard error %ld\n", GetLastError());
+    ok(GetClipboardOwner() == hWnd1, "clipboard should be owned by %p, not by %p\n", hWnd1, GetClipboardOwner());
+
+    SetLastError(0xdeadbeef);
+    ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef,
+       "OpenClipboard should fail without setting last error value\n");
+
+    ret = CloseClipboard();
+    ok( ret, "CloseClipboard error %ld\n", GetLastError());
+    ok(GetClipboardOwner() == hWnd1, "clipboard should still be owned\n");
+
+    ret = DestroyWindow(hWnd1);
+    ok( ret, "DestroyWindow error %ld\n", GetLastError());
+    ret = DestroyWindow(hWnd2);
+    ok( ret, "DestroyWindow error %ld\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should not be owned\n");
+}
+
+static void test_RegisterClipboardFormatA(void)
+{
+    ATOM atom_id;
+    UINT format_id, format_id2;
+    char buf[256];
+    int len;
+    BOOL ret;
+
+    format_id = RegisterClipboardFormatA("my_cool_clipboard_format");
+    ok(format_id > 0xc000 && format_id < 0xffff, "invalid clipboard format id %04x\n", format_id);
+
+    format_id2 = RegisterClipboardFormatA("MY_COOL_CLIPBOARD_FORMAT");
+    ok(format_id2 == format_id, "invalid clipboard format id %04x\n", format_id2);
+
+    len = GetClipboardFormatNameA(format_id, buf, 256);
+    ok(len == lstrlenA("my_cool_clipboard_format"), "wrong format name length %d\n", len);
+    ok(!lstrcmpA(buf, "my_cool_clipboard_format"), "wrong format name \"%s\"\n", buf);
+
+    lstrcpyA(buf, "foo");
+    SetLastError(0xdeadbeef);
+    len = GetAtomNameA((ATOM)format_id, buf, 256);
+    ok(len == 0, "GetAtomNameA should fail\n");
+    test_last_error(ERROR_INVALID_HANDLE);
+
+todo_wine
+{
+    lstrcpyA(buf, "foo");
+    SetLastError(0xdeadbeef);
+    len = GlobalGetAtomNameA((ATOM)format_id, buf, 256);
+    ok(len == 0, "GlobalGetAtomNameA should fail\n");
+    test_last_error(ERROR_INVALID_HANDLE);
+}
+
+    SetLastError(0xdeadbeef);
+    atom_id = FindAtomA("my_cool_clipboard_format");
+    ok(atom_id == 0, "FindAtomA should fail\n");
+    test_last_error(ERROR_FILE_NOT_FOUND);
+
+#if 0
+    /* this relies on the clipboard and global atom table being different */
+    SetLastError(0xdeadbeef);
+    atom_id = GlobalFindAtomA("my_cool_clipboard_format");
+    ok(atom_id == 0, "GlobalFindAtomA should fail\n");
+    test_last_error(ERROR_FILE_NOT_FOUND);
+
+    for (format_id = 0; format_id < 0xffff; format_id++)
+    {
+        SetLastError(0xdeadbeef);
+        len = GetClipboardFormatNameA(format_id, buf, 256);
+
+        if (format_id < 0xc000)
+        {
+            ok(!len, "GetClipboardFormatNameA should fail, but it returned %d (%s)\n", len, buf);
+            test_last_error(ERROR_INVALID_PARAMETER);
+        }
+        else
+        {
+            if (len)
+                trace("%04x: %s\n", format_id, len ? buf : "");
+            else
+                test_last_error(ERROR_INVALID_HANDLE);
+        }
+    }
+#endif
+
+    ret = OpenClipboard(0);
+    ok( ret, "OpenClipboard error %ld\n", GetLastError());
+
+    trace("# of formats available: %d\n", CountClipboardFormats());
+
+    format_id = 0;
+    while ((format_id = EnumClipboardFormats(format_id)))
+    {
+        ok(IsClipboardFormatAvailable(format_id), "format %04x was listed as available\n", format_id);
+        len = GetClipboardFormatNameA(format_id, buf, 256);
+        trace("%04x: %s\n", format_id, len ? buf : "");
+    }
+
+    ret = EmptyClipboard();
+    ok( ret, "EmptyClipboard error %ld\n", GetLastError());
+    ret =CloseClipboard();
+    ok( ret, "CloseClipboard error %ld\n", GetLastError());
+
+    if (CountClipboardFormats())
+    {
+        SetLastError(0xdeadbeef);
+        ok(!EnumClipboardFormats(0), "EnumClipboardFormats should fail if clipboard wasn't open\n");
+        ok(GetLastError() == ERROR_CLIPBOARD_NOT_OPEN,
+           "Last error should be set to ERROR_CLIPBOARD_NOT_OPEN, not %ld\n", GetLastError());
+    }
+
+    SetLastError(0xdeadbeef);
+    ok(!EmptyClipboard(), "EmptyClipboard should fail if clipboard wasn't open\n");
+    test_last_error(ERROR_CLIPBOARD_NOT_OPEN);
+}
+
+START_TEST(clipboard)
+{
+    SetLastError(0xdeadbeef);
+    FindAtomW(NULL);
+    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) is_win9x = TRUE;
+
+    test_RegisterClipboardFormatA();
+    test_ClipboardOwner();
+}
diff --git a/reactos/regtests/winetests/user32/dce.c b/reactos/regtests/winetests/user32/dce.c
new file mode 100755 (executable)
index 0000000..76beef2
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Unit tests for DCE support
+ *
+ * Copyright 2005 Alexandre Julliard
+ *
+ * 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
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#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"
+
+static HWND hwnd_cache, hwnd_owndc, hwnd_classdc, hwnd_classdc2;
+
+/* test behavior of DC attributes with various GetDC/ReleaseDC combinations */
+static void test_dc_attributes(void)
+{
+    HDC hdc, old_hdc;
+    INT rop, def_rop;
+
+    /* test cache DC */
+
+    hdc = GetDC( hwnd_cache );
+    def_rop = GetROP2( hdc );
+
+    SetROP2( hdc, R2_WHITE );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+    ReleaseDC( hwnd_cache, hdc );
+    hdc = GetDC( hwnd_cache );
+    rop = GetROP2( hdc );
+    ok( rop == def_rop, "wrong ROP2 %d after release\n", rop );
+    SetROP2( hdc, R2_WHITE );
+    ReleaseDC( hwnd_cache, hdc );
+
+    hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+    rop = GetROP2( hdc );
+    /* Win9x seems to silently ignore DCX_NORESETATTRS */
+    ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+    SetROP2( hdc, R2_WHITE );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+    ReleaseDC( hwnd_cache, hdc );
+    hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+    rop = GetROP2( hdc );
+    ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+    ReleaseDC( hwnd_cache, hdc );
+
+    hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE );
+    rop = GetROP2( hdc );
+    ok( rop == def_rop, "wrong ROP2 %d after release\n", rop );
+    ReleaseDC( hwnd_cache, hdc );
+
+    /* test own DC */
+
+    hdc = GetDC( hwnd_owndc );
+    SetROP2( hdc, R2_WHITE );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+    old_hdc = hdc;
+    ReleaseDC( hwnd_owndc, hdc );
+    hdc = GetDC( hwnd_owndc );
+    ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+    ReleaseDC( hwnd_owndc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
+
+    /* test class DC */
+
+    hdc = GetDC( hwnd_classdc );
+    SetROP2( hdc, R2_WHITE );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+    old_hdc = hdc;
+    ReleaseDC( hwnd_classdc, hdc );
+    hdc = GetDC( hwnd_classdc );
+    ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+    ReleaseDC( hwnd_classdc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
+
+    /* test class DC with 2 windows */
+
+    old_hdc = GetDC( hwnd_classdc );
+    SetROP2( old_hdc, R2_BLACK );
+    hdc = GetDC( hwnd_classdc2 );
+    ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_BLACK, "wrong ROP2 %d for other window\n", rop );
+    ReleaseDC( hwnd_classdc, old_hdc );
+    ReleaseDC( hwnd_classdc, hdc );
+    rop = GetROP2( hdc );
+    ok( rop == R2_BLACK, "wrong ROP2 %d after release\n", rop );
+}
+
+
+/* test behavior with various invalid parameters */
+static void test_parameters(void)
+{
+    HDC hdc;
+
+    hdc = GetDC( hwnd_cache );
+    ok( ReleaseDC( hwnd_owndc, hdc ), "ReleaseDC with wrong window should succeed\n" );
+
+    hdc = GetDC( hwnd_cache );
+    ok( !ReleaseDC( hwnd_cache, 0 ), "ReleaseDC with wrong HDC should fail\n" );
+    ok( ReleaseDC( hwnd_cache, hdc ), "correct ReleaseDC should succeed\n" );
+    ok( !ReleaseDC( hwnd_cache, hdc ), "second ReleaseDC should fail\n" );
+
+    hdc = GetDC( hwnd_owndc );
+    ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
+    hdc = GetDC( hwnd_owndc );
+    ok( ReleaseDC( hwnd_owndc, hdc ), "correct ReleaseDC should succeed\n" );
+    ok( ReleaseDC( hwnd_owndc, hdc ), "second ReleaseDC should succeed\n" );
+
+    hdc = GetDC( hwnd_classdc );
+    ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
+    hdc = GetDC( hwnd_classdc );
+    ok( ReleaseDC( hwnd_classdc, hdc ), "correct ReleaseDC should succeed\n" );
+    ok( ReleaseDC( hwnd_classdc, hdc ), "second ReleaseDC should succeed\n" );
+}
+
+
+static void test_dc_visrgn(void)
+{
+    HDC old_hdc, hdc;
+    HRGN hrgn, hrgn2;
+    RECT rect;
+
+    /* cache DC */
+
+    SetRect( &rect, 10, 10, 20, 20 );
+    MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
+    hrgn = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_cache, hdc );
+    ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+
+    /* cache DC with NORESETATTRS */
+
+    SetRect( &rect, 10, 10, 20, 20 );
+    MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
+    hrgn = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE | DCX_NORESETATTRS );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_cache, hdc );
+    ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+    hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+        "clip box sould have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ReleaseDC( hwnd_cache, hdc );
+
+    /* window DC */
+
+    SetRect( &rect, 10, 10, 20, 20 );
+    MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
+    hrgn = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_owndc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_owndc, hdc );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    hdc = GetDCEx( hwnd_owndc, 0, DCX_USESTYLE );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_owndc, hdc );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+
+    SetRect( &rect, 20, 20, 30, 30 );
+    MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
+    hrgn2 = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_owndc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
+    ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+    ReleaseDC( hwnd_owndc, hdc );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+    hdc = GetDCEx( hwnd_owndc, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
+    ok( GetRgnBox( hrgn2, &rect ) == ERROR, "region must no longer be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+        "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ReleaseDC( hwnd_owndc, hdc );
+
+    /* class DC */
+
+    SetRect( &rect, 10, 10, 20, 20 );
+    MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
+    hrgn = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_classdc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_classdc, hdc );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+    hdc = GetDCEx( hwnd_classdc, 0, DCX_USESTYLE );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+    ReleaseDC( hwnd_classdc, hdc );
+    ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+
+    SetRect( &rect, 20, 20, 30, 30 );
+    MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
+    hrgn2 = CreateRectRgnIndirect( &rect );
+    hdc = GetDCEx( hwnd_classdc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
+    ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+
+    old_hdc = hdc;
+    hdc = GetDCEx( hwnd_classdc2, 0, DCX_USESTYLE );
+    ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+        "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ReleaseDC( hwnd_classdc2, hdc );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+    hdc = GetDCEx( hwnd_classdc2, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
+    ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+    ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+        "clip box must have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ReleaseDC( hwnd_classdc2, hdc );
+}
+
+
+/* test various BeginPaint/EndPaint behaviors */
+static void test_begin_paint(void)
+{
+    HDC old_hdc, hdc;
+    RECT rect;
+    PAINTSTRUCT ps;
+
+    /* cache DC */
+
+    /* clear update region */
+    RedrawWindow( hwnd_cache, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+    SetRect( &rect, 10, 10, 20, 20 );
+    RedrawWindow( hwnd_cache, &rect, 0, RDW_INVALIDATE );
+    hdc = BeginPaint( hwnd_cache, &ps );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    EndPaint( hwnd_cache, &ps );
+
+    /* window DC */
+
+    RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+    SetRect( &rect, 10, 10, 20, 20 );
+    RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE );
+    hdc = BeginPaint( hwnd_owndc, &ps );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ReleaseDC( hwnd_owndc, hdc );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    EndPaint( hwnd_owndc, &ps );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+        "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+    SetRect( &rect, 10, 10, 20, 20 );
+    RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE|RDW_ERASE );
+    ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+        "clip box should be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+    RedrawWindow( hwnd_owndc, NULL, 0, RDW_ERASENOW );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+        "clip box should still be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+    /* class DC */
+
+    RedrawWindow( hwnd_classdc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+    SetRect( &rect, 10, 10, 20, 20 );
+    RedrawWindow( hwnd_classdc, &rect, 0, RDW_INVALIDATE );
+    hdc = BeginPaint( hwnd_classdc, &ps );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+        "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+    old_hdc = hdc;
+    hdc = GetDC( hwnd_classdc2 );
+    ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
+    SetRectEmpty( &rect );
+    GetClipBox( hdc, &rect );
+    ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+        "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+}
+
+
+START_TEST(dce)
+{
+    WNDCLASSA cls;
+
+    cls.style = CS_DBLCLKS;
+    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 = "cache_class";
+    RegisterClassA(&cls);
+    cls.style = CS_DBLCLKS | CS_OWNDC;
+    cls.lpszClassName = "owndc_class";
+    RegisterClassA(&cls);
+    cls.style = CS_DBLCLKS | CS_CLASSDC;
+    cls.lpszClassName = "classdc_class";
+    RegisterClassA(&cls);
+
+    hwnd_cache = CreateWindowA("cache_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+                               0, 0, 100, 100,
+                               0, 0, GetModuleHandleA(0), NULL );
+    hwnd_owndc = CreateWindowA("owndc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+                               0, 200, 100, 100,
+                               0, 0, GetModuleHandleA(0), NULL );
+    hwnd_classdc = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+                                 200, 0, 100, 100,
+                                 0, 0, GetModuleHandleA(0), NULL );
+    hwnd_classdc2 = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+                                  200, 200, 100, 100,
+                                  0, 0, GetModuleHandleA(0), NULL );
+    test_dc_attributes();
+    test_parameters();
+    test_dc_visrgn();
+    test_begin_paint();
+}
diff --git a/reactos/regtests/winetests/user32/dde.c b/reactos/regtests/winetests/user32/dde.c
new file mode 100755 (executable)
index 0000000..deacc78
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Unit tests for DDE functions
+ *
+ * Copyright (c) 2004 Dmitry Timoshkov
+ *
+ * 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 "wine/test.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "dde.h"
+#include "ddeml.h"
+#include "winerror.h"
+
+static const WCHAR TEST_DDE_SERVICE[] = {'T','e','s','t','D','D','E','S','e','r','v','i','c','e',0};
+
+static const char exec_cmdA[] = "ANSI dde command";
+static const WCHAR exec_cmdW[] = {'u','n','i','c','o','d','e',' ','d','d','e',' ','c','o','m','m','a','n','d',0};
+
+static WNDPROC old_dde_client_wndproc;
+
+LRESULT WINAPI hook_dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    UINT_PTR lo, hi;
+
+    trace("hook_dde_client_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam);
+
+    switch (msg)
+    {
+    case WM_DDE_ACK:
+        UnpackDDElParam(WM_DDE_ACK, lparam, &lo, &hi);
+        trace("WM_DDE_ACK: status %04x hglobal %p\n", lo, (HGLOBAL)hi);
+        break;
+
+    default:
+        break;
+    }
+    return CallWindowProcA(old_dde_client_wndproc, hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI dde_server_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    trace("dde_server_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam);
+
+    switch (msg)
+    {
+    case WM_DDE_INITIATE:
+    {
+        ATOM aService = GlobalAddAtomW(TEST_DDE_SERVICE);
+
+        trace("server: got WM_DDE_INITIATE from %p with %08lx\n", (HWND)wparam, lparam);
+
+        if (LOWORD(lparam) == aService)
+        {
+            ok(!IsWindowUnicode((HWND)wparam), "client should be an ANSI window\n");
+            old_dde_client_wndproc = (WNDPROC)SetWindowLongPtrA((HWND)wparam, GWLP_WNDPROC, (ULONG_PTR)hook_dde_client_wndproc);
+            trace("server: sending WM_DDE_ACK to %p\n", (HWND)wparam);
+            SendMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, MAKELPARAM(aService, 0));
+        }
+        else
+            GlobalDeleteAtom(aService);
+        return 0;
+    }
+
+    case WM_DDE_EXECUTE:
+    {
+        DDEACK ack;
+        WORD status;
+        LPCSTR cmd;
+        UINT_PTR lo, hi;
+
+        trace("server: got WM_DDE_EXECUTE from %p with %08lx\n", (HWND)wparam, lparam);
+
+        UnpackDDElParam(WM_DDE_EXECUTE, lparam, &lo, &hi);
+        trace("%08lx => lo %04x hi %04x\n", lparam, lo, hi);
+
+        ack.bAppReturnCode = 0;
+        ack.reserved = 0;
+        ack.fBusy = 0;
+
+        cmd = GlobalLock((HGLOBAL)hi);
+
+        if (!cmd || (lstrcmpW((LPCWSTR)cmd, exec_cmdW) && lstrcmpA(cmd, exec_cmdA)))
+        {
+            trace("ignoring unknown WM_DDE_EXECUTE command\n");
+            /* We have to send a negative acknowledge even if we don't
+             * accept the command, otherwise Windows goes mad and next time
+             * we send an acknowledge DDEML drops the connection.
+             * Not sure how to call it: a bug or a feature.
+             */
+            ack.fAck = 0;
+        }
+        else
+            ack.fAck = 1;
+        GlobalUnlock((HGLOBAL)hi);
+
+        trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam);
+
+        status = *((WORD *)&ack);
+        lparam = ReuseDDElParam(lparam, WM_DDE_EXECUTE, WM_DDE_ACK, status, hi);
+
+        PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam);
+        return 0;
+    }
+
+    case WM_DDE_TERMINATE:
+    {
+        DDEACK ack;
+        WORD status;
+
+        trace("server: got WM_DDE_TERMINATE from %p with %08lx\n", (HWND)wparam, lparam);
+
+        ack.bAppReturnCode = 0;
+        ack.reserved = 0;
+        ack.fBusy = 0;
+        ack.fAck = 1;
+
+        trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam);
+
+        status = *((WORD *)&ack);
+        lparam = PackDDElParam(WM_DDE_ACK, status, 0);
+
+        PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam);
+        return 0;
+    }
+
+    default:
+        break;
+    }
+
+    return DefWindowProcW(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static BOOL create_dde_windows(HWND *hwnd_client, HWND *hwnd_server)
+{
+    WNDCLASSA wcA;
+    WNDCLASSW wcW;
+    static const WCHAR server_class_name[] = {'d','d','e','_','s','e','r','v','e','r','_','w','i','n','d','o','w',0};
+    static const char client_class_name[] = "dde_client_window";
+
+    memset(&wcW, 0, sizeof(wcW));
+    wcW.lpfnWndProc = dde_server_wndproc;
+    wcW.lpszClassName = server_class_name;
+    wcW.hInstance = GetModuleHandleA(0);
+    if (!RegisterClassW(&wcW)) return FALSE;
+
+    memset(&wcA, 0, sizeof(wcA));
+    wcA.lpfnWndProc = dde_client_wndproc;
+    wcA.lpszClassName = client_class_name;
+    wcA.hInstance = GetModuleHandleA(0);
+    assert(RegisterClassA(&wcA));
+
+    *hwnd_server = CreateWindowExW(0, server_class_name, NULL,
+                                   WS_POPUP,
+                                   100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+                                   GetDesktopWindow(), 0,
+                                   GetModuleHandleA(0), NULL);
+    assert(*hwnd_server);
+
+    *hwnd_client = CreateWindowExA(0, client_class_name, NULL,
+                                   WS_POPUP,
+                                   100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+                                   GetDesktopWindow(), 0,
+                                   GetModuleHandleA(0), NULL);
+    assert(*hwnd_client);
+
+    trace("server hwnd %p, client hwnd %p\n", *hwnd_server, *hwnd_client);
+
+    ok(IsWindowUnicode(*hwnd_server), "server has to be a unicode window\n");
+    ok(!IsWindowUnicode(*hwnd_client), "client has to be an ANSI window\n");
+
+    return TRUE;
+}
+
+static HDDEDATA CALLBACK client_dde_callback(UINT uType, UINT uFmt, HCONV hconv,
+                                     HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+                                     ULONG_PTR dwData1, ULONG_PTR dwData2)
+{
+    static const char * const cmd_type[15] = {
+        "XTYP_ERROR", "XTYP_ADVDATA", "XTYP_ADVREQ", "XTYP_ADVSTART",
+        "XTYP_ADVSTOP", "XTYP_EXECUTE", "XTYP_CONNECT", "XTYP_CONNECT_CONFIRM",
+        "XTYP_XACT_COMPLETE", "XTYP_POKE", "XTYP_REGISTER", "XTYP_REQUEST",
+        "XTYP_DISCONNECT", "XTYP_UNREGISTER", "XTYP_WILDCONNECT" };
+    UINT type;
+    const char *cmd_name;
+
+    type = (uType & XTYP_MASK) >> XTYP_SHIFT;
+    cmd_name = (type >= 0 && type <= 14) ? cmd_type[type] : "unknown";
+
+    trace("client_dde_callback: %04x (%s) %d %p %p %p %p %08lx %08lx\n",
+          uType, cmd_name, uFmt, hconv, hsz1, hsz2, hdata, dwData1, dwData2);
+    return 0;
+}
+
+static void test_dde_transaction(void)
+{
+    HSZ hsz_server;
+    DWORD dde_inst, ret, err;
+    HCONV hconv;
+    HWND hwnd_client, hwnd_server;
+    CONVINFO info;
+    HDDEDATA hdata;
+    static const char test_cmd[] = "test dde command";
+
+    /* server: unicode, client: ansi */
+    if (!create_dde_windows(&hwnd_client, &hwnd_server)) return;
+
+    dde_inst = 0;
+    ret = DdeInitializeA(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+    ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%x)\n",
+       ret, DdeGetLastError(dde_inst));
+
+    hsz_server = DdeCreateStringHandleW(dde_inst, TEST_DDE_SERVICE, CP_WINUNICODE);
+
+    hconv = DdeConnect(dde_inst, hsz_server, 0, NULL);
+    ok(hconv != 0, "DdeConnect error %x\n", DdeGetLastError(dde_inst));
+    err = DdeGetLastError(dde_inst);
+    ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+    info.cb = sizeof(info);
+    ret = DdeQueryConvInfo(hconv, QID_SYNC, &info);
+    ok(ret, "wrong info size %ld, DdeQueryConvInfo error %x\n", ret, DdeGetLastError(dde_inst));
+    /* should be CP_WINANSI since we used DdeInitializeA */
+    ok(info.ConvCtxt.iCodePage == CP_WINANSI, "wrong iCodePage %d\n", info.ConvCtxt.iCodePage);
+    ok(!info.hConvPartner, "unexpected info.hConvPartner: %p\n", info.hConvPartner);
+todo_wine {
+    ok((info.wStatus & DDE_FACK), "unexpected info.wStatus: %04x\n", info.wStatus);
+}
+    ok((info.wStatus & (ST_CONNECTED | ST_CLIENT)) == (ST_CONNECTED | ST_CLIENT), "unexpected info.wStatus: %04x\n", info.wStatus);
+    ok(info.wConvst == XST_CONNECTED, "unexpected info.wConvst: %04x\n", info.wConvst);
+    ok(info.wType == 0, "unexpected info.wType: %04x\n", info.wType);
+
+    trace("hwnd %p, hwndPartner %p\n", info.hwnd, info.hwndPartner);
+
+    trace("sending test client transaction command\n");
+    ret = 0xdeadbeef;
+    hdata = DdeClientTransaction((LPBYTE)test_cmd, strlen(test_cmd) + 1, hconv, (HSZ)0xdead, 0xbeef, XTYP_EXECUTE, 1000, &ret);
+    ok(!hdata, "DdeClientTransaction succeeded\n");
+    ok(ret == DDE_FNOTPROCESSED, "wrong status code %04lx\n", ret);
+    err = DdeGetLastError(dde_inst);
+    ok(err == DMLERR_NOTPROCESSED, "wrong dde error %lx\n", err);
+
+    trace("sending ANSI client transaction command\n");
+    ret = 0xdeadbeef;
+    hdata = DdeClientTransaction((LPBYTE)exec_cmdA, lstrlenA(exec_cmdA) + 1, hconv, 0, 0, XTYP_EXECUTE, 1000, &ret);
+    ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst));
+    ok(ret == DDE_FACK, "wrong status code %04lx\n", ret);
+
+    err = DdeGetLastError(dde_inst);
+    ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+    trace("sending unicode client transaction command\n");
+    ret = 0xdeadbeef;
+    hdata = DdeClientTransaction((LPBYTE)exec_cmdW, (lstrlenW(exec_cmdW) + 1) * sizeof(WCHAR), hconv, 0, 0, XTYP_EXECUTE, 1000, &ret);
+    ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst));
+    ok(ret == DDE_FACK, "wrong status code %04lx\n", ret);
+    err = DdeGetLastError(dde_inst);
+    ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+    ok(DdeDisconnect(hconv), "DdeDisconnect error %x\n", DdeGetLastError(dde_inst));
+
+    info.cb = sizeof(info);
+    ret = DdeQueryConvInfo(hconv, QID_SYNC, &info);
+    ok(!ret, "DdeQueryConvInfo should fail\n");
+    err = DdeGetLastError(dde_inst);
+todo_wine {
+    ok(err == DMLERR_INVALIDPARAMETER, "wrong dde error %lx\n", err);
+}
+
+    ok(DdeFreeStringHandle(dde_inst, hsz_server), "DdeFreeStringHandle error %x\n", DdeGetLastError(dde_inst));
+
+    /* This call hangs on win2k SP4.
+    DdeUninitialize(dde_inst);*/
+
+    DestroyWindow(hwnd_client);
+    DestroyWindow(hwnd_server);
+
+    DdeUninitialize(dde_inst);
+}
+
+static void test_DdeCreateStringHandleW(DWORD dde_inst, int codepage)
+{
+    static const WCHAR dde_string[] = {'D','D','E',' ','S','t','r','i','n','g',0};
+    HSZ str_handle;
+    WCHAR bufW[256];
+    char buf[256];
+    int ret;
+
+    str_handle = DdeCreateStringHandleW(dde_inst, dde_string, codepage);
+    ok(str_handle != 0, "DdeCreateStringHandleW failed with error %08x\n",
+       DdeGetLastError(dde_inst));
+
+    ret = DdeQueryStringW(dde_inst, str_handle, NULL, 0, codepage);
+    if (codepage == CP_WINANSI)
+        ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret);
+    else
+        ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret);
+
+    ret = DdeQueryStringW(dde_inst, str_handle, bufW, 256, codepage);
+    if (codepage == CP_WINANSI)
+    {
+        ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret);
+        ok(!lstrcmpA("D", (LPCSTR)bufW), "DdeQueryStringW returned wrong string\n");
+    }
+    else
+    {
+        ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret);
+        ok(!lstrcmpW(dde_string, bufW), "DdeQueryStringW returned wrong string\n");
+    }
+
+    ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINANSI);
+    if (codepage == CP_WINANSI)
+    {
+        ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret);
+        ok(!lstrcmpA("D", buf), "DdeQueryStringW returned wrong string\n");
+    }
+    else
+    {
+        ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret);
+        ok(!lstrcmpA("DDE String", buf), "DdeQueryStringA returned wrong string %s\n", buf);
+    }
+
+    ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINUNICODE);
+    if (codepage == CP_WINANSI)
+    {
+        ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret);
+        ok(!lstrcmpA("D", buf), "DdeQueryStringA returned wrong string %s\n", buf);
+    }
+    else
+    {
+        ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret);
+        ok(!lstrcmpW(dde_string, (LPCWSTR)buf), "DdeQueryStringW returned wrong string\n");
+    }
+
+    ok(DdeFreeStringHandle(dde_inst, str_handle), "DdeFreeStringHandle failed\n");
+}
+
+static void test_DdeCreateStringHandle(void)
+{
+    DWORD dde_inst, ret;
+
+    dde_inst = 0xdeadbeef;
+    SetLastError(0xdeadbeef);
+    ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        trace("Skipping the DDE test on a Win9x platform\n");
+        return;
+    }
+
+    ok(ret == DMLERR_INVALIDPARAMETER, "DdeInitializeW should fail, but got %04lx instead\n", ret);
+    ok(DdeGetLastError(dde_inst) == DMLERR_INVALIDPARAMETER, "expected DMLERR_INVALIDPARAMETER\n");
+
+    dde_inst = 0;
+    ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+    ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%08x)\n",
+       ret, DdeGetLastError(dde_inst));
+
+    test_DdeCreateStringHandleW(dde_inst, 0);
+    test_DdeCreateStringHandleW(dde_inst, CP_WINUNICODE);
+    test_DdeCreateStringHandleW(dde_inst, CP_WINANSI);
+
+    ok(DdeUninitialize(dde_inst), "DdeUninitialize failed\n");
+}
+
+START_TEST(dde)
+{
+   test_DdeCreateStringHandle();
+   test_dde_transaction();
+}
diff --git a/reactos/regtests/winetests/user32/dialog.c b/reactos/regtests/winetests/user32/dialog.c
new file mode 100755 (executable)
index 0000000..d55eef4
--- /dev/null
@@ -0,0 +1,876 @@
+/* Unit test suite for the dialog functions.
+ *
+ * Copyright 2004 Bill Medland
+ *
+ * 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
+ *
+ *
+ *
+ * This test suite currently works by building a quite complex hierarchy of
+ * objects in a variety of styles and then performs a limited number of tests
+ * for the previous and next dialog group or tab items.
+ *
+ * The test specifically does not test all possibilities at this time since
+ * there are several cases where the Windows behaviour is rather strange and
+ * significant work would be required to get the Wine code to duplicate the
+ * strangeness, especially since most are in situations that would not
+ * normally be met.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+
+#define MAXHWNDS 1024
+static HWND hwnd [MAXHWNDS];
+static unsigned int numwnds=1; /* 0 is reserved for null */
+
+/* Global handles */
+static HINSTANCE g_hinst;                          /* This application's HINSTANCE */
+static HWND g_hwndMain, g_hwndButton1, g_hwndButton2, g_hwndButtonCancel;
+static HWND g_hwndTestDlg, g_hwndTestDlgBut1, g_hwndTestDlgBut2, g_hwndTestDlgEdit;
+static HWND g_hwndInitialFocusT1, g_hwndInitialFocusT2, g_hwndInitialFocusGroupBox;
+
+static LONG g_styleInitialFocusT1, g_styleInitialFocusT2;
+static BOOL g_bInitialFocusInitDlgResult;
+
+static int g_terminated;
+
+typedef struct {
+    unsigned int id;
+    int parent;
+    DWORD style;
+    DWORD exstyle;
+} h_entry;
+
+static const h_entry hierarchy [] = {
+    /* 0 is reserved for the null window */
+    {  1,  0, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE},
+    { 20,  1,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    {  2,  1,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+    { 60,  2,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    /* What happens with groups when the parent is disabled */
+    {  8,  2,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, WS_EX_CONTROLPARENT},
+    { 85,  8,  WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP, 0},
+    {  9,  8,  WS_CHILD, WS_EX_CONTROLPARENT},
+    { 86,  9,  WS_CHILD | WS_VISIBLE, 0},
+    { 87,  9,  WS_CHILD | WS_VISIBLE, 0},
+    { 31,  8,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 10,  2,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+    { 88, 10,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 11, 10,  WS_CHILD, WS_EX_CONTROLPARENT},
+    { 89, 11,  WS_CHILD | WS_VISIBLE, 0},
+    { 32, 11,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 90, 11,  WS_CHILD | WS_VISIBLE, 0},
+    { 33, 10,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 21,  2,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 61,  2,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    {  3,  1,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+    { 22,  3,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 62,  3,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    {  7,  3,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+    {  4,  7,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+    { 83,  4,  WS_CHILD | WS_VISIBLE, 0},
+    {  5,  4,  WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+    /* A couple of controls around the main dialog */
+    { 29,  5,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 81,  5,  WS_CHILD | WS_VISIBLE, 0},
+    /* The main dialog with lots of controls */
+    {  6,  5,  WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+        /* At the start of a dialog */
+        /* Disabled controls are skipped */
+    { 63,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+        /* Invisible controls are skipped */
+    { 64,  6,  WS_CHILD | WS_TABSTOP, 0},
+        /* Invisible disabled controls are skipped */
+    { 65,  6,  WS_CHILD | WS_DISABLED | WS_TABSTOP, 0},
+        /* Non-tabstop controls are skipped for tabs but not for groups */
+    { 66,  6,  WS_CHILD | WS_VISIBLE, 0},
+        /* End of first group, with no tabstops in it */
+    { 23,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+        /* At last a tabstop */
+    { 67,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    /* A group that is totally disabled or invisible */
+    { 24,  6,  WS_CHILD | WS_DISABLED | WS_GROUP, 0},
+    { 68,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+    { 69,  6,  WS_CHILD | WS_TABSTOP, 0},
+    /* A valid group in the middle of the dialog (not the first nor last group*/
+    { 25,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+        /* A non-tabstop item will be skipped for tabs */
+    { 70,  6,  WS_CHILD | WS_VISIBLE, 0},
+        /* A disabled item will be skipped for tabs and groups */
+    { 71,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+        /* A valid item will be found for tabs and groups */
+    { 72,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+        /* A disabled item to skip when looking for the next group item */
+    { 73,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+    /* The next group begins with an enabled visible label */
+    { 26,  6,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    { 74,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    { 75,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    /* That group is terminated by a disabled label */
+    { 27,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP, 0},
+    { 76,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    { 77,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    /* That group is terminated by an invisible label */
+    { 28,  6,  WS_CHILD | WS_GROUP, 0},
+    /* The end of the dialog with item for loop and recursion testing */
+    { 78,  6,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    /* No tabstop so skipped for prev tab, but found for prev group */
+    { 79,  6,  WS_CHILD | WS_VISIBLE, 0},
+    { 80,  6,  WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+    /* A couple of controls after the main dialog */
+    { 82,  5,  WS_CHILD | WS_VISIBLE, 0},
+    { 30,  5,  WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+    /* And around them */
+    { 84,  4,  WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+    {0, 0, 0, 0}
+};
+
+static BOOL CreateWindows (HINSTANCE hinst)
+{
+    const h_entry *p = hierarchy;
+
+    while (p->id != 0)
+    {
+        DWORD style, exstyle;
+        char ctrlname[9];
+
+        /* Basically assert that the hierarchy is valid and track the
+         * maximum control number
+         */
+        if (p->id >= numwnds)
+        {
+            if (p->id >=  sizeof(hwnd)/sizeof(hwnd[0]))
+            {
+                trace ("Control %d is out of range\n", p->id);
+                return FALSE;
+            }
+            else
+                numwnds = p->id+1;
+        }
+        if (p->id <= 0)
+        {
+            trace ("Control %d is out of range\n", p->id);
+            return FALSE;
+        }
+        if (hwnd[p->id] != 0)
+        {
+            trace ("Control %d is used more than once\n", p->id);
+            return FALSE;
+        }
+
+        /* Create the control */
+        sprintf (ctrlname, "ctrl%4.4d", p->id);
+        hwnd[p->id] = CreateWindowEx (p->exstyle, TEXT(p->parent ? "static" : "GetNextDlgItemWindowClass"), TEXT(ctrlname), p->style, 10, 10, 10, 10, hwnd[p->parent], p->parent ? (HMENU) (2000 + p->id) : 0, hinst, 0);
+        if (!hwnd[p->id])
+        {
+            trace ("Failed to create control %d\n", p->id);
+            return FALSE;
+        }
+
+        /* Check that the styles are as we specified (except the main one
+         * which is quite frequently messed up).  If this keeps breaking then
+         * we could mask out the bits that don't concern us.
+         */
+        if (p->parent)
+        {
+            style = GetWindowLong (hwnd[p->id], GWL_STYLE);
+            exstyle = GetWindowLong (hwnd[p->id], GWL_EXSTYLE);
+            if (style == p->style && exstyle == p->exstyle)
+            {
+                trace ("Style mismatch at %d: %8.8lx %8.8lx cf %8.8lx %8.8lx\n", p->id, style, exstyle, p->style, p->exstyle);
+            }
+        }
+        p++;
+    }
+
+    return TRUE;
+}
+
+/* Form the lParam of a WM_KEYDOWN message */
+static DWORD KeyDownData (int repeat, int scancode, int extended, int wasdown)
+{
+    return ((repeat & 0x0000FFFF) | ((scancode & 0x00FF) >> 16) |
+            (extended ? 0x01000000 : 0) | (wasdown ? 0x40000000 : 0));
+}
+
+/* Form a WM_KEYDOWN VK_TAB message to the specified window */
+static void FormTabMsg (MSG *pMsg, HWND hwnd)
+{
+    pMsg->hwnd = hwnd;
+    pMsg->message = WM_KEYDOWN;
+    pMsg->wParam = VK_TAB;
+    pMsg->lParam = KeyDownData (1, 0x0F, 0, 0);
+    /* pMsg->time is not set.  It shouldn't be needed */
+    /* pMsg->pt is ignored */
+}
+
+/* Form a WM_KEYDOWN VK_RETURN message to the specified window */
+static void FormEnterMsg (MSG *pMsg, HWND hwnd)
+{
+    pMsg->hwnd = hwnd;
+    pMsg->message = WM_KEYDOWN;
+    pMsg->wParam = VK_RETURN;
+    pMsg->lParam = KeyDownData (1, 0x1C, 0, 0);
+    /* pMsg->time is not set.  It shouldn't be needed */
+    /* pMsg->pt is ignored */
+}
+
+/***********************************************************************
+ *
+ * The actual tests
+ */
+
+typedef struct
+{
+    int isok; /* or is it todo */
+    int test;
+    int dlg;
+    int ctl;
+    int tab;
+    int prev;
+    int res;
+} test_record;
+
+static int id (HWND h)
+{
+    unsigned int i;
+    for (i = 0; i < numwnds; i++)
+        if (hwnd[i] == h)
+            return i;
+    return -1;
+}
+
+/* Tests
+ *
+ * Tests 1-8 test the hCtl argument of null or the dialog itself.
+ *
+ *   1. Prev Group of null is null
+ *   2. Prev Tab of null is null
+ *   3. Prev Group of hDlg in hDlg is null
+ *   4. Prev Tab of hDlg in hDlg is null
+ *   5. Next Group of null is first visible enabled child
+ *      Check it skips invisible, diabled and both.
+ *   6. Next Tab of null is first visible enabled tabstop
+ *      Check it skips invisible, disabled, nontabstop, and in combination.
+ *   7. Next Group of hDlg in hDlg is as of null
+ *   8. Next Tab of hDlg in hDlg is as of null
+ *
+ * Tests 9-14 test descent
+ *
+ *   9. DS_CONTROL does not result in descending the hierarchy for Tab Next
+ *  10. DS_CONTROL does not result in descending the hierarchy for Group Next
+ *  11. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Next
+ *  12. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Next
+ *  13. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Prev
+ *  14. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Prev
+ *
+ * Tests 15-24 are the basic Prev/Next Group tests
+ *
+ *  15. Next Group of a visible enabled non-group control is the next visible
+ *      enabled non-group control, if there is one before the next group
+ *  16. Next Group of a visible enabled non-group control wraps around to the
+ *      beginning of the group on finding a control that starts another group.
+ *      Note that the group is in the middle of the dialog.
+ *  17. As 16 except note that the next group is started with a disabled
+ *      visible control.
+ *  18. As 16 except note that the next group is started with an invisible
+ *      enabled control.
+ *  19. Next Group wraps around the controls of the dialog
+ *  20. Next Group is the same even if the initial control is disabled.
+ *  21. Next Group is the same even if the initial control is invisible.
+ *  22. Next Group is the same even if the initial control has the group style
+ *  23. Next Group returns the initial control if there is no visible enabled
+ *      control in the group. (Initial control disabled and not group style).
+ *  24. Prev version of test 16.
+ *      Prev Group of a visible enabled non-group control wraps around to the
+ *      beginning of the group on finding a control that starts the group.
+ *      Note that the group is in the middle of the dialog.
+ *
+ * In tests 25 to 28 the control is sitting under dialogs which do not have
+ * the WS_EX_CONTROLPARENT style and so cannot be reached from the top of
+ * the dialog.
+ *
+ *  25. Next Group of an inaccessible control is as if it were accessible
+ *  26. Prev Group of an inaccessible control begins searching at the highest
+ *      level ancestor that did not permit recursion down the hierarchy
+ *  27. Next Tab of an inaccessible control is as if it were accessible
+ *  28. Prev Tab of an inaccessible control begins searching at the highest
+ *      level ancestor that did not permit recursion down the hierarchy.
+ *
+ * Tests 29- are the basic Tab tests
+ *
+ *  29. Next Tab of a control is the next visible enabled control with the
+ *      Tabstop style (N.B. skips disabled, invisible and non-tabstop)
+ *  30. Prev Tab of a control is the previous visible enabled control with the
+ *      Tabstop style (N.B. skips disabled, invisible and non-tabstop)
+ *  31. Next Tab test with at least two layers of descent and finding the
+ *      result not at the first control.
+ *  32. Next Tab test with at least two layers of descent with the descent and
+ *      control at the start of each level.
+ *  33. Prev Tab test with at least two layers of descent and finding the
+ *      result not at the last control.
+ *  34. Prev Tab test with at least two layers of descent with the descent and
+ *      control at the end of each level.
+ *
+ *  35. Passing NULL may result in the first child being the one returned.
+ *      (group test)
+ *  36. Passing NULL may result in the first child being the one returned.
+ *      (tab test)
+ */
+
+static void GetNextDlgItemTest (void)
+{
+    static test_record test [] =
+    {
+        /* isok test dlg  ctl  tab  prev res  */
+
+        {   1,   1,    6,   0,   0,   1,   0},
+        {   1,   2,    6,   0,   1,   1,   0},
+        {   1,   3,    6,   6,   0,   1,   0},
+        {   1,   4,    6,   6,   1,   1,   0},
+        {   1,   5,    6,   0,   0,   0,  66},
+        {   1,   6,    6,   0,   1,   0,  67},
+        {   1,   7,    6,   6,   0,   0,  66},
+        {   1,   8,    6,   6,   1,   0,  67},
+
+        {   1,   9,    4,  83,   1,   0,  84},
+        {   1,  10,    4,  83,   0,   0,   5},
+        {   1,  11,    5,  81,   1,   0,  67},
+        {   1,  12,    5,  81,   0,   0,  66},
+        {   1,  13,    5,  82,   1,   1,  78},
+
+        {   1,  14,    5,  82,   0,   1,  79},
+        {   1,  15,    6,  70,   0,   0,  72},
+        {   1,  16,    6,  72,   0,   0,  25},
+        {   1,  17,    6,  75,   0,   0,  26},
+        {   1,  18,    6,  77,   0,   0,  76},
+        {   1,  19,    6,  79,   0,   0,  66},
+        {   1,  20,    6,  71,   0,   0,  72},
+        {   1,  21,    6,  64,   0,   0,  66},
+
+        {   1,  22,    6,  25,   0,   0,  70},
+        {   1,  23,    6,  68,   0,   0,  68},
+        {   1,  24,    6,  25,   0,   1,  72},
+        {   1,  25,    1,  70,   0,   0,  72},
+        /*{   0,  26,    1,  70,   0,   1,   3}, Crashes Win95*/
+        {   1,  27,    1,  70,   1,   0,  72},
+        /*{   0,  28,    1,  70,   1,   1,  61}, Crashes Win95*/
+
+        {   1,  29,    6,  67,   1,   0,  72},
+        {   1,  30,    6,  72,   1,   1,  67},
+
+        {   1,  35,    2,   0,   0,   0,  60},
+        {   1,  36,    2,   0,   1,   0,  60},
+
+        {   0,   0,    0,   0,   0,   0,   0}  /* End of test */
+    };
+    const test_record *p = test;
+
+    ok (CreateWindows (g_hinst), "Could not create test windows\n");
+
+    while (p->dlg)
+    {
+        HWND a;
+        a = (p->tab ? GetNextDlgTabItem : GetNextDlgGroupItem) (hwnd[p->dlg], hwnd[p->ctl], p->prev);
+        if (p->isok)
+        {
+            ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was %d instead of %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
+        }
+        else
+        {
+            todo_wine
+            {
+                ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was actually  %d matching expected %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
+            }
+        }
+        p++;
+    }
+}
+
+/*
+ *  OnMainWindowCreate
+ */
+static BOOL OnMainWindowCreate (HWND hwnd, LPCREATESTRUCT lpcs)
+{
+    g_hwndButton1 = CreateWindow (TEXT("button"), TEXT("Button &1"),
+            WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | BS_TEXT,
+            10, 10, 80, 80, hwnd, (HMENU)100, g_hinst, 0);
+    if (!g_hwndButton1) return FALSE;
+
+    g_hwndButton2 = CreateWindow (TEXT("button"), TEXT("Button &2"),
+            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_TEXT,
+            110, 10, 80, 80, hwnd, (HMENU)200, g_hinst, 0);
+    if (!g_hwndButton2) return FALSE;
+
+    g_hwndButtonCancel = CreateWindow (TEXT("button"), TEXT("Cancel"),
+            WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+            210, 10, 80, 80, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
+    if (!g_hwndButtonCancel) return FALSE;
+
+    return TRUE;
+}
+
+
+/*
+ *  OnTestDlgCreate
+ */
+
+static BOOL OnTestDlgCreate (HWND hwnd, LPCREATESTRUCT lpcs)
+{
+    g_hwndTestDlgEdit = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING |
+            WS_EX_RIGHTSCROLLBAR | WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE,
+            TEXT("Edit"), TEXT("Edit"),
+            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL,
+            16,33,184,24, hwnd, (HMENU)101, g_hinst, 0);
+    if (!g_hwndTestDlgEdit) return FALSE;
+
+    g_hwndTestDlgBut1 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+            | WS_EX_NOPARENTNOTIFY,
+            TEXT("button"), TEXT("Button &1"),
+            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+            204,33,30,24, hwnd, (HMENU)201, g_hinst, 0);
+    if (!g_hwndTestDlgBut1) return FALSE;
+
+    g_hwndTestDlgBut2 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+            | WS_EX_NOPARENTNOTIFY, TEXT("button"),
+            TEXT("Button &2"),
+            WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+            90,102,80,24, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
+    if (!g_hwndTestDlgBut2) return FALSE;
+
+    return TRUE;
+}
+
+static LRESULT CALLBACK main_window_procA (HWND hwnd, UINT uiMsg, WPARAM wParam,
+        LPARAM lParam)
+{
+    LRESULT result;
+    switch (uiMsg)
+    {
+        /* Add blank case statements for these to ensure we don't use them
+         * by mistake.
+         */
+        case DM_GETDEFID: break;
+        case DM_SETDEFID: break;
+
+        case WM_CREATE:
+            return (OnMainWindowCreate (hwnd,
+                    (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
+        case WM_COMMAND:
+            if (wParam == IDCANCEL)
+            {
+                g_terminated = TRUE;
+                return 0;
+            }
+            break;
+    }
+
+    result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
+    return result;
+}
+
+static LRESULT CALLBACK testDlgWinProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
+        LPARAM lParam)
+{
+    LRESULT result;
+    switch (uiMsg)
+    {
+        /* Add blank case statements for these to ensure we don't use them
+         * by mistake.
+         */
+        case DM_GETDEFID: break;
+        case DM_SETDEFID: break;
+
+        case WM_CREATE:
+            return (OnTestDlgCreate (hwnd,
+                    (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
+    }
+
+    result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
+    return result;
+}
+
+static BOOL RegisterWindowClasses (void)
+{
+    WNDCLASSA cls;
+
+    cls.style = 0;
+    cls.lpfnWndProc = DefWindowProcA;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = g_hinst;
+    cls.hIcon = NULL;
+    cls.hCursor = LoadCursorA (NULL, IDC_ARROW);
+    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    cls.lpszMenuName = NULL;
+    cls.lpszClassName = "GetNextDlgItemWindowClass";
+
+    if (!RegisterClassA (&cls)) return FALSE;
+
+    cls.lpfnWndProc = main_window_procA;
+    cls.lpszClassName = "IsDialogMessageWindowClass";
+
+    if (!RegisterClassA (&cls)) return FALSE;
+
+    GetClassInfoA(0, "#32770", &cls);
+    cls.lpfnWndProc = testDlgWinProc;
+    cls.lpszClassName = "WM_NEXTDLGCTLWndClass";
+    if (!RegisterClassA (&cls)) return FALSE;
+
+    return TRUE;
+}
+
+static void WM_NEXTDLGCTLTest(void)
+{
+    DWORD dwVal;
+
+    g_hwndTestDlg = CreateWindowEx( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+              | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT | WS_EX_APPWINDOW,
+              "WM_NEXTDLGCTLWndClass",
+              "WM_NEXTDLGCTL Message test window",
+              WS_POPUPWINDOW | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_OVERLAPPED |
+              WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK | DS_SETFONT | DS_MODALFRAME,
+              0, 0, 235, 135,
+              NULL, NULL, g_hinst, 0);
+
+    assert (g_hwndTestDlg);
+    assert (g_hwndTestDlgBut1);
+    assert (g_hwndTestDlgBut2);
+    assert (g_hwndTestDlgEdit);
+
+    /*
+     * Test message DM_SETDEFID
+     */
+
+    DefDlgProcA( g_hwndTestDlg, DM_SETDEFID, IDCANCEL, 0 );
+    DefDlgProcA( g_hwndTestDlgBut1, BM_SETSTYLE, BS_DEFPUSHBUTTON, FALSE );
+    dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+
+    ok ( IDCANCEL == (LOWORD(dwVal)), "Did not set default ID\n" );
+
+    /*
+     * Check whether message WM_NEXTDLGCTL is changing the focus to next control and if
+     * the destination control is a button, style of the button should be changed to
+     * BS_DEFPUSHBUTTON with out making it default.
+     */
+
+    /*
+     * Keep the focus on Edit control.
+     */
+
+    if ( SetFocus( g_hwndTestDlgEdit ) )
+    {
+         ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't set on Edit control\n");
+
+        /*
+         * Test message WM_NEXTDLGCTL
+         */
+        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+        ok ((GetFocus() == g_hwndTestDlgBut1), "Focus didn't move to first button\n");
+
+        /*
+         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+         */
+        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+
+        /*
+         * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
+         * the style of default button changed to BS_PUSHBUTTON.
+         */
+        if ( IDCANCEL == (LOWORD(dwVal)) )
+        {
+                ok ( ((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+                        "Button1 style not set to BS_DEFPUSHBUTTON\n" );
+
+                ok ( !((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+                        "Button2's style not chaged to BS_PUSHBUTTON\n" );
+        }
+
+        /*
+         * Move focus to Button2 using "WM_NEXTDLGCTL"
+         */
+        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+        ok ((GetFocus() == g_hwndTestDlgBut2), "Focus didn't move to second button\n");
+
+        /*
+         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+         */
+        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+
+        /*
+         * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
+         * the style of button which lost the focus changed to BS_PUSHBUTTON.
+         */
+        if ( IDCANCEL == (LOWORD(dwVal)) )
+        {
+                ok ( ((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+                        "Button2 style not set to BS_DEFPUSHBUTTON\n" );
+
+                ok ( !((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+                        "Button1's style not chaged to BS_PUSHBUTTON\n" );
+        }
+
+        /*
+         * Move focus to Edit control using "WM_NEXTDLGCTL"
+         */
+        DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+        ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't move to Edit control\n");
+
+        /*
+         * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+         */
+        dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+        ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+    }
+    DestroyWindow(g_hwndTestDlg);
+}
+
+static void IsDialogMessageWTest (void)
+{
+    MSG msg;
+
+    g_hwndMain = CreateWindow ("IsDialogMessageWindowClass", "IsDialogMessageWindowClass",
+            WS_OVERLAPPEDWINDOW,
+            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+            NULL, NULL, g_hinst, 0);
+
+    assert (g_hwndMain);
+    assert (g_hwndButton1);
+    assert (g_hwndButtonCancel);
+
+    /* The focus should initially be nowhere.  The first TAB should take it
+     * to the first button.  The second TAB should take it to the Cancel
+     * button.
+     */
+    FormTabMsg (&msg, g_hwndMain);
+    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle first TAB\n");
+    ok ((GetFocus() == g_hwndButton1), "Focus did not move to first button\n");
+    FormTabMsg (&msg, g_hwndButton1);
+    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle second TAB\n");
+    ok ((GetFocus() == g_hwndButtonCancel),
+            "Focus did not move to cancel button\n");
+    FormEnterMsg (&msg, g_hwndButtonCancel);
+    ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle the ENTER\n");
+    ok (g_terminated, "ENTER did not terminate\n");
+}
+
+
+static LRESULT CALLBACK delayFocusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
+        LPARAM lParam)
+{
+    switch (uiMsg)
+    {
+    case WM_INITDIALOG:
+        g_hwndMain = hDlg;
+       g_hwndInitialFocusGroupBox = GetDlgItem(hDlg,100);
+       g_hwndButton1 = GetDlgItem(hDlg,200);
+       g_hwndButton2 = GetDlgItem(hDlg,201);
+       g_hwndButtonCancel = GetDlgItem(hDlg,IDCANCEL);
+       g_styleInitialFocusT1 = GetWindowLong(g_hwndInitialFocusGroupBox, GWL_STYLE);
+
+       /* Initially check the second radio button */
+       SendMessage(g_hwndButton1, BM_SETCHECK, BST_UNCHECKED, 0);
+       SendMessage(g_hwndButton2, BM_SETCHECK, BST_CHECKED  , 0);
+       /* Continue testing after dialog initialization */
+       PostMessage(hDlg, WM_USER, 0, 0);
+       return g_bInitialFocusInitDlgResult;
+
+    case WM_COMMAND:
+        if (LOWORD(wParam) == IDCANCEL)
+       {
+           EndDialog(hDlg, LOWORD(wParam));
+           return TRUE;
+       }
+       return FALSE;
+
+    case WM_USER:
+       g_styleInitialFocusT2 = GetWindowLong(hDlg, GWL_STYLE);
+        g_hwndInitialFocusT1 = GetFocus();
+       SetFocus(hDlg);
+        g_hwndInitialFocusT2 = GetFocus();
+       PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static LRESULT CALLBACK focusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
+        LPARAM lParam)
+{
+    switch (uiMsg)
+    {
+    case WM_INITDIALOG:
+       return TRUE;
+
+    case WM_COMMAND:
+       if (LOWORD(wParam) == IDCANCEL)
+       {
+           EndDialog(hDlg, LOWORD(wParam));
+           return TRUE;
+       }
+       else if (LOWORD(wParam) == 200)
+       {
+           if (HIWORD(wParam) == EN_SETFOCUS)
+               g_hwndInitialFocusT1 = (HWND)lParam;
+       }
+       return FALSE;
+    }
+
+    return FALSE;
+}
+
+/* Helper for InitialFocusTest */
+static const char * GetHwndString(HWND hw)
+{
+  if (hw == NULL)
+    return "a null handle";
+  if (hw == g_hwndMain)
+    return "the dialog handle";
+  if (hw == g_hwndInitialFocusGroupBox)
+    return "the group box control";
+  if (hw == g_hwndButton1)
+    return "the first button";
+  if (hw == g_hwndButton2)
+    return "the second button";
+  if (hw == g_hwndButtonCancel)
+    return "the cancel button";
+
+  return "unknown handle";
+}
+
+static void InitialFocusTest (void)
+{
+    /* Test 1:
+     * This test intentionally returns FALSE in response to WM_INITDIALOG
+     * without setting focus to a control. This is not allowed according to
+     * MSDN, but it is exactly what MFC's CFormView does.
+     *
+     * Since the WM_INITDIALOG handler returns FALSE without setting the focus,
+     * the focus should initially be NULL. Later, when we manually set focus to
+     * the dialog, the default handler should set focus to the first control that
+     * is "visible, not disabled, and has the WS_TABSTOP style" (MSDN). Because the
+     * second radio button has been checked, it should be the first control
+     * that meets these criteria and should receive the focus.
+     */
+
+    g_bInitialFocusInitDlgResult = FALSE;
+    g_hwndInitialFocusT1 = (HWND) -1;
+    g_hwndInitialFocusT2 = (HWND) -1;
+    g_styleInitialFocusT1 = -1;
+    g_styleInitialFocusT2 = -1;
+
+    DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc);
+
+    ok (((g_styleInitialFocusT1 & WS_TABSTOP) == 0),
+       "Error in wrc - Detected WS_TABSTOP as default style for GROUPBOX\n");
+
+    ok (((g_styleInitialFocusT2 & WS_VISIBLE) == 0),
+       "Modal dialogs should not be shown until the message queue first goes empty\n");
+
+    ok ((g_hwndInitialFocusT1 == NULL),
+        "Error in initial focus when WM_INITDIALOG returned FALSE: "
+        "Expected NULL focus, got %s (%p).\n",
+        GetHwndString(g_hwndInitialFocusT1), g_hwndInitialFocusT1);
+
+    ok ((g_hwndInitialFocusT2 == g_hwndButton2),
+        "Error after first SetFocus() when WM_INITDIALOG returned FALSE: "
+        "Expected the second button (%p), got %s (%p).\n",
+        g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+        g_hwndInitialFocusT2);
+
+    /* Test 2:
+     * This is the same as above, except WM_INITDIALOG is made to return TRUE.
+     * This should cause the focus to go to the second radio button right away
+     * and stay there (until the user indicates otherwise).
+     */
+
+    g_bInitialFocusInitDlgResult = TRUE;
+    g_hwndInitialFocusT1 = (HWND) -1;
+    g_hwndInitialFocusT2 = (HWND) -1;
+    g_styleInitialFocusT1 = -1;
+    g_styleInitialFocusT2 = -1;
+
+    DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc);
+
+    ok ((g_hwndInitialFocusT1 == g_hwndButton2),
+       "Error in initial focus when WM_INITDIALOG returned TRUE: "
+       "Expected the second button (%p), got %s (%p).\n",
+       g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+       g_hwndInitialFocusT2);
+
+    ok ((g_hwndInitialFocusT2 == g_hwndButton2),
+       "Error after first SetFocus() when WM_INITDIALOG returned TRUE: "
+       "Expected the second button (%p), got %s (%p).\n",
+       g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+       g_hwndInitialFocusT2);
+
+    /* Test 3:
+     * If the dialog has DS_CONTROL and it's not visible then we shouldn't change focus */
+    {
+        HWND hDlg;
+        HRSRC hResource;
+        HANDLE hTemplate;
+        DLGTEMPLATE* pTemplate;
+
+        hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG", (LPSTR)RT_DIALOG);
+        hTemplate = LoadResource(g_hinst, hResource);
+        pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
+
+        g_hwndInitialFocusT1 = 0;
+        hDlg = CreateDialogIndirectParamW(g_hinst, pTemplate, NULL, (DLGPROC)focusDlgWinProc,0);
+        ok (hDlg != 0, "Failed to create test dialog.\n");
+
+        ok ((g_hwndInitialFocusT1 == 0),
+            "Focus should not be set for an invisible DS_CONTROL dialog %p.\n", g_hwndInitialFocusT1);
+
+        DestroyWindow(hDlg);
+    }
+}
+
+static void test_GetDlgItemText(void)
+{
+    char string[64];
+    BOOL ret;
+
+    strcpy(string, "Overwrite Me");
+    ret = GetDlgItemTextA(NULL, 0, string, sizeof(string)/sizeof(string[0]));
+    ok(!ret, "GetDlgItemText(NULL) shouldn't have succeeded\n");
+
+    ok(string[0] == '\0', "string retrieved using GetDlgItemText should have been NULL terminated\n");
+}
+
+
+START_TEST(dialog)
+{
+    g_hinst = GetModuleHandleA (0);
+
+    if (!RegisterWindowClasses()) assert(0);
+
+    GetNextDlgItemTest();
+    IsDialogMessageWTest();
+    WM_NEXTDLGCTLTest();
+    InitialFocusTest();
+    test_GetDlgItemText();
+}
diff --git a/reactos/regtests/winetests/user32/edit.c b/reactos/regtests/winetests/user32/edit.c
new file mode 100755 (executable)
index 0000000..61ab5b7
--- /dev/null
@@ -0,0 +1,988 @@
+/* Unit test suite for edit control.
+ *
+ * Copyright 2004 Vitaliy Margolen
+ * Copyright 2005 C. Scott Ananian
+ *
+ * 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 <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+#ifndef ES_COMBO
+#define ES_COMBO 0x200
+#endif
+
+#define ID_EDITTEST2 99
+#define MAXLEN 200
+
+struct edit_notify {
+    int en_change, en_maxtext, en_update;
+};
+
+static struct edit_notify notifications;
+
+static char szEditTest2Name[] = "Edit Test 2 window class";
+static HINSTANCE hinst;
+static HWND hwndET2;
+
+static HWND create_editcontrol (DWORD style, DWORD exstyle)
+{
+    HWND handle;
+
+    handle = CreateWindowEx(exstyle,
+                         "EDIT",
+                         NULL,
+                         ES_AUTOHSCROLL | ES_AUTOVSCROLL | style,
+                         10, 10, 300, 300,
+                         NULL, NULL, NULL, NULL);
+    assert (handle);
+    if (winetest_interactive)
+       ShowWindow (handle, SW_SHOW);
+    return handle;
+}
+
+static LONG get_edit_style (HWND hwnd)
+{
+    return GetWindowLongA( hwnd, GWL_STYLE ) & (
+       ES_LEFT |
+/* FIXME: not implemented
+       ES_CENTER |
+       ES_RIGHT |
+       ES_OEMCONVERT |
+*/
+       ES_MULTILINE |
+       ES_UPPERCASE |
+       ES_LOWERCASE |
+       ES_PASSWORD |
+       ES_AUTOVSCROLL |
+       ES_AUTOHSCROLL |
+       ES_NOHIDESEL |
+       ES_COMBO |
+       ES_READONLY |
+       ES_WANTRETURN |
+       ES_NUMBER
+       );
+}
+
+static void set_client_height(HWND Wnd, unsigned Height)
+{
+    RECT ClientRect, WindowRect;
+
+    GetWindowRect(Wnd, &WindowRect);
+    GetClientRect(Wnd, &ClientRect);
+    SetWindowPos(Wnd, NULL, WindowRect.left, WindowRect.top,
+                 WindowRect.right - WindowRect.left,
+                 Height + (WindowRect.bottom - WindowRect.top) - (ClientRect.bottom - ClientRect.top),
+                 SWP_NOMOVE | SWP_NOZORDER);
+}
+
+#define edit_pos_ok(exp, got, txt) \
+    ok(exp == got, "wrong " #txt " expected %d got %ld\n", exp, got);
+
+#define edit_todo_pos_ok(exp, got, txt, todo) \
+    if (todo) todo_wine { edit_pos_ok(exp, got, txt); } \
+    else edit_pos_ok(exp, got, txt)
+
+#define check_pos(hwEdit, set_height, test_top, test_height, test_left, todo_top, todo_height, todo_left) \
+do { \
+    RECT format_rect; \
+    int left_margin; \
+    set_client_height(hwEdit, set_height); \
+    SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM) &format_rect); \
+    left_margin = LOWORD(SendMessage(hwEdit, EM_GETMARGINS, 0, 0)); \
+    edit_todo_pos_ok(test_top, format_rect.top, vertical position, todo_top); \
+    edit_todo_pos_ok((int)test_height, format_rect.bottom - format_rect.top, height, todo_height); \
+    edit_todo_pos_ok(test_left, format_rect.left - left_margin, left, todo_left); \
+} while(0)
+
+static void test_edit_control_1(void)
+{
+    HWND hwEdit;
+    MSG msMessage;
+    int i;
+    LONG r;
+    HFONT Font, OldFont;
+    HDC Dc;
+    TEXTMETRIC Metrics;
+
+    msMessage.message = WM_KEYDOWN;
+
+    trace("EDIT: Single line\n");
+    hwEdit = create_editcontrol(0, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL), "Wrong style expected 0xc0 got: 0x%lx\n", r); 
+    for (i=0;i<65535;i++)
+    {
+       msMessage.wParam = i;
+       r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+       ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+           "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r);
+    }
+    DestroyWindow (hwEdit);
+
+    trace("EDIT: Single line want returns\n");
+    hwEdit = create_editcontrol(ES_WANTRETURN, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN), "Wrong style expected 0x10c0 got: 0x%lx\n", r); 
+    for (i=0;i<65535;i++)
+    {
+       msMessage.wParam = i;
+       r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+       ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+           "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r);
+    }
+    DestroyWindow (hwEdit);
+
+    trace("EDIT: Multiline line\n");
+    hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0xc4 got: 0x%lx\n", r); 
+    for (i=0;i<65535;i++)
+    {
+       msMessage.wParam = i;
+       r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+       ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+           "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r);
+    }
+    DestroyWindow (hwEdit);
+
+    trace("EDIT: Multi line want returns\n");
+    hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN, 0);
+    r = get_edit_style(hwEdit);
+    ok(r == (ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0x10c4 got: 0x%lx\n", r); 
+    for (i=0;i<65535;i++)
+    {
+       msMessage.wParam = i;
+       r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+       ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+           "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r);
+    }
+    DestroyWindow (hwEdit);
+
+    /* Get a stock font for which we can determine the metrics */
+    Font = GetStockObject(SYSTEM_FONT);
+    assert(NULL != Font);
+    Dc = GetDC(NULL);
+    assert(NULL != Dc);
+    OldFont = SelectObject(Dc, Font);
+    assert(NULL != OldFont);
+    if (! GetTextMetrics(Dc, &Metrics))
+    {
+       assert(FALSE);
+    }
+    SelectObject(Dc, OldFont);
+    ReleaseDC(NULL, Dc);
+
+    trace("EDIT: Text position\n");
+    hwEdit = create_editcontrol(0, 0);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, 0);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP, 0);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, 0);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  3, 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 2, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 2, Metrics.tmHeight    , 2, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP, WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP, WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 0, Metrics.tmHeight    , 0, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  3, 0, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 2, Metrics.tmHeight    , 2, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 2, Metrics.tmHeight    , 2, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, ES_MULTILINE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_CLIENTEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 1, 1, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 1, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(0, ES_MULTILINE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(WS_BORDER, ES_MULTILINE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
+    SendMessage(hwEdit, WM_SETFONT, (WPARAM) Font, (LPARAM) FALSE);
+    check_pos(hwEdit, Metrics.tmHeight -  1, 0, Metrics.tmHeight - 1, 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight     , 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  1, 0, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  2, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight +  4, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    check_pos(hwEdit, Metrics.tmHeight + 10, 1, Metrics.tmHeight    , 1, 0, 0, 0);
+    DestroyWindow(hwEdit);
+}
+
+/* WM_SETTEXT is implemented by selecting all text, and then replacing the
+ * selection.  This test checks that the first 'select all' doesn't generate
+ * an UPDATE message which can escape and (via a handler) change the
+ * selection, which would cause WM_SETTEXT to break.  This old bug
+ * was fixed 18-Mar-2005; we check here to ensure it doesn't regress.
+ */
+static void test_edit_control_2(void)
+{
+    HWND hwndMain;
+    char szLocalString[MAXLEN];
+
+    /* Create main and edit windows. */
+    hwndMain = CreateWindow(szEditTest2Name, "ET2", WS_OVERLAPPEDWINDOW,
+                            0, 0, 200, 200, NULL, NULL, hinst, NULL);
+    assert(hwndMain);
+    if (winetest_interactive)
+        ShowWindow (hwndMain, SW_SHOW);
+
+    hwndET2 = CreateWindow("EDIT", NULL,
+                           WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,
+                           0, 0, 150, 50, /* important this not be 0 size. */
+                           hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL);
+    assert(hwndET2);
+    if (winetest_interactive)
+        ShowWindow (hwndET2, SW_SHOW);
+
+    trace("EDIT: SETTEXT atomicity\n");
+    /* Send messages to "type" in the word 'foo'. */
+    SendMessage(hwndET2, WM_CHAR, 'f', 1);
+    SendMessage(hwndET2, WM_CHAR, 'o', 1);
+    SendMessage(hwndET2, WM_CHAR, 'o', 1);
+    /* 'foo' should have been changed to 'bar' by the UPDATE handler. */
+    GetWindowText(hwndET2, szLocalString, MAXLEN);
+    ok(lstrcmp(szLocalString, "bar")==0,
+       "Wrong contents of edit: %s\n", szLocalString);
+
+    /* OK, done! */
+    DestroyWindow (hwndET2);
+    DestroyWindow (hwndMain);
+}
+
+static void ET2_check_change(void) {
+   char szLocalString[MAXLEN];
+   /* This EN_UPDATE handler changes any 'foo' to 'bar'. */
+   GetWindowText(hwndET2, szLocalString, MAXLEN);
+   if (lstrcmp(szLocalString, "foo")==0) {
+       lstrcpy(szLocalString, "bar");
+       SendMessage(hwndET2, WM_SETTEXT, 0, (LPARAM) szLocalString);
+   }
+   /* always leave the cursor at the end. */
+   SendMessage(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1);
+}
+static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
+{
+    if (id==ID_EDITTEST2 && codeNotify == EN_UPDATE)
+        ET2_check_change();
+}
+static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (iMsg) {
+        HANDLE_MSG(hwnd, WM_COMMAND, ET2_OnCommand);
+    }
+    return DefWindowProc(hwnd, iMsg, wParam, lParam);
+}
+
+static BOOL RegisterWindowClasses (void)
+{
+    WNDCLASSA cls;
+    cls.style = 0;
+    cls.lpfnWndProc = ET2_WndProc;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = hinst;
+    cls.hIcon = NULL;
+    cls.hCursor = LoadCursorA (NULL, IDC_ARROW);
+    cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    cls.lpszMenuName = NULL;
+    cls.lpszClassName = szEditTest2Name;
+    if (!RegisterClassA (&cls)) return FALSE;
+
+    return TRUE;
+}
+
+static void zero_notify(void)
+{
+    notifications.en_change = 0;
+    notifications.en_maxtext = 0;
+    notifications.en_update = 0;
+}
+
+#define test_notify(enchange, enmaxtext, enupdate) \
+    ok(notifications.en_change == enchange, "expected %d EN_CHANGE notifications, " \
+    "got %d\n", enchange, notifications.en_change); \
+    ok(notifications.en_maxtext == enmaxtext, "expected %d EN_MAXTEXT notifications, " \
+    "got %d\n", enmaxtext, notifications.en_maxtext); \
+    ok(notifications.en_update == enupdate, "expected %d EN_UPDATE notifications, " \
+    "got %d\n", enupdate, notifications.en_update)
+
+
+static LRESULT CALLBACK edit3_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch (msg) {
+        case WM_COMMAND:
+            switch (HIWORD(wParam)) {
+                case EN_MAXTEXT:
+                    notifications.en_maxtext++;
+                    break;
+                case EN_UPDATE:
+                    notifications.en_update++;
+                    break;
+                case EN_CHANGE:
+                    notifications.en_change++;
+                    break;
+            }
+            break;
+    }
+    return DefWindowProcA(hWnd, msg, wParam, lParam);
+}
+
+/* Test behaviour of WM_SETTEXT, WM_REPLACESEL and notificatisons sent in response
+ * to these messages.
+ */
+static void test_edit_control_3(void)
+{
+    WNDCLASSA cls;
+    HWND hWnd;
+    HWND hParent;
+    int len;
+    static const char *str = "this is a long string.";
+    static const char *str2 = "this is a long string.\r\nthis is a long string.\r\nthis is a long string.\r\nthis is a long string.";
+
+    trace("EDIT: Test notifications\n");
+
+    cls.style = 0;
+    cls.lpfnWndProc = edit3_wnd_procA;
+    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 = "ParentWindowClass";
+
+    assert(RegisterClassA(&cls));
+
+    hParent = CreateWindowExA(0,
+              "ParentWindowClass",
+              NULL,
+              0,
+              CW_USEDEFAULT, CW_USEDEFAULT, 10, 10,
+              NULL, NULL, NULL, NULL);
+    assert(hParent);
+
+    trace("EDIT: Single line, no ES_AUTOHSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              0,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) > len, "text should have been truncated\n");
+    test_notify(1, 1, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == len, "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Single line, ES_AUTOHSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_AUTOHSCROLL,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, no ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE | ES_AUTOHSCROLL,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+
+    trace("EDIT: Multline, ES_AUTOHSCROLL and ES_AUTOVSCROLL\n");
+    hWnd = CreateWindowExA(0,
+              "EDIT",
+              NULL,
+              ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
+              10, 10, 50, 50,
+              hParent, NULL, NULL, NULL);
+    assert(hWnd);
+
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(1, 0, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+    zero_notify();
+    SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+    test_notify(1, 1, 1);
+
+    zero_notify();
+    SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+    len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+    ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+    test_notify(0, 0, 0);
+
+    DestroyWindow(hWnd);
+}
+
+/* Test EM_CHARFROMPOS and EM_POSFROMCHAR
+ */
+static void test_edit_control_4(void)
+{
+    HWND hwEdit;
+    int lo, hi, mid;
+    int ret;
+    int i;
+
+    trace("EDIT: Test EM_CHARFROMPOS and EM_POSFROMCHAR\n");
+    hwEdit = create_editcontrol(0, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_RIGHT, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_CENTER, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 +1;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_RIGHT, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 +1;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_CENTER, 0);
+    SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+    lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+    hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+    mid = lo + (hi - lo) / 2 +1;
+
+    for (i = lo; i < mid; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(0 == ret, "expected 0 got %d\n", ret);
+    }
+    for (i = mid; i <= hi; i++) {
+       ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+       ok(1 == ret, "expected 1 got %d\n", ret);
+    }
+    ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+    ok(-1 == ret, "expected -1 got %d\n", ret);
+    DestroyWindow(hwEdit);
+}
+
+static void test_margins(void)
+{
+    HWND hwEdit;
+    RECT old_rect, new_rect;
+    INT old_left_margin, old_right_margin;
+    DWORD old_margins, new_margins;
+
+    hwEdit = create_editcontrol(WS_BORDER, 0);
+    
+    old_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+    old_left_margin = LOWORD(old_margins);
+    old_right_margin = HIWORD(old_margins);
+    
+    /* Check if setting the margins works */
+    
+    SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN, MAKELONG(10, 0));
+    new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+    ok(HIWORD(new_margins) == old_right_margin, "Wrong right margin: %d\n", HIWORD(new_margins));
+    
+    SendMessage(hwEdit, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, 10));
+    new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+    ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+    ok(HIWORD(new_margins) == 10, "Wrong right margin: %d\n", HIWORD(new_margins));
+    
+    
+    /* The size of the rectangle must decrease if we increase the margin */
+    
+    SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5));
+    SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+    SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(15, 20));
+    SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(new_rect.left == old_rect.left + 10, "The left border of the rectangle is wrong\n");
+    ok(new_rect.right == old_rect.right - 15, "The right border of the rectangle is wrong\n");
+    ok(new_rect.top == old_rect.top, "The top border of the rectangle must not change\n");
+    ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle must not change\n");
+    
+    
+    /* If we set the margin to same value as the current margin,
+       the rectangle must not change */
+    
+    SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+    old_rect.left = 1;
+    old_rect.right = 99;
+    old_rect.top = 1;
+    old_rect.bottom = 99;
+    SendMessage(hwEdit, EM_SETRECT, 0, (LPARAM)&old_rect);    
+    SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+    SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+    SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+    ok(new_rect.left == old_rect.left, "The left border of the rectangle has changed\n");
+    ok(new_rect.right == old_rect.right, "The right border of the rectangle has changed\n");
+    ok(new_rect.top == old_rect.top, "The top border of the rectangle has changed\n");
+    ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle has changed\n");
+    
+    DestroyWindow (hwEdit);
+}
+
+START_TEST(edit)
+{
+    hinst = GetModuleHandleA (NULL);
+    if (!RegisterWindowClasses())
+        assert(0);
+
+    test_edit_control_1();
+    test_edit_control_2();
+    test_edit_control_3();
+    test_edit_control_4();
+    test_margins();
+}
diff --git a/reactos/regtests/winetests/user32/input.c b/reactos/regtests/winetests/user32/input.c
new file mode 100755 (executable)
index 0000000..9eca893
--- /dev/null
@@ -0,0 +1,366 @@
+/* Test Key event to Key message translation
+ *
+ * Copyright 2003 Rein Klazes
+ *
+ * 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
+ */
+
+/* test whether the right type of messages:
+ * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN  are sent in case of combined
+ * keystrokes.
+ *
+ * For instance <ALT>-X can be accompished by
+ * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP
+ * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP
+ * Whether a KEY or a SYSKEY message is sent is not always clear, it is
+ * also not the same in WINNT as in WIN9X */
+
+/* NOTE that there will be test failures under WIN9X
+ * No applications are known to me that rely on this
+ * so I don't fix it */
+
+/* TODO:
+ * 1. extend it to the wm_command and wm_syscommand notifications
+ * 2. add some more tests with special cases like dead keys or right (alt) key
+ * 3. there is some adapted code from input.c in here. Should really
+ *    make that code exactly the same.
+ * 4. resolve the win9x case when there is a need or the testing frame work
+ *    offers a nice way.
+ * 5. The test app creates a window, the user should not take the focus
+ *    away during its short existence. I could do something to prevent that
+ *    if it is a problem.
+ *
+ */
+
+#define _WIN32_WINNT 0x403
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windows.h"
+
+#include "wine/test.h"
+
+/* globals */
+static HWND hWndTest;
+static long timetag = 0x10000000;
+
+static UINT (WINAPI *ptr_SendInput) (UINT, INPUT*, size_t);
+
+#define MAXKEYEVENTS 6
+#define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
+                                       and only one message */
+
+/* keyboard message names, sorted as their value */
+static const char *MSGNAME[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR",
+    "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"};
+
+/* keyevents, add more as needed */
+typedef enum KEVtag
+{  ALTDOWN = 1, ALTUP, XDOWN, XUP, SHIFTDOWN, SHIFTUP, CTRLDOWN, CTRLUP } KEV;
+/* matching VK's */
+static const int GETVKEY[]={0, VK_MENU, VK_MENU, 'X', 'X', VK_SHIFT, VK_SHIFT, VK_CONTROL, VK_CONTROL};
+/* matching scan codes */
+static const int GETSCAN[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D };
+/* matching updown events */
+static const int GETUPDOWN[]={0, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP};
+/* matching descripts */
+static const char *getdesc[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"};
+
+/* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */
+typedef struct
+{
+    DWORD type;
+    union
+    {
+        MOUSEINPUT      mi;
+        KEYBDINPUT      ki;
+        HARDWAREINPUT   hi;
+    } u;
+} TEST_INPUT;
+
+#define ADDTOINPUTS(kev) \
+inputs[evtctr].type = INPUT_KEYBOARD; \
+    ((TEST_INPUT*)inputs)[evtctr].u.ki.wVk = GETVKEY[ kev]; \
+    ((TEST_INPUT*)inputs)[evtctr].u.ki.wScan = GETSCAN[ kev]; \
+    ((TEST_INPUT*)inputs)[evtctr].u.ki.dwFlags = GETUPDOWN[ kev]; \
+    ((TEST_INPUT*)inputs)[evtctr].u.ki.dwExtraInfo = 0; \
+    ((TEST_INPUT*)inputs)[evtctr].u.ki.time = ++timetag; \
+    if( kev) evtctr++;
+
+typedef struct {
+    UINT    message;
+    WPARAM  wParam;
+    LPARAM  lParam;
+} KMSG;
+
+/*******************************************
+ * add new test sets here
+ * the software will make all combinations of the
+ * keyevent defined here
+ */
+static const struct {
+    int nrkev;
+    KEV keydwn[MAXKEYEVENTS];
+    KEV keyup[MAXKEYEVENTS];
+} testkeyset[]= {
+    { 2, { ALTDOWN, XDOWN }, { ALTUP, XUP}},
+    { 3, { ALTDOWN, XDOWN , SHIFTDOWN}, { ALTUP, XUP, SHIFTUP}},
+    { 3, { ALTDOWN, XDOWN , CTRLDOWN}, { ALTUP, XUP, CTRLUP}},
+    { 3, { SHIFTDOWN, XDOWN , CTRLDOWN}, { SHIFTUP, XUP, CTRLUP}},
+    { 0 } /* mark the end */
+};
+
+/**********************adapted from input.c **********************************/
+
+static BYTE InputKeyStateTable[256];
+static BYTE AsyncKeyStateTable[256];
+static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
+                         or a WM_KEYUP message */
+typedef union
+{
+    struct
+    {
+       unsigned long count : 16;
+       unsigned long code : 8;
+       unsigned long extended : 1;
+       unsigned long unused : 2;
+       unsigned long win_internal : 2;
+       unsigned long context : 1;
+       unsigned long previous : 1;
+       unsigned long transition : 1;
+    } lp1;
+    unsigned long lp2;
+} KEYLP;
+
+static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam )
+{
+    UINT message;
+    int VKey = GETVKEY[kev];
+    KEYLP keylp;
+
+    keylp.lp2 = 0;
+
+    keylp.lp1.count = 1;
+    keylp.lp1.code = GETSCAN[kev];
+    keylp.lp1.extended = 0 ;/*  FIXME (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; */
+    keylp.lp1.win_internal = 0;
+
+    if (GETUPDOWN[kev] & KEYEVENTF_KEYUP )
+    {
+        message = WM_KEYUP;
+        if( (InputKeyStateTable[VK_MENU] & 0x80) && (
+                (VKey == VK_MENU) || (VKey == VK_CONTROL) ||
+                 !(InputKeyStateTable[VK_CONTROL] & 0x80))) {
+            if(  TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
+                    (VKey != VK_MENU)) /* <ALT>-down...<something else>-up */
+                message = WM_SYSKEYUP;
+                TrackSysKey = 0;
+        }
+        InputKeyStateTable[VKey] &= ~0x80;
+        keylp.lp1.previous = 1;
+        keylp.lp1.transition = 1;
+    }
+    else
+    {
+        keylp.lp1.previous = (InputKeyStateTable[VKey] & 0x80) != 0;
+        keylp.lp1.transition = 0;
+        if (!(InputKeyStateTable[VKey] & 0x80)) InputKeyStateTable[VKey] ^= 0x01;
+        InputKeyStateTable[VKey] |= 0x80;
+        AsyncKeyStateTable[VKey] |= 0x80;
+
+        message = WM_KEYDOWN;
+        if( (InputKeyStateTable[VK_MENU] & 0x80) &&
+                !(InputKeyStateTable[VK_CONTROL] & 0x80)) {
+            message = WM_SYSKEYDOWN;
+            TrackSysKey = VKey;
+        }
+    }
+
+    keylp.lp1.context = (InputKeyStateTable[VK_MENU] & 0x80) != 0; /* 1 if alt */
+
+    if( plParam) *plParam = keylp.lp2;
+    if( pwParam) *pwParam = VKey;
+    return message;
+}
+
+/****************************** end copy input.c ****************************/
+
+/*
+ * . prepare the keyevents for SendInputs
+ * . calculate the "expected" messages
+ * . Send the events to our window
+ * . retrieve the messages from the input queue
+ * . verify
+ */
+static void do_test( HWND hwnd, int seqnr, const KEV td[] )
+{
+    HMODULE module;
+    INPUT inputs[MAXKEYEVENTS];
+    KMSG expmsg[MAXKEYEVENTS];
+    MSG msg;
+    char buf[100];
+    UINT evtctr=0;
+    int kmctr, i;
+
+    module = GetModuleHandleA("user32");
+    if (!module) return;
+    ptr_SendInput = (void *)GetProcAddress(module, "SendInput");
+    if (!ptr_SendInput) return;
+
+    buf[0]='\0';
+    TrackSysKey=0; /* see input.c */
+    for( i = 0; i < MAXKEYEVENTS; i++) {
+        ADDTOINPUTS(td[i])
+        strcat(buf, getdesc[td[i]]);
+        if(td[i])
+            expmsg[i].message = KbdMessage(td[i], &(expmsg[i].wParam), &(expmsg[i].lParam)); /* see queue_kbd_event() */
+        else
+            expmsg[i].message = 0;
+    }
+    for( kmctr = 0; kmctr < MAXKEYEVENTS && expmsg[kmctr].message; kmctr++)
+        ;
+    assert( evtctr <= MAXKEYEVENTS );
+    assert( evtctr == ptr_SendInput(evtctr, &inputs[0], sizeof(INPUT)));
+    i = 0;
+    trace("======== key stroke sequence #%d: %s =============\n",
+            seqnr + 1, buf);
+    while( PeekMessage(&msg,hwnd,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE) ) {
+        trace("message[%d] %-15s wParam %04x lParam %08lx time %lx\n", i,
+                MSGNAME[msg.message - WM_KEYFIRST], msg.wParam, msg.lParam, msg.time);
+        if( i < kmctr ) {
+            ok( msg.message == expmsg[i].message &&
+                    msg.wParam == expmsg[i].wParam &&
+                    msg.lParam == expmsg[i].lParam,
+                    "wrong message! expected:\n"
+                    "message[%d] %-15s wParam %04x lParam %08lx\n",i,
+                    MSGNAME[(expmsg[i]).message - WM_KEYFIRST],
+                    expmsg[i].wParam, expmsg[i].lParam );
+        }
+        i++;
+    }
+    trace("%d messages retrieved\n", i);
+    ok( i == kmctr, "message count is wrong: got %d expected: %d\n", i, kmctr);
+}
+
+/* test all combinations of the specified key events */
+static void TestASet( HWND hWnd, int nrkev, const KEV kevdwn[], const KEV kevup[] )
+{
+    int i,j,k,l,m,n;
+    static int count=0;
+    KEV kbuf[MAXKEYEVENTS];
+    assert( nrkev==2 || nrkev==3);
+    for(i=0;i<MAXKEYEVENTS;i++) kbuf[i]=0;
+    /* two keys involved gives 4 test cases */
+    if(nrkev==2) {
+        for(i=0;i<nrkev;i++) {
+            for(j=0;j<nrkev;j++) {
+                kbuf[0] = kevdwn[i];
+                kbuf[1] = kevdwn[1-i];
+                kbuf[2] = kevup[j];
+                kbuf[3] = kevup[1-j];
+                do_test( hWnd, count++, kbuf);
+            }
+        }
+    }
+    /* three keys involved gives 36 test cases */
+    if(nrkev==3){
+        for(i=0;i<nrkev;i++){
+            for(j=0;j<nrkev;j++){
+                if(j==i) continue;
+                for(k=0;k<nrkev;k++){
+                    if(k==i || k==j) continue;
+                    for(l=0;l<nrkev;l++){
+                        for(m=0;m<nrkev;m++){
+                            if(m==l) continue;
+                            for(n=0;n<nrkev;n++){
+                                if(n==l ||n==m) continue;
+                                kbuf[0] = kevdwn[i];
+                                kbuf[1] = kevdwn[j];
+                                kbuf[2] = kevdwn[k];
+                                kbuf[3] = kevup[l];
+                                kbuf[4] = kevup[m];
+                                kbuf[5] = kevup[n];
+                                do_test( hWnd, count++, kbuf);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+/* test each set specified in the global testkeyset array */
+static void TestSysKeys( HWND hWnd)
+{
+    int i;
+    for(i=0; testkeyset[i].nrkev;i++)
+        TestASet( hWnd, testkeyset[i].nrkev, testkeyset[i].keydwn,
+                testkeyset[i].keyup);
+}
+
+static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam,
+        LPARAM lParam )
+{
+    switch (msg) {
+        case WM_USER:
+            SetFocus(hWnd);
+            /* window has focus, now do the test */
+            if( hWnd == hWndTest) TestSysKeys( hWnd);
+            /* finished :-) */
+            break;
+
+        case WM_DESTROY:
+            PostQuitMessage( 0 );
+            break;
+
+        default:
+            return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
+    }
+
+    return 0;
+}
+
+START_TEST(input)
+{
+    MSG msg;
+    WNDCLASSA  wclass;
+    HANDLE hInstance = GetModuleHandleA( NULL );
+
+    wclass.lpszClassName = "InputSysKeyTestClass";
+    wclass.style         = CS_HREDRAW | CS_VREDRAW;
+    wclass.lpfnWndProc   = WndProc;
+    wclass.hInstance     = hInstance;
+    wclass.hIcon         = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
+    wclass.hCursor       = LoadCursorA( NULL, IDC_ARROW);
+    wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
+    wclass.lpszMenuName = 0;
+    wclass.cbClsExtra    = 0;
+    wclass.cbWndExtra    = 0;
+    assert (RegisterClassA( &wclass ));
+    /* create the test window that will receive the keystrokes */
+    assert ( hWndTest = CreateWindowA( wclass.lpszClassName, "InputSysKeyTest",
+                WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
+                NULL, NULL, hInstance, NULL) );
+    ShowWindow( hWndTest, SW_SHOW);
+    UpdateWindow( hWndTest);
+
+    /* flush pending messages */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
+
+    SendMessageA(hWndTest, WM_USER, 0, 0);
+    DestroyWindow(hWndTest);
+}
diff --git a/reactos/regtests/winetests/user32/listbox.c b/reactos/regtests/winetests/user32/listbox.c
new file mode 100644 (file)
index 0000000..df75bc8
--- /dev/null
@@ -0,0 +1,363 @@
+/* Unit test suite for list boxes.
+ *
+ * Copyright 2003 Ferenc Wagner
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+
+#include "wine/test.h"
+
+#ifdef VISIBLE
+#define WAIT Sleep (1000)
+#define REDRAW RedrawWindow (handle, NULL, 0, RDW_UPDATENOW)
+#else
+#define WAIT
+#define REDRAW
+#endif
+
+static const char * const strings[4] = {
+  "First added",
+  "Second added",
+  "Third added",
+  "Fourth added which is very long because at some time we only had a 256 byte character buffer and that was overflowing in one of those applications that had a common dialog file open box and tried to add a 300 characters long custom filter string which of course the code did not like and crashed. Just make sure this string is longer than 256 characters."
+};
+
+static HWND
+create_listbox (DWORD add_style, HWND parent)
+{
+  HWND handle=CreateWindow ("LISTBOX", "TestList",
+                            (LBS_STANDARD & ~LBS_SORT) | add_style,
+                            0, 0, 100, 100,
+                            parent, (HMENU)1, NULL, 0);
+
+  assert (handle);
+  SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[0]);
+  SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[1]);
+  SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[2]);
+  SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[3]);
+
+#ifdef VISIBLE
+  ShowWindow (handle, SW_SHOW);
+#endif
+  REDRAW;
+
+  return handle;
+}
+
+struct listbox_prop {
+  DWORD add_style;
+};
+
+struct listbox_stat {
+  int selected, anchor, caret, selcount;
+};
+
+struct listbox_test {
+  struct listbox_prop prop;
+  struct listbox_stat  init,  init_todo;
+  struct listbox_stat click, click_todo;
+  struct listbox_stat  step,  step_todo;
+  struct listbox_stat   sel,   sel_todo;
+};
+
+static void
+listbox_query (HWND handle, struct listbox_stat *results)
+{
+  results->selected = SendMessage (handle, LB_GETCURSEL, 0, 0);
+  results->anchor   = SendMessage (handle, LB_GETANCHORINDEX, 0, 0);
+  results->caret    = SendMessage (handle, LB_GETCARETINDEX, 0, 0);
+  results->selcount = SendMessage (handle, LB_GETSELCOUNT, 0, 0);
+}
+
+static void
+buttonpress (HWND handle, WORD x, WORD y)
+{
+  LPARAM lp=x+(y<<16);
+
+  WAIT;
+  SendMessage (handle, WM_LBUTTONDOWN, (WPARAM) MK_LBUTTON, lp);
+  SendMessage (handle, WM_LBUTTONUP  , (WPARAM) 0         , lp);
+  REDRAW;
+}
+
+static void
+keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
+{
+  LPARAM lp=1+(scancode<<16)+(extended?KEYEVENTF_EXTENDEDKEY:0);
+
+  WAIT;
+  SendMessage (handle, WM_KEYDOWN, keycode, lp);
+  SendMessage (handle, WM_KEYUP  , keycode, lp | 0xc000000);
+  REDRAW;
+}
+
+#define listbox_field_ok(t, s, f, got) \
+  ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \
+      ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \
+      t.s.f, got.f)
+
+#define listbox_todo_field_ok(t, s, f, got) \
+  if (t.s##_todo.f) todo_wine { listbox_field_ok(t, s, f, got); } \
+  else listbox_field_ok(t, s, f, got)
+
+#define listbox_ok(t, s, got) \
+  listbox_todo_field_ok(t, s, selected, got); \
+  listbox_todo_field_ok(t, s, anchor, got); \
+  listbox_todo_field_ok(t, s, caret, got); \
+  listbox_todo_field_ok(t, s, selcount, got)
+
+static void
+check (const struct listbox_test test)
+{
+  struct listbox_stat answer;
+  HWND hLB=create_listbox (test.prop.add_style, 0);
+  RECT second_item;
+  int i;
+
+  listbox_query (hLB, &answer);
+  listbox_ok (test, init, answer);
+
+  SendMessage (hLB, LB_GETITEMRECT, (WPARAM) 1, (LPARAM) &second_item);
+  buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top);
+
+  listbox_query (hLB, &answer);
+  listbox_ok (test, click, answer);
+
+  keypress (hLB, VK_DOWN, 0x50, TRUE);
+
+  listbox_query (hLB, &answer);
+  listbox_ok (test, step, answer);
+
+  DestroyWindow (hLB);
+  hLB=create_listbox (test.prop.add_style, 0);
+
+  SendMessage (hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
+  listbox_query (hLB, &answer);
+  listbox_ok (test, sel, answer);
+
+  for (i=0;i<4;i++) {
+       DWORD size = SendMessage (hLB, LB_GETTEXTLEN, i, 0);
+       CHAR *txt;
+       WCHAR *txtw;
+
+       txt = HeapAlloc (GetProcessHeap(), 0, size+1);
+       SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt);
+        ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+
+       txtw = HeapAlloc (GetProcessHeap(), 0, 2*size+2);
+       SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
+       WideCharToMultiByte( CP_ACP, 0, txtw, -1, txt, size, NULL, NULL );
+        ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+
+       HeapFree (GetProcessHeap(), 0, txtw);
+       HeapFree (GetProcessHeap(), 0, txt);
+  }
+  
+  WAIT;
+  DestroyWindow (hLB);
+}
+
+static void check_item_height(void)
+{
+    HWND hLB;
+    HDC hdc;
+    HFONT font;
+    TEXTMETRIC tm;
+    INT itemHeight;
+
+    hLB = create_listbox (0, 0);
+    ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n");
+    ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n");
+    ok (GetTextMetrics( hdc, &tm ), "Can't read font metrics\n");
+    ReleaseDC( hLB, hdc);
+
+    ok (SendMessage(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n");
+
+    itemHeight = SendMessage(hLB, LB_GETITEMHEIGHT, 0, 0);
+    ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %ld\n", itemHeight, tm.tmHeight);
+
+    DestroyWindow (hLB);
+}
+
+static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_DRAWITEM:
+    {
+        RECT rc_item, rc_client, rc_clip;
+        DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam;
+
+        trace("%p WM_DRAWITEM %08x %08lx\n", hwnd, wparam, lparam);
+
+        ok(wparam == 0, "wrong wparam %04x\n", wparam);
+        ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType);
+
+        GetClientRect(dis->hwndItem, &rc_client);
+        trace("hwndItem %p client rect (%ld,%ld-%ld,%ld)\n", dis->hwndItem,
+               rc_client.left, rc_client.top, rc_client.right, rc_client.bottom);
+        GetClipBox(dis->hDC, &rc_clip);
+        trace("clip rect (%ld,%ld-%ld,%ld)\n", rc_clip.left, rc_clip.top, rc_clip.right, rc_clip.bottom);
+        ok(EqualRect(&rc_client, &rc_clip), "client rect of the listbox should be equal to the clip box\n");
+
+        trace("rcItem (%ld,%ld-%ld,%ld)\n", dis->rcItem.left, dis->rcItem.top,
+               dis->rcItem.right, dis->rcItem.bottom);
+        SendMessage(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item);
+        trace("item rect (%ld,%ld-%ld,%ld)\n", rc_item.left, rc_item.top, rc_item.right, rc_item.bottom);
+        ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n");
+
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void test_ownerdraw(void)
+{
+    WNDCLASS cls;
+    HWND parent, hLB;
+    INT ret;
+    RECT rc;
+
+    cls.style = 0;
+    cls.lpfnWndProc = main_window_proc;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = GetModuleHandle(0);
+    cls.hIcon = 0;
+    cls.hCursor = LoadCursor(0, (LPSTR)IDC_ARROW);
+    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+    cls.lpszMenuName = NULL;
+    cls.lpszClassName = "main_window_class";
+    assert(RegisterClass(&cls));
+
+    parent = CreateWindowEx(0, "main_window_class", NULL,
+                            WS_POPUP | WS_VISIBLE,
+                            100, 100, 400, 400,
+                            GetDesktopWindow(), 0,
+                            GetModuleHandle(0), NULL);
+    assert(parent);
+
+    hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent);
+    assert(hLB);
+
+    UpdateWindow(hLB);
+
+    /* make height short enough */
+    SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+    SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1,
+                 SWP_NOZORDER | SWP_NOMOVE);
+
+    /* make 0 item invisible */
+    SendMessage(hLB, LB_SETTOPINDEX, 1, 0);
+    ret = SendMessage(hLB, LB_GETTOPINDEX, 0, 0);
+    ok(ret == 1, "wrong top index %d\n", ret);
+
+    SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+    trace("item 0 rect (%ld,%ld-%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+    ok(!IsRectEmpty(&rc), "empty item rect\n");
+    ok(rc.top < 0, "rc.top is not negative (%ld)\n", rc.top);
+
+    DestroyWindow(hLB);
+    DestroyWindow(parent);
+}
+
+START_TEST(listbox)
+{
+  const struct listbox_test SS =
+/*   {add_style} */
+    {{0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+     {     1,      1,      1, LB_ERR}, {0,0,0,0},
+     {     2,      2,      2, LB_ERR}, {0,0,0,0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+/* {selected, anchor,  caret, selcount}{TODO fields} */
+  const struct listbox_test SS_NS =
+    {{LBS_NOSEL},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+     {     1,      1,      1, LB_ERR}, {0,0,0,0},
+     {     2,      2,      2, LB_ERR}, {0,0,0,0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+  const struct listbox_test MS =
+    {{LBS_MULTIPLESEL},
+     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+     {     1,      1,      1,      1}, {0,0,0,0},
+     {     2,      1,      2,      1}, {0,0,0,0},
+     {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+  const struct listbox_test MS_NS =
+    {{LBS_MULTIPLESEL | LBS_NOSEL},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+     {     1,      1,      1, LB_ERR}, {0,0,0,0},
+     {     2,      2,      2, LB_ERR}, {0,0,0,0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+  const struct listbox_test ES =
+    {{LBS_EXTENDEDSEL},
+     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+     {     1,      1,      1,      1}, {0,0,0,0},
+     {     2,      2,      2,      1}, {0,0,0,0},
+     {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+  const struct listbox_test ES_NS =
+    {{LBS_EXTENDEDSEL | LBS_NOSEL},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+     {     1,      1,      1, LB_ERR}, {0,0,0,0},
+     {     2,      2,      2, LB_ERR}, {0,0,0,0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+  const struct listbox_test EMS =
+    {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL},
+     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+     {     1,      1,      1,      1}, {0,0,0,0},
+     {     2,      2,      2,      1}, {0,0,0,0},
+     {     0, LB_ERR,      0,      2}, {0,0,0,0}};
+  const struct listbox_test EMS_NS =
+    {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+     {     1,      1,      1, LB_ERR}, {0,0,0,0},
+     {     2,      2,      2, LB_ERR}, {0,0,0,0},
+     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
+
+  trace (" Testing single selection...\n");
+  check (SS);
+  trace (" ... with NOSEL\n");
+  check (SS_NS);
+  trace (" Testing multiple selection...\n");
+  check (MS);
+  trace (" ... with NOSEL\n");
+  check (MS_NS);
+  trace (" Testing extended selection...\n");
+  check (ES);
+  trace (" ... with NOSEL\n");
+  check (ES_NS);
+  trace (" Testing extended and multiple selection...\n");
+  check (EMS);
+  trace (" ... with NOSEL\n");
+  check (EMS_NS);
+
+  check_item_height();
+  test_ownerdraw();
+}
diff --git a/reactos/regtests/winetests/user32/menu.c b/reactos/regtests/winetests/user32/menu.c
new file mode 100755 (executable)
index 0000000..ef97bf8
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Unit tests for menus
+ *
+ * Copyright 2005 Robert Shearman
+ *
+ * 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
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+static ATOM atomMenuCheckClass;
+
+static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+    case WM_ENTERMENULOOP:
+        /* mark window as having entered menu loop */
+        SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
+        /* exit menu modal loop
+         * ( A SendMessage does not work on NT3.51 here ) */
+        return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
+    }
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+/* globals to communicate between test and wndproc */
+unsigned int MOD_maxid;
+RECT MOD_rc[4];
+int MOD_avec, MOD_hic;
+int MOD_odheight;
+#define MOD_SIZE 10
+/* wndproc used by test_menu_ownerdraw() */
+static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
+        WPARAM wparam, LPARAM lparam)
+{
+    switch (msg)
+    {
+        case WM_MEASUREITEM:
+            {
+                MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
+                if( winetest_debug)
+                    trace("WM_MEASUREITEM received %d,%d\n",
+                            pmis->itemWidth, pmis->itemHeight);
+                MOD_odheight = pmis->itemHeight;
+                pmis->itemWidth = MOD_SIZE;
+                pmis->itemHeight = MOD_SIZE;
+                return TRUE;
+            }
+        case WM_DRAWITEM:
+            {
+                DRAWITEMSTRUCT * pdis;
+                TEXTMETRIC tm;
+                HPEN oldpen;
+                char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+                SIZE sz;
+                pdis = (DRAWITEMSTRUCT *) lparam;
+                if( winetest_debug) {
+                    trace("WM_DRAWITEM received itemdata %ld item %d rc %ld,%ld-%ld,%ld \n",
+                            pdis->itemData,
+                            pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
+                            pdis->rcItem.right,pdis->rcItem.bottom );
+                    oldpen=SelectObject( pdis->hDC, GetStockObject(
+                                pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
+                    Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
+                            pdis->rcItem.right,pdis->rcItem.bottom );
+                    SelectObject( pdis->hDC, oldpen);
+                }
+                if( pdis->itemData > MOD_maxid) return TRUE;
+                /* store the rectangl */
+                MOD_rc[pdis->itemData] = pdis->rcItem;
+                /* calculate average character width */
+                GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
+                MOD_avec = (sz.cx + 26)/52;
+                GetTextMetrics( pdis->hDC, &tm);
+                MOD_hic = tm.tmHeight;
+                if( pdis->itemData == MOD_maxid) PostMessage(hwnd, WM_CANCELMODE, 0, 0);
+                return TRUE;
+            }
+
+    }
+    return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void register_menu_check_class(void)
+{
+    WNDCLASS wc =
+    {
+        0,
+        menu_check_wnd_proc,
+        0,
+        0,
+        GetModuleHandle(NULL),
+        NULL,
+        LoadCursor(NULL, IDC_ARROW),
+        (HBRUSH)(COLOR_BTNFACE+1),
+        NULL,
+        TEXT("WineMenuCheck"),
+    };
+    
+    atomMenuCheckClass = RegisterClass(&wc);
+}
+
+/* demonstrates that windows lock the menu object so that it is still valid
+ * even after a client calls DestroyMenu on it */
+static void test_menu_locked_by_window(void)
+{
+    BOOL ret;
+    HMENU hmenu;
+    HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+        WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+        NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+    hmenu = CreateMenu();
+    ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+    ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
+    ok(ret, "InsertMenu failed with error %ld\n", GetLastError());
+    ret = SetMenu(hwnd, hmenu);
+    ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+    ret = DestroyMenu(hmenu);
+    ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+
+    ret = DrawMenuBar(hwnd);
+    todo_wine {
+    ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError());
+    }
+    ret = IsMenu(GetMenu(hwnd));
+    ok(!ret, "Menu handle should have been destroyed\n");
+
+    SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+    /* did we process the WM_INITMENU message? */
+    ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+    todo_wine {
+    ok(ret, "WM_INITMENU should have been sent\n");
+    }
+
+    DestroyWindow(hwnd);
+}
+
+static void test_menu_ownerdraw(void)
+{
+    int i,j,k;
+    BOOL ret;
+    HMENU hmenu;
+    LONG leftcol;
+    HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+            WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+            NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+    if( !hwnd) return;
+    SetWindowLong( hwnd, GWL_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
+    hmenu = CreatePopupMenu();
+    ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+    if( !hmenu) { DestroyWindow(hwnd);return;}
+    k=0;
+    for( j=0;j<2;j++) /* create columns */
+        for(i=0;i<2;i++) { /* create rows */
+            ret = AppendMenu( hmenu, MF_OWNERDRAW | 
+                    (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
+            k++;
+            ok( ret, "AppendMenu failed for %d\n", k-1);
+        }
+    MOD_maxid = k-1;
+    assert( k <= sizeof(MOD_rc)/sizeof(RECT));
+    /* display the menu */
+    ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+
+    /* columns have a 4 pixel gap between them */
+    ok( MOD_rc[0].right + 4 ==  MOD_rc[2].left,
+            "item rectangles are not separated by 4 pixels space\n");
+    /* height should be what the MEASUREITEM message has returned */
+    ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
+            "menu item has wrong height: %ld should be %d\n",
+            MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
+    /* no gaps between the rows */
+    ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
+            "There should not be a space between the rows, gap is %ld\n",
+            MOD_rc[0].bottom - MOD_rc[1].top);
+    /* test the correct value of the item height that was sent
+     * by the WM_MEASUREITEM message */
+    ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
+            MOD_odheight == MOD_hic,                     /* Win95,98,ME */
+            "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
+            HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
+    /* test what MF_MENUBREAK did at the first position. Also show
+     * that an MF_SEPARATOR is ignored in the height calculation. */
+    leftcol= MOD_rc[0].left;
+    ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0); 
+    /* display the menu */
+    ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+    /* left should be 4 pixels less now */
+    ok( leftcol == MOD_rc[0].left + 4, 
+            "columns should be 4 pixels to the left (actual %ld).\n",
+            leftcol - MOD_rc[0].left);
+    /* test width */
+    ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
+            "width of owner drawn menu item is wrong. Got %ld expected %d\n",
+            MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
+    /* and height */
+    ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
+            "Height is incorrect. Got %ld expected %d\n",
+            MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
+
+    /* test width/height of a OD menu bar as well */
+    ret = DestroyMenu(hmenu);
+    ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+    hmenu = CreateMenu();
+    ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+    if( !hmenu) { DestroyWindow(hwnd);return;}
+    MOD_maxid=1;
+    for(i=0;i<2;i++) { 
+        ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
+        ok( ret, "AppendMenu failed for %d\n", i);
+    }
+    SetMenu( hwnd, hmenu);
+    UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
+    ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+    /* test width */
+    ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
+            "width of owner drawn menu item is wrong. Got %ld expected %d\n",
+            MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
+    /* test hight */
+    ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
+            "Height of owner drawn menu item is wrong. Got %ld expected %d\n",
+            MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
+    /* clean up */
+    DestroyWindow(hwnd);
+}
+
+START_TEST(menu)
+{
+    register_menu_check_class();
+
+    test_menu_locked_by_window();
+    test_menu_ownerdraw();
+}
diff --git a/reactos/regtests/winetests/user32/msg.c b/reactos/regtests/winetests/user32/msg.c
new file mode 100755 (executable)
index 0000000..615fd15
--- /dev/null
@@ -0,0 +1,5858 @@
+/*
+ * Unit tests for window message handling
+ *
+ * Copyright 1999 Ove Kaaven
+ * Copyright 2003 Dimitrie O. Paun
+ * Copyright 2004, 2005 Dmitry Timoshkov
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+
+#define WINVER 0x0500
+#define _WIN32_WINNT 0x0500 /* For WM_CHANGEUISTATE */
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+#define MDI_FIRST_CHILD_ID 2004
+#define OBJID_QUERYCLASSNAMEIDX -12 // Fixme w32api
+#define OBJID_NATIVEOM          -16 // ditto
+
+/* undocumented SWP flags - from SDK 3.1 */
+#define SWP_NOCLIENTSIZE       0x0800
+#define SWP_NOCLIENTMOVE       0x1000
+
+#define WND_PARENT_ID          1
+#define WND_POPUP_ID           2
+#define WND_CHILD_ID           3
+
+static BOOL test_DestroyWindow_flag;
+static HWINEVENTHOOK hEvent_hook;
+
+static HWND (WINAPI *pGetAncestor)(HWND,UINT);
+
+/*
+FIXME: add tests for these
+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
+*/
+
+typedef enum {
+    sent=0x1,
+    posted=0x2,
+    parent=0x4,
+    wparam=0x8,
+    lparam=0x10,
+    defwinproc=0x20,
+    beginpaint=0x40,
+    optional=0x80,
+    hook=0x100,
+    winevent_hook=0x200
+} msg_flags_t;
+
+struct message {
+    UINT message;          /* the WM_* code */
+    msg_flags_t flags;     /* message props */
+    WPARAM wParam;         /* expected value of wParam */
+    LPARAM lParam;         /* expected value of lParam */
+};
+
+/* Empty message sequence */
+static const struct message WmEmptySeq[] =
+{
+    { 0 }
+};
+/* CreateWindow (for overlapped window, not initially visible) (16/32) */
+static const struct message WmCreateOverlappedSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_GETMINMAXINFO, sent },
+    { WM_NCCREATE, sent },
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a not visible overlapped window.
+ */
+static const struct message WmSWP_ShowOverlappedSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* Win9x: SWP_NOSENDCHANGING */
+    { WM_ACTIVATEAPP, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { WM_IME_NOTIFY, sent|defwinproc|optional },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    /* Win9x adds SWP_NOZORDER below */
+    { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+    { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_ERASEBKGND, sent|optional },
+    { 0 }
+};
+/* SetWindowPos(SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_HideOverlappedSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_ResizeSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|wparam, TRUE },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
+    { WM_NCCALCSIZE, sent|wparam|optional, TRUE },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE)
+ * for a visible popup window.
+ */
+static const struct message WmSWP_ResizePopupSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE },
+    { WM_NCCALCSIZE, sent|wparam, TRUE },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
+    { WM_NCCALCSIZE, sent|wparam|optional, TRUE },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_MoveSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE },
+    { WM_MOVE, sent|defwinproc|wparam, 0 },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+
+/* ShowWindow(SW_SHOW) for a not visible overlapped window */
+static const struct message WmShowOverlappedSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_ACTIVATEAPP, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { WM_IME_NOTIFY, sent|defwinproc|optional },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    /* Win9x adds SWP_NOZORDER below */
+    { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+    { WM_NCCALCSIZE, sent|optional },
+    { WM_NCPAINT, sent|optional },
+    { WM_ERASEBKGND, sent|optional },
+#if 0 /* CreateWindow/ShowWindow(SW_SHOW) also generates WM_SIZE/WM_MOVE
+       * messages. Does that mean that CreateWindow doesn't set initial
+       * window dimensions for overlapped windows?
+       */
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+#endif
+    { 0 }
+};
+/* ShowWindow(SW_HIDE) for a visible overlapped window */
+static const struct message WmHideOverlappedSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_ACTIVATE, sent|wparam, 0 },
+    { WM_ACTIVATEAPP, sent|wparam, 0 },
+    { WM_KILLFOCUS, sent|wparam, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_NOTIFY, sent|optional|defwinproc },
+    { 0 }
+};
+/* DestroyWindow for a visible overlapped window */
+static const struct message WmDestroyOverlappedSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_ACTIVATE, sent|wparam, 0 },
+    { WM_ACTIVATEAPP, sent|wparam, 0 },
+    { WM_KILLFOCUS, sent|wparam, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_NOTIFY, sent|optional|defwinproc },
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* CreateWindow (for a child popup window, not initially visible) */
+static const struct message WmCreateChildPopupSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { 0 }
+};
+/* CreateWindow (for a popup window, not initially visible,
+ * which sets WS_VISIBLE in WM_CREATE handler)
+ */
+static const struct message WmCreateInvisiblePopupSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { WM_STYLECHANGING, sent },
+    { WM_STYLECHANGED, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER)
+ * for a popup window with WS_VISIBLE style set
+ */
+static const struct message WmShowVisiblePopupSeq_2[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a popup window with WS_VISIBLE style set
+ */
+static const struct message WmShowVisiblePopupSeq_3[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent|parent },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { WM_IME_NOTIFY, sent|defwinproc|optional },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+    { 0 }
+};
+/* CreateWindow (for child window, not initially visible) */
+static const struct message WmCreateChildSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    /* child is inserted into parent's child list after WM_NCCREATE returns */
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+    { 0 }
+};
+/* CreateWindow (for maximized child window, not initially visible) */
+static const struct message WmCreateMaximizedChildSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+    { 0 }
+};
+/* CreateWindow (for a child window, initially visible) */
+static const struct message WmCreateVisibleChildSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    /* child is inserted into parent's child list after WM_NCCREATE returns */
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* WinXP */
+    { 0 }
+};
+/* ShowWindow(SW_SHOW) for a not visible child window */
+static const struct message WmShowChildSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* ShowWindow(SW_HIDE) for a visible child window */
+static const struct message WmHideChildSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a not visible child window
+ */
+static const struct message WmShowChildSeq_2[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CHILDACTIVATE, sent },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE)
+ * for a not visible child window
+ */
+static const struct message WmShowChildSeq_3[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a visible child window with a caption
+ */
+static const struct message WmShowChildSeq_4[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_CHILDACTIVATE, sent },
+    { 0 }
+};
+/* ShowWindow(SW_SHOW) for child with invisible parent */
+static const struct message WmShowChildInvisibleParentSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { 0 }
+};
+/* ShowWindow(SW_HIDE) for child with invisible parent */
+static const struct message WmHideChildInvisibleParentSeq[] = {
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW) for child with invisible parent */
+static const struct message WmShowChildInvisibleParentSeq_2[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* SetWindowPos(SWP_HIDEWINDOW) for child with invisible parent */
+static const struct message WmHideChildInvisibleParentSeq_2[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* DestroyWindow for a visible child window */
+static const struct message WmDestroyChildSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    { WM_PARENTNOTIFY, sent|parent|wparam, WM_DESTROY },
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { HCBT_SETFOCUS, hook }, /* set focus to a parent */
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|parent|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|parent },
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_DESTROY, sent|optional }, /* some other (IME?) window */
+    { WM_NCDESTROY, sent|optional }, /* some other (IME?) window */
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* Moving the mouse in nonclient area */
+static const struct message WmMouseMoveInNonClientAreaSeq[] = { /* FIXME: add */
+    { WM_NCHITTEST, sent },
+    { WM_SETCURSOR, sent },
+    { WM_NCMOUSEMOVE, posted },
+    { 0 }
+};
+/* Moving the mouse in client area */
+static const struct message WmMouseMoveInClientAreaSeq[] = { /* FIXME: add */
+    { WM_NCHITTEST, sent },
+    { WM_SETCURSOR, sent },
+    { WM_MOUSEMOVE, posted },
+    { 0 }
+};
+/* Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
+static const struct message WmDragTitleBarSeq[] = { /* FIXME: add */
+    { WM_NCLBUTTONDOWN, sent|wparam, HTCAPTION },
+    { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_MOVE+2 },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_ENTERSIZEMOVE, sent|defwinproc },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
+    { WM_MOVE, sent|defwinproc },
+    { WM_EXITSIZEMOVE, sent|defwinproc },
+    { 0 }
+};
+/* Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
+static const struct message WmDragThickBordersBarSeq[] = { /* FIXME: add */
+    { WM_NCLBUTTONDOWN, sent|wparam, 0xd },
+    { WM_SYSCOMMAND, sent|defwinproc|wparam, 0xf004 },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_ENTERSIZEMOVE, sent|defwinproc },
+    { WM_SIZING, sent|defwinproc|wparam, 4}, /* one for each mouse movement */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+    { WM_NCPAINT, sent|defwinproc|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc },
+    { WM_ERASEBKGND, sent|defwinproc },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { WM_EXITSIZEMOVE, sent|defwinproc },
+    { 0 }
+};
+/* Resizing child window with MoveWindow (32) */
+static const struct message WmResizingChildWithMoveWindowSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+/* Clicking on inactive button */
+static const struct message WmClickInactiveButtonSeq[] = { /* FIXME: add */
+    { WM_NCHITTEST, sent },
+    { WM_PARENTNOTIFY, sent|parent|wparam, WM_LBUTTONDOWN },
+    { WM_MOUSEACTIVATE, sent },
+    { WM_MOUSEACTIVATE, sent|parent|defwinproc },
+    { WM_SETCURSOR, sent },
+    { WM_SETCURSOR, sent|parent|defwinproc },
+    { WM_LBUTTONDOWN, posted },
+    { WM_KILLFOCUS, posted|parent },
+    { WM_SETFOCUS, posted },
+    { WM_CTLCOLORBTN, posted|parent },
+    { BM_SETSTATE, posted },
+    { WM_CTLCOLORBTN, posted|parent },
+    { WM_LBUTTONUP, posted },
+    { BM_SETSTATE, posted },
+    { WM_CTLCOLORBTN, posted|parent },
+    { WM_COMMAND, posted|parent },
+    { 0 }
+};
+/* Reparenting a button (16/32) */
+/* The last child (button) reparented gets topmost for its new parent. */
+static const struct message WmReparentButtonSeq[] = { /* FIXME: add */
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOZORDER },
+    { WM_CHILDACTIVATE, sent },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { 0 }
+};
+/* Creation of a custom dialog (32) */
+static const struct message WmCreateCustomDialogSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_GETMINMAXINFO, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_NCCREATE, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { WM_KILLFOCUS, sent|parent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_IME_NOTIFY, sent|optional|defwinproc },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_GETDLGCODE, sent|defwinproc|wparam, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { WM_ERASEBKGND, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_CTLCOLORDLG, sent|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_NCCALCSIZE, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_NCPAINT, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETICON, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { WM_ERASEBKGND, sent|optional },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_CTLCOLORDLG, sent|optional|defwinproc },
+    { WM_SIZE, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_MOVE, sent },
+    { 0 }
+};
+/* Calling EndDialog for a custom dialog (32) */
+static const struct message WmEndCustomDialogSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_GETTEXT, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { HCBT_ACTIVATE, hook },
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETTEXT, sent|optional|defwinproc },
+    { WM_ACTIVATE, sent|wparam, 0 },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 },
+    { WM_IME_NOTIFY, sent|optional },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|parent|defwinproc },
+    { 0 }
+};
+/* ShowWindow(SW_SHOW) for a custom dialog (initially invisible) */
+static const struct message WmShowCustomDialogSeq[] = {
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_ACTIVATEAPP, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_ACTIVATE, sent|wparam, 1 },
+
+    { WM_KILLFOCUS, sent|parent },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { WM_IME_NOTIFY, sent|optional|defwinproc },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_GETDLGCODE, sent|defwinproc|wparam, 0 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_ERASEBKGND, sent },
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_CTLCOLORDLG, sent|defwinproc },
+
+    { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* Creation and destruction of a modal dialog (32) */
+static const struct message WmModalDialogSeq[] = {
+    { WM_CANCELMODE, sent|parent },
+    { HCBT_SETFOCUS, hook },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_KILLFOCUS, sent|parent },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ENABLE, sent|parent|wparam, 0 },
+    { HCBT_CREATEWND, hook },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SETFONT, sent },
+    { WM_INITDIALOG, sent },
+    { WM_CHANGEUISTATE, sent|optional },
+    { WM_SHOWWINDOW, sent },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_ERASEBKGND, sent },
+    { WM_CTLCOLORDLG, sent },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_NCCALCSIZE, sent|optional },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_CTLCOLORDLG, sent|optional },
+    { WM_PAINT, sent|optional },
+    { WM_CTLCOLORBTN, sent },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_ENTERIDLE, sent|parent|optional },
+    { WM_GETICON, sent|parent|optional },
+    { WM_GETICON, sent|parent|optional },
+    { WM_GETICON, sent|parent|optional },
+    { WM_TIMER, sent },
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ENABLE, sent|parent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { HCBT_ACTIVATE, hook },
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_ACTIVATE, sent|wparam, 0 },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|optional },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|parent|defwinproc },
+    { EVENT_SYSTEM_DIALOGEND, winevent_hook|wparam|lparam, 0, 0 },
+    { HCBT_DESTROYWND, hook },
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* Creation of a modal dialog that is resized inside WM_INITDIALOG (32) */
+static const struct message WmCreateModalDialogResizeSeq[] = { /* FIXME: add */
+    /* (inside dialog proc, handling WM_INITDIALOG) */
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { WM_NCCALCSIZE, sent },
+    { WM_NCACTIVATE, sent|parent|wparam, 0 },
+    { WM_GETTEXT, sent|defwinproc },
+    { WM_ACTIVATE, sent|parent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|parent },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
+    { WM_SIZE, sent|defwinproc },
+    /* (setting focus) */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc },
+    { WM_ERASEBKGND, sent },
+    { WM_CTLCOLORDLG, sent|defwinproc },
+    { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
+    { WM_PAINT, sent },
+    /* (bunch of WM_CTLCOLOR* for each control) */
+    { WM_PAINT, sent|parent },
+    { WM_ENTERIDLE, sent|parent|wparam, 0 },
+    { WM_SETCURSOR, sent|parent },
+    { 0 }
+};
+/* SetMenu for NonVisible windows with size change*/
+static const struct message WmSetMenuNonVisibleSizeChangeSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+    { 0 }
+};
+/* SetMenu for NonVisible windows with no size change */
+static const struct message WmSetMenuNonVisibleNoSizeChangeSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+/* SetMenu for Visible windows with size change */
+static const struct message WmSetMenuVisibleSizeChangeSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_ACTIVATE, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_ERASEBKGND, sent|optional },
+    { 0 }
+};
+/* SetMenu for Visible windows with no size change */
+static const struct message WmSetMenuVisibleNoSizeChangeSeq[] = {
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_ACTIVATE, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+/* DrawMenuBar for a visible window */
+static const struct message WmDrawMenuBarSeq[] =
+{
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+
+static const struct message WmSetRedrawFalseSeq[] =
+{
+    { WM_SETREDRAW, sent|wparam, 0 },
+    { 0 }
+};
+
+static const struct message WmSetRedrawTrueSeq[] =
+{
+    { WM_SETREDRAW, sent|wparam, 1 },
+    { 0 }
+};
+
+static const struct message WmEnableWindowSeq_1[] =
+{
+    { WM_CANCELMODE, sent|wparam|lparam, 0, 0 },
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ENABLE, sent|wparam|lparam, FALSE, 0 },
+    { 0 }
+};
+
+static const struct message WmEnableWindowSeq_2[] =
+{
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ENABLE, sent|wparam|lparam, TRUE, 0 },
+    { 0 }
+};
+
+static const struct message WmGetScrollRangeSeq[] =
+{
+    { SBM_GETRANGE, sent },
+    { 0 }
+};
+static const struct message WmGetScrollInfoSeq[] =
+{
+    { SBM_GETSCROLLINFO, sent },
+    { 0 }
+};
+static const struct message WmSetScrollRangeSeq[] =
+{
+    /* MSDN claims that Windows sends SBM_SETRANGE message, but win2k SP4
+       sends SBM_SETSCROLLINFO.
+     */
+    { SBM_SETSCROLLINFO, sent },
+    { 0 }
+};
+/* SetScrollRange for a window without a non-client area */
+static const struct message WmSetScrollRangeHSeq_empty[] =
+{
+    { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_HSCROLL, 0 },
+    { 0 }
+};
+static const struct message WmSetScrollRangeVSeq_empty[] =
+{
+    { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_VSCROLL, 0 },
+    { 0 }
+};
+static const struct message WmSetScrollRangeHVSeq[] =
+{
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { 0 }
+};
+/* SetScrollRange for a window with a non-client area */
+static const struct message WmSetScrollRangeHV_NC_Seq[] =
+{
+    { WM_WINDOWPOSCHANGING, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER*/ },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETICON, sent|optional|defwinproc },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_CTLCOLORDLG, sent|defwinproc|optional }, /* sent to a parent of the dialog */
+    { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|0x1000*/ },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_GETTEXT, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|optional },
+    { 0 }
+};
+/* test if we receive the right sequence of messages */
+/* after calling ShowWindow( SW_SHOWNA) */
+static const struct message WmSHOWNAChildInvisParInvis[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { 0 }
+};
+static const struct message WmSHOWNAChildVisParInvis[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { 0 }
+};
+static const struct message WmSHOWNAChildVisParVis[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+    { 0 }
+};
+static const struct message WmSHOWNAChildInvisParVis[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER},
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+static const struct message WmSHOWNATopVisible[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE },
+    { 0 }
+};
+static const struct message WmSHOWNATopInvisible[] = {
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETICON, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+    { WM_NCPAINT, sent|wparam|optional, 1 },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { 0 }
+};
+
+static int after_end_dialog;
+static int sequence_cnt, sequence_size;
+static struct message* sequence;
+static int log_all_parent_messages;
+
+static void add_message(const struct message *msg)
+{
+    if (!sequence) 
+    {
+       sequence_size = 10;
+       sequence = HeapAlloc( GetProcessHeap(), 0, sequence_size * sizeof (struct message) );
+    }
+    if (sequence_cnt == sequence_size) 
+    {
+       sequence_size *= 2;
+       sequence = HeapReAlloc( GetProcessHeap(), 0, sequence, sequence_size * sizeof (struct message) );
+    }
+    assert(sequence);
+
+    sequence[sequence_cnt].message = msg->message;
+    sequence[sequence_cnt].flags = msg->flags;
+    sequence[sequence_cnt].wParam = msg->wParam;
+    sequence[sequence_cnt].lParam = msg->lParam;
+
+    sequence_cnt++;
+}
+
+static void flush_sequence(void)
+{
+    HeapFree(GetProcessHeap(), 0, sequence);
+    sequence = 0;
+    sequence_cnt = sequence_size = 0;
+}
+
+#define ok_sequence( exp, contx, todo) \
+        ok_sequence_( (exp), (contx), (todo), __FILE__, __LINE__)
+
+
+static void ok_sequence_(const struct message *expected, const char *context, int todo,
+        const char *file, int line)
+{
+    static const struct message end_of_sequence = { 0, 0, 0, 0 };
+    const struct message *actual;
+    int failcount = 0;
+    
+    add_message(&end_of_sequence);
+
+    actual = sequence;
+
+    while (expected->message && actual->message)
+    {
+       trace_( file, line)("expected %04x - actual %04x\n", expected->message, actual->message);
+
+       if (expected->message == actual->message)
+       {
+           if (expected->flags & wparam)
+           {
+               if (expected->wParam != actual->wParam && todo)
+               {
+                   todo_wine {
+                        failcount ++;
+                        ok_( file, line) (FALSE,
+                           "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n",
+                           context, expected->message, expected->wParam, actual->wParam);
+                   }
+               }
+               else
+               ok_( file, line) (expected->wParam == actual->wParam,
+                    "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n",
+                    context, expected->message, expected->wParam, actual->wParam);
+           }
+           if (expected->flags & lparam)
+                ok_( file, line) (expected->lParam == actual->lParam,
+                    "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
+                    context, expected->message, expected->lParam, actual->lParam);
+           ok_( file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc),
+               "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
+               context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
+           ok_( file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint),
+               "%s: the msg 0x%04x should %shave been sent by BeginPaint\n",
+               context, expected->message, (expected->flags & beginpaint) ? "" : "NOT ");
+           ok_( file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
+               "%s: the msg 0x%04x should have been %s\n",
+               context, expected->message, (expected->flags & posted) ? "posted" : "sent");
+           ok_( file, line) ((expected->flags & parent) == (actual->flags & parent),
+               "%s: the msg 0x%04x was expected in %s\n",
+               context, expected->message, (expected->flags & parent) ? "parent" : "child");
+           ok_( file, line) ((expected->flags & hook) == (actual->flags & hook),
+               "%s: the msg 0x%04x should have been sent by a hook\n",
+               context, expected->message);
+           ok_( file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook),
+               "%s: the msg 0x%04x should have been sent by a winevent hook\n",
+               context, expected->message);
+           expected++;
+           actual++;
+       }
+       /* silently drop winevent messages if there is no support for them */
+       else if ((expected->flags & optional) || ((expected->flags & winevent_hook) && !hEvent_hook))
+           expected++;
+       else if (todo)
+       {
+            failcount++;
+            todo_wine {
+                ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+                    context, expected->message, actual->message);
+            }
+            flush_sequence();
+            return;
+        }
+        else
+        {
+            ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+                context, expected->message, actual->message);
+            expected++;
+            actual++;
+        }
+    }
+
+    /* skip all optional trailing messages */
+    while (expected->message && ((expected->flags & optional) ||
+           ((expected->flags & winevent_hook) && !hEvent_hook)))
+       expected++;
+
+    if (todo)
+    {
+        todo_wine {
+            if (expected->message || actual->message) {
+                failcount++;
+                ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+                    context, expected->message, actual->message);
+            }
+        }
+    }
+    else
+    {
+        if (expected->message || actual->message)
+            ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+                context, expected->message, actual->message);
+    }
+    if( todo && !failcount) /* succeeded yet marked todo */
+        todo_wine {
+            ok_( file, line)( TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
+        }
+
+    flush_sequence();
+}
+
+/******************************** MDI test **********************************/
+
+/* CreateWindow for MDI frame window, initially visible */
+static const struct message WmCreateMDIframeSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_GETMINMAXINFO, sent },
+    { WM_NCCREATE, sent },
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { HCBT_ACTIVATE, hook },
+    { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, /* Win9x */
+    { WM_ACTIVATEAPP, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_GETICON, sent|defwinproc|optional },
+    { WM_GETICON, sent|defwinproc|optional },
+    { WM_ACTIVATE, sent|wparam, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+    /* Win9x adds SWP_NOZORDER below */
+    { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { 0 }
+};
+/* DestroyWindow for MDI frame window, initially visible */
+static const struct message WmDestroyMDIframeSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_ACTIVATE, sent|wparam|optional, 0 }, /* Win9x */
+    { WM_ACTIVATEAPP, sent|wparam|optional, 0 }, /* Win9x */
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* CreateWindow for MDI client window, initially visible */
+static const struct message WmCreateMDIclientSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent },
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { WM_PARENTNOTIFY, sent|wparam, WM_CREATE }, /* in MDI frame */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { 0 }
+};
+/* DestroyWindow for MDI client window, initially visible */
+static const struct message WmDestroyMDIclientSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    { WM_PARENTNOTIFY, sent|wparam, WM_DESTROY }, /* in MDI frame */
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* CreateWindow for MDI child window, initially visible */
+static const struct message WmCreateMDIchildVisibleSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+    /* Win9x: message sequence terminates here. */
+
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_MDIACTIVATE, sent|defwinproc },
+    { 0 }
+};
+/* DestroyWindow for MDI child window, initially visible */
+static const struct message WmDestroyMDIchildVisibleSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+    /* { WM_DESTROY, sent }
+     * Win9x: message sequence terminates here.
+     */
+
+    { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+
+    { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { WM_DESTROY, sent },
+
+    { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* CreateWindow for MDI child window, initially invisible */
+static const struct message WmCreateMDIchildInvisibleSeq[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+    { 0 }
+};
+/* DestroyWindow for MDI child window, initially invisible */
+static const struct message WmDestroyMDIchildInvisibleSeq[] = {
+    { HCBT_DESTROYWND, hook },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* CreateWindow for the 1st MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq1[] = {
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+    /* Win9x: message sequence terminates here. */
+
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_MDIACTIVATE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { 0 }
+};
+/* CreateWindow for the 2nd MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq2[] = {
+    /* restore the 1st MDI child */
+    { WM_SETREDRAW, sent|wparam, 0 },
+    { HCBT_MINMAX, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { WM_SETREDRAW, sent|wparam, 1 }, /* in the 1st MDI child */
+    /* create the 2nd MDI child */
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */
+    { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */
+
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+    /* Win9x: message sequence terminates here. */
+
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent|defwinproc }, /* in the 1st MDI child */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 }, /* in the 1st MDI child */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+
+    { WM_MDIACTIVATE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { 0 }
+};
+/* WM_MDICREATE MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq3[] = {
+    { WM_MDICREATE, sent },
+    { HCBT_CREATEWND, hook },
+    { WM_NCCREATE, sent }, 
+    { WM_NCCALCSIZE, sent|wparam, 0 },
+    { WM_CREATE, sent },
+    { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent },
+    { WM_MOVE, sent },
+    { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+    /* Win9x: message sequence terminates here. */
+
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+
+    { WM_MDIACTIVATE, sent|defwinproc },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI client */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent|defwinproc },
+
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+    { 0 }
+};
+/* WM_SYSCOMMAND/SC_CLOSE for the 2nd MDI child window, initially visible and maximized */
+static const struct message WmDestroyMDIchildVisibleMaxSeq2[] = {
+    { WM_SYSCOMMAND, sent|wparam, SC_CLOSE },
+    { HCBT_SYSCOMMAND, hook },
+    { WM_CLOSE, sent|defwinproc },
+    { WM_MDIDESTROY, sent }, /* in MDI client */
+
+    /* bring the 1st MDI child to top */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE }, /* in the 1st MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, /* in the 2nd MDI child */
+
+    { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+
+    { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 }, /* in the 1st MDI child */
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */
+    { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */
+
+    /* maximize the 1st MDI child */
+    { HCBT_MINMAX, hook },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+    { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+
+    /* restore the 2nd MDI child */
+    { WM_SETREDRAW, sent|defwinproc|wparam, 0 },
+    { HCBT_MINMAX, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOZORDER|0x8000 },
+    { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+    { WM_SETREDRAW, sent|defwinproc|wparam, 1 },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+    /* bring the 1st MDI child to top */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent|defwinproc },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_MDIACTIVATE, sent|defwinproc },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+    /* apparently ShowWindow(SW_SHOW) on an MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_MDIREFRESHMENU, sent },
+
+    { HCBT_DESTROYWND, hook },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|defwinproc|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent|defwinproc },
+    { WM_NCDESTROY, sent|defwinproc },
+    { 0 }
+};
+/* WM_MDIDESTROY for the single MDI child window, initially visible and maximized */
+static const struct message WmDestroyMDIchildVisibleMaxSeq1[] = {
+    { WM_MDIDESTROY, sent }, /* in MDI client */
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI client */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI client */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent|defwinproc },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+    { WM_NCACTIVATE, sent|wparam, 0 },
+    { WM_MDIACTIVATE, sent },
+
+    { HCBT_MINMAX, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI child */
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+
+     /* in MDI client */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+    { WM_SIZE, sent },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+
+    { WM_MDIREFRESHMENU, sent }, /* in MDI client */
+
+    { HCBT_DESTROYWND, hook },
+    /* Win2k sends wparam set to
+     * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+     * while Win9x doesn't bother to set child window id according to
+     * CLIENTCREATESTRUCT.idFirstChild
+     */
+    { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+
+    { WM_SHOWWINDOW, sent|wparam, 0 },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_ERASEBKGND, sent|parent|optional },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+    { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_DESTROY, sent },
+    { WM_NCDESTROY, sent },
+    { 0 }
+};
+/* ShowWindow(SW_MAXIMIZE) for a not visible MDI child window */
+static const struct message WmMaximizeMDIchildInvisibleSeq[] = {
+    { HCBT_MINMAX, hook },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+    { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent }, /* in MDI client */
+    { HCBT_SETFOCUS, hook },
+    { WM_KILLFOCUS, sent }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_MDIACTIVATE, sent|defwinproc },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { 0 }
+};
+/* ShowWindow(SW_MAXIMIZE) for a visible MDI child window */
+static const struct message WmMaximizeMDIchildVisibleSeq[] = {
+    { HCBT_MINMAX, hook },
+    { WM_GETMINMAXINFO, sent },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { 0 }
+};
+/* ShowWindow(SW_RESTORE) for a visible MDI child window */
+static const struct message WmRestoreMDIchildVisibleSeq[] = {
+    { HCBT_MINMAX, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { 0 }
+};
+/* ShowWindow(SW_RESTORE) for a not visible MDI child window */
+static const struct message WmRestoreMDIchildInisibleSeq[] = {
+    { HCBT_MINMAX, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+    { WM_SIZE, sent|defwinproc },
+     /* in MDI frame */
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+    { WM_NCCALCSIZE, sent|wparam, 1 },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { 0 }
+};
+
+static HWND mdi_client;
+static WNDPROC old_mdi_client_proc;
+
+static LRESULT WINAPI mdi_client_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    struct message msg;
+
+    /* do not log painting messages */
+    if (message != WM_PAINT &&
+        message != WM_ERASEBKGND &&
+        message != WM_NCPAINT &&
+        message != WM_NCHITTEST &&
+        message != WM_GETTEXT &&
+        message != WM_MDIGETACTIVE &&
+        message != WM_DEVICECHANGE)
+    {
+        trace("mdi client: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+        switch (message)
+        {
+            case WM_WINDOWPOSCHANGING:
+            case WM_WINDOWPOSCHANGED:
+            {
+                WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+                trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+                trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+                      winpos->hwnd, winpos->hwndInsertAfter,
+                      winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+                /* Log only documented flags, win2k uses 0x1000 and 0x2000
+                 * in the high word for internal purposes
+                 */
+                wParam = winpos->flags & 0xffff;
+                break;
+            }
+        }
+
+        msg.message = message;
+        msg.flags = sent|wparam|lparam;
+        msg.wParam = wParam;
+        msg.lParam = lParam;
+        add_message(&msg);
+    }
+
+    return CallWindowProcA(old_mdi_client_proc, hwnd, message, wParam, lParam);
+}
+
+static LRESULT WINAPI mdi_child_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    /* do not log painting messages */
+    if (message != WM_PAINT &&
+        message != WM_ERASEBKGND &&
+        message != WM_NCPAINT &&
+        message != WM_NCHITTEST &&
+        message != WM_GETTEXT &&
+        message != WM_DEVICECHANGE)
+    {
+        trace("mdi child: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+        switch (message)
+        {
+            case WM_WINDOWPOSCHANGING:
+            case WM_WINDOWPOSCHANGED:
+            {
+                WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+                trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+                trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+                      winpos->hwnd, winpos->hwndInsertAfter,
+                      winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+                /* Log only documented flags, win2k uses 0x1000 and 0x2000
+                 * in the high word for internal purposes
+                 */
+                wParam = winpos->flags & 0xffff;
+                break;
+            }
+
+            case WM_MDIACTIVATE:
+            {
+                HWND active, client = GetParent(hwnd);
+
+                active = (HWND)SendMessageA(client, WM_MDIGETACTIVE, 0, 0);
+
+                if (hwnd == (HWND)lParam) /* if we are being activated */
+                    ok (active == (HWND)lParam, "new active %p != active %p\n", (HWND)lParam, active);
+                else
+                    ok (active == (HWND)wParam, "old active %p != active %p\n", (HWND)wParam, active);
+                break;
+            }
+        }
+
+        msg.message = message;
+        msg.flags = sent|wparam|lparam;
+        if (defwndproc_counter) msg.flags |= defwinproc;
+        msg.wParam = wParam;
+        msg.lParam = lParam;
+        add_message(&msg);
+    }
+
+    defwndproc_counter++;
+    ret = DefMDIChildProcA(hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
+static LRESULT WINAPI mdi_frame_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    /* do not log painting messages */
+    if (message != WM_PAINT &&
+        message != WM_ERASEBKGND &&
+        message != WM_NCPAINT &&
+        message != WM_NCHITTEST &&
+        message != WM_GETTEXT &&
+        message != WM_DEVICECHANGE)
+    {
+        trace("mdi frame: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+        switch (message)
+        {
+            case WM_WINDOWPOSCHANGING:
+            case WM_WINDOWPOSCHANGED:
+            {
+                WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+                trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+                trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+                      winpos->hwnd, winpos->hwndInsertAfter,
+                      winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+                /* Log only documented flags, win2k uses 0x1000 and 0x2000
+                 * in the high word for internal purposes
+                 */
+                wParam = winpos->flags & 0xffff;
+                break;
+            }
+        }
+
+        msg.message = message;
+        msg.flags = sent|wparam|lparam;
+        if (defwndproc_counter) msg.flags |= defwinproc;
+        msg.wParam = wParam;
+        msg.lParam = lParam;
+        add_message(&msg);
+    }
+
+    defwndproc_counter++;
+    ret = DefFrameProcA(hwnd, mdi_client, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
+static BOOL mdi_RegisterWindowClasses(void)
+{
+    WNDCLASSA cls;
+
+    cls.style = 0;
+    cls.lpfnWndProc = mdi_frame_wnd_proc;
+    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 = "MDI_frame_class";
+    if (!RegisterClassA(&cls)) return FALSE;
+
+    cls.lpfnWndProc = mdi_child_wnd_proc;
+    cls.lpszClassName = "MDI_child_class";
+    if (!RegisterClassA(&cls)) return FALSE;
+
+    if (!GetClassInfoA(0, "MDIClient", &cls)) assert(0);
+    old_mdi_client_proc = cls.lpfnWndProc;
+    cls.hInstance = GetModuleHandleA(0);
+    cls.lpfnWndProc = mdi_client_hook_proc;
+    cls.lpszClassName = "MDI_client_class";
+    if (!RegisterClassA(&cls)) assert(0);
+
+    return TRUE;
+}
+
+static void test_mdi_messages(void)
+{
+    MDICREATESTRUCTA mdi_cs;
+    CLIENTCREATESTRUCT client_cs;
+    HWND mdi_frame, mdi_child, mdi_child2, active_child;
+    BOOL zoomed;
+    HMENU hMenu = CreateMenu();
+
+    assert(mdi_RegisterWindowClasses());
+
+    flush_sequence();
+
+    trace("creating MDI frame window\n");
+    mdi_frame = CreateWindowExA(0, "MDI_frame_class", "MDI frame window",
+                                WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                                WS_MAXIMIZEBOX | WS_VISIBLE,
+                                100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+                                GetDesktopWindow(), hMenu,
+                                GetModuleHandleA(0), NULL);
+    assert(mdi_frame);
+    ok_sequence(WmCreateMDIframeSeq, "Create MDI frame window", TRUE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_frame, "wrong focus window %p\n", GetFocus());
+
+    trace("creating MDI client window\n");
+    client_cs.hWindowMenu = 0;
+    client_cs.idFirstChild = MDI_FIRST_CHILD_ID;
+    mdi_client = CreateWindowExA(0, "MDI_client_class",
+                                 NULL,
+                                 WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES,
+                                 0, 0, 0, 0,
+                                 mdi_frame, 0, GetModuleHandleA(0), &client_cs);
+    assert(mdi_client);
+    ok_sequence(WmCreateMDIclientSeq, "Create visible MDI client window", FALSE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_frame, "input focus should be on MDI frame not on %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(!active_child, "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+    SetFocus(0);
+    flush_sequence();
+
+    trace("creating invisible MDI child window\n");
+    mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child);
+
+    flush_sequence();
+    ShowWindow(mdi_child, SW_SHOWNORMAL);
+    ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOWNORMAL) MDI child window", FALSE);
+
+    ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+    ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(!active_child, "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+    ShowWindow(mdi_child, SW_HIDE);
+    ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE) MDI child window", FALSE);
+    flush_sequence();
+
+    ShowWindow(mdi_child, SW_SHOW);
+    ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW) MDI child window", FALSE);
+
+    ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+    ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(!active_child, "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+    DestroyWindow(mdi_child);
+    flush_sequence();
+
+    trace("creating visible MDI child window\n");
+    mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD | WS_VISIBLE,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child);
+    ok_sequence(WmCreateMDIchildVisibleSeq, "Create visible MDI child window", FALSE);
+
+    ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+    ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    DestroyWindow(mdi_child);
+    ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    /* Win2k: MDI client still returns a just destroyed child as active
+     * Win9x: MDI client returns 0
+     */
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child || /* win2k */
+       !active_child, /* win9x */
+       "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+    flush_sequence();
+
+    trace("creating invisible MDI child window\n");
+    mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child2);
+    ok_sequence(WmCreateMDIchildInvisibleSeq, "Create invisible MDI child window", FALSE);
+
+    ok(!(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE), "MDI child should not be visible\n");
+    ok(!IsWindowVisible(mdi_child2), "MDI child should not be visible\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    /* Win2k: MDI client still returns a just destroyed child as active
+     * Win9x: MDI client returns mdi_child2
+     */
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child || /* win2k */
+       active_child == mdi_child2, /* win9x */
+       "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    ShowWindow(mdi_child2, SW_MAXIMIZE);
+    ok_sequence(WmMaximizeMDIchildInvisibleSeq, "ShowWindow(SW_MAXIMIZE):invisible MDI child", TRUE);
+
+    ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+    ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n");
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child2 || /* win2k */
+       GetFocus() == 0, /* win9x */
+       "wrong focus window %p\n", GetFocus());
+
+    SetFocus(0);
+    flush_sequence();
+
+    ShowWindow(mdi_child2, SW_HIDE);
+    ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+    ShowWindow(mdi_child2, SW_RESTORE);
+    ok_sequence(WmRestoreMDIchildInisibleSeq, "ShowWindow(SW_RESTORE):invisible MDI child", TRUE);
+    flush_sequence();
+
+    ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+    ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n");
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+    ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    SetFocus(0);
+    flush_sequence();
+
+    ShowWindow(mdi_child2, SW_HIDE);
+    ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+    ShowWindow(mdi_child2, SW_SHOW);
+    ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):MDI child", FALSE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    ShowWindow(mdi_child2, SW_MAXIMIZE);
+    ok_sequence(WmMaximizeMDIchildVisibleSeq, "ShowWindow(SW_MAXIMIZE):MDI child", TRUE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    ShowWindow(mdi_child2, SW_RESTORE);
+    ok_sequence(WmRestoreMDIchildVisibleSeq, "ShowWindow(SW_RESTORE):MDI child", TRUE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    SetFocus(0);
+    flush_sequence();
+
+    ShowWindow(mdi_child2, SW_HIDE);
+    ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    DestroyWindow(mdi_child2);
+    ok_sequence(WmDestroyMDIchildInvisibleSeq, "Destroy invisible MDI child window", FALSE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    /* test for maximized MDI children */
+    trace("creating maximized visible MDI child window 1\n");
+    mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child);
+    ok_sequence(WmCreateMDIchildVisibleMaxSeq1, "Create maximized visible 1st MDI child window", TRUE);
+    ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child || /* win2k */
+       GetFocus() == 0, /* win9x */
+       "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    trace("creating maximized visible MDI child window 2\n");
+    mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child2);
+    ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE);
+    ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n");
+    ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    trace("destroying maximized visible MDI child window 2\n");
+    DestroyWindow(mdi_child2);
+    ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+    ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    /* Win2k: MDI client still returns a just destroyed child as active
+     * Win9x: MDI client returns 0
+     */
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child2 || /* win2k */
+       !active_child, /* win9x */
+       "wrong active MDI child %p\n", active_child);
+    flush_sequence();
+
+    ShowWindow(mdi_child, SW_MAXIMIZE);
+    ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+    flush_sequence();
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+    trace("re-creating maximized visible MDI child window 2\n");
+    mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+                                WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+                                0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+                                mdi_client, 0, GetModuleHandleA(0), NULL);
+    assert(mdi_child2);
+    ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE);
+    ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n");
+    ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    SendMessageA(mdi_child2, WM_SYSCOMMAND, SC_CLOSE, 0);
+    ok_sequence(WmDestroyMDIchildVisibleMaxSeq2, "WM_SYSCOMMAND/SC_CLOSE on a visible maximized MDI child window", TRUE);
+    ok(!IsWindow(mdi_child2), "MDI child 2 should be destroyed\n");
+
+    ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    DestroyWindow(mdi_child);
+    ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+    /* Win2k: MDI client still returns a just destroyed child as active
+     * Win9x: MDI client returns 0
+     */
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child || /* win2k */
+       !active_child, /* win9x */
+       "wrong active MDI child %p\n", active_child);
+    flush_sequence();
+    /* end of test for maximized MDI children */
+
+    mdi_cs.szClass = "MDI_child_Class";
+    mdi_cs.szTitle = "MDI child";
+    mdi_cs.hOwner = GetModuleHandleA(0);
+    mdi_cs.x = 0;
+    mdi_cs.y = 0;
+    mdi_cs.cx = CW_USEDEFAULT;
+    mdi_cs.cy = CW_USEDEFAULT;
+    mdi_cs.style = WS_CHILD | WS_SYSMENU | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE;
+    mdi_cs.lParam = 0;
+    mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+    ok(mdi_child != 0, "MDI child creation failed\n");
+    ok_sequence(WmCreateMDIchildVisibleMaxSeq3, "WM_MDICREATE for maximized visible MDI child window", TRUE);
+
+    ok(GetMenuItemID(hMenu, GetMenuItemCount(hMenu) - 1) == SC_CLOSE, "SC_CLOSE menu item not found\n");
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+
+    ok(IsZoomed(mdi_child), "MDI child should be maximized\n");
+    ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+    ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+    ok(zoomed, "wrong zoomed state %d\n", zoomed);
+    flush_sequence();
+
+    SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+    ok_sequence(WmDestroyMDIchildVisibleMaxSeq1, "Destroy visible maximized MDI child window", TRUE);
+
+    ok(!IsWindow(mdi_child), "MDI child should be destroyed\n");
+    active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+    ok(!active_child, "wrong active MDI child %p\n", active_child);
+
+    SetFocus(0);
+    flush_sequence();
+
+    DestroyWindow(mdi_client);
+    ok_sequence(WmDestroyMDIclientSeq, "Destroy MDI client window", FALSE);
+
+    DestroyWindow(mdi_frame);
+    ok_sequence(WmDestroyMDIframeSeq, "Destroy MDI frame window", FALSE);
+}
+/************************* End of MDI test **********************************/
+
+static void test_WM_SETREDRAW(HWND hwnd)
+{
+    DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+
+    flush_sequence();
+
+    SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
+    ok_sequence(WmSetRedrawFalseSeq, "SetRedraw:FALSE", FALSE);
+
+    ok(!(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should NOT be set\n");
+    ok(!IsWindowVisible(hwnd), "IsWindowVisible() should return FALSE\n");
+
+    flush_sequence();
+    SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
+    ok_sequence(WmSetRedrawTrueSeq, "SetRedraw:TRUE", FALSE);
+
+    ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+    ok(IsWindowVisible(hwnd), "IsWindowVisible() should return TRUE\n");
+
+    /* restore original WS_VISIBLE state */
+    SetWindowLongA(hwnd, GWL_STYLE, style);
+
+    flush_sequence();
+}
+
+static INT_PTR CALLBACK TestModalDlgProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    struct message msg;
+
+    trace("dialog: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+    switch (message)
+    {
+        case WM_WINDOWPOSCHANGING:
+        case WM_WINDOWPOSCHANGED:
+        {
+            WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+            trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+            trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+                  winpos->hwnd, winpos->hwndInsertAfter,
+                  winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+            /* Log only documented flags, win2k uses 0x1000 and 0x2000
+             * in the high word for internal purposes
+             */
+            wParam = winpos->flags & 0xffff;
+            break;
+        }
+    }
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    add_message(&msg);
+
+    if (message == WM_INITDIALOG) SetTimer( hwnd, 1, 100, NULL );
+    if (message == WM_TIMER) EndDialog( hwnd, 0 );
+    return 0;
+}
+
+static void test_hv_scroll_1(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max)
+{
+    DWORD style, exstyle;
+    INT xmin, xmax;
+    BOOL ret;
+
+    exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    /* do not be confused by WS_DLGFRAME set */
+    if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION;
+
+    if (clear) ok(style & clear, "style %08lx should be set\n", clear);
+    if (set) ok(!(style & set), "style %08lx should not be set\n", set);
+
+    ret = SetScrollRange(hwnd, ctl, min, max, FALSE);
+    ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError());
+    if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME))
+        ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollRange(SB_HORZ/SB_VERT) NC", FALSE);
+    else
+        ok_sequence(WmSetScrollRangeHVSeq, "SetScrollRange(SB_HORZ/SB_VERT)", FALSE);
+
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    if (set) ok(style & set, "style %08lx should be set\n", set);
+    if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear);
+
+    /* a subsequent call should do nothing */
+    ret = SetScrollRange(hwnd, ctl, min, max, FALSE);
+    ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError());
+    ok_sequence(WmEmptySeq, "SetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+    xmin = 0xdeadbeef;
+    xmax = 0xdeadbeef;
+    trace("Ignore GetScrollRange error below if you are on Win9x\n");
+    ret = GetScrollRange(hwnd, ctl, &xmin, &xmax);
+    ok( ret, "GetScrollRange(%d) error %ld\n", ctl, GetLastError());
+    ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+    ok(xmin == min, "unexpected min scroll value %d\n", xmin);
+    ok(xmax == max, "unexpected max scroll value %d\n", xmax);
+}
+
+static void test_hv_scroll_2(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max)
+{
+    DWORD style, exstyle;
+    SCROLLINFO si;
+    BOOL ret;
+
+    exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    /* do not be confused by WS_DLGFRAME set */
+    if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION;
+
+    if (clear) ok(style & clear, "style %08lx should be set\n", clear);
+    if (set) ok(!(style & set), "style %08lx should not be set\n", set);
+
+    si.cbSize = sizeof(si);
+    si.fMask = SIF_RANGE;
+    si.nMin = min;
+    si.nMax = max;
+    SetScrollInfo(hwnd, ctl, &si, TRUE);
+    if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME))
+        ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollInfo(SB_HORZ/SB_VERT) NC", FALSE);
+    else
+        ok_sequence(WmSetScrollRangeHVSeq, "SetScrollInfo(SB_HORZ/SB_VERT)", FALSE);
+
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    if (set) ok(style & set, "style %08lx should be set\n", set);
+    if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear);
+
+    /* a subsequent call should do nothing */
+    SetScrollInfo(hwnd, ctl, &si, TRUE);
+    if (style & WS_HSCROLL)
+        ok_sequence(WmSetScrollRangeHSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+    else if (style & WS_VSCROLL)
+        ok_sequence(WmSetScrollRangeVSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+    else
+        ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+    si.fMask = SIF_PAGE;
+    si.nPage = 5;
+    SetScrollInfo(hwnd, ctl, &si, FALSE);
+    ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+    si.fMask = SIF_POS;
+    si.nPos = max - 1;
+    SetScrollInfo(hwnd, ctl, &si, FALSE);
+    ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+    si.fMask = SIF_RANGE;
+    si.nMin = 0xdeadbeef;
+    si.nMax = 0xdeadbeef;
+    ret = GetScrollInfo(hwnd, ctl, &si);
+    ok( ret, "GetScrollInfo error %ld\n", GetLastError());
+    ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+    ok(si.nMin == min, "unexpected min scroll value %d\n", si.nMin);
+    ok(si.nMax == max, "unexpected max scroll value %d\n", si.nMax);
+}
+
+/* Win9x sends WM_USER+xxx while and NT versions send SBM_xxx messages */
+static void test_scroll_messages(HWND hwnd)
+{
+    SCROLLINFO si;
+    INT min, max;
+    BOOL ret;
+
+    min = 0xdeadbeef;
+    max = 0xdeadbeef;
+    ret = GetScrollRange(hwnd, SB_CTL, &min, &max);
+    ok( ret, "GetScrollRange error %ld\n", GetLastError());
+    if (sequence->message != WmGetScrollRangeSeq[0].message)
+        trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+    /* values of min and max are undefined */
+    flush_sequence();
+
+    ret = SetScrollRange(hwnd, SB_CTL, 10, 150, FALSE);
+    ok( ret, "SetScrollRange error %ld\n", GetLastError());
+    if (sequence->message != WmSetScrollRangeSeq[0].message)
+        trace("SetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+    flush_sequence();
+
+    min = 0xdeadbeef;
+    max = 0xdeadbeef;
+    ret = GetScrollRange(hwnd, SB_CTL, &min, &max);
+    ok( ret, "GetScrollRange error %ld\n", GetLastError());
+    if (sequence->message != WmGetScrollRangeSeq[0].message)
+        trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+    /* values of min and max are undefined */
+    flush_sequence();
+
+    si.cbSize = sizeof(si);
+    si.fMask = SIF_RANGE;
+    si.nMin = 20;
+    si.nMax = 160;
+    SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+    if (sequence->message != WmSetScrollRangeSeq[0].message)
+        trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+    flush_sequence();
+
+    si.fMask = SIF_PAGE;
+    si.nPage = 10;
+    SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+    if (sequence->message != WmSetScrollRangeSeq[0].message)
+        trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+    flush_sequence();
+
+    si.fMask = SIF_POS;
+    si.nPos = 20;
+    SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+    if (sequence->message != WmSetScrollRangeSeq[0].message)
+        trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+    flush_sequence();
+
+    si.fMask = SIF_RANGE;
+    si.nMin = 0xdeadbeef;
+    si.nMax = 0xdeadbeef;
+    ret = GetScrollInfo(hwnd, SB_CTL, &si);
+    ok( ret, "GetScrollInfo error %ld\n", GetLastError());
+    if (sequence->message != WmGetScrollInfoSeq[0].message)
+        trace("GetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+    /* values of min and max are undefined */
+    flush_sequence();
+
+    /* set WS_HSCROLL */
+    test_hv_scroll_1(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150);
+    /* clear WS_HSCROLL */
+    test_hv_scroll_1(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0);
+
+    /* set WS_HSCROLL */
+    test_hv_scroll_2(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150);
+    /* clear WS_HSCROLL */
+    test_hv_scroll_2(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0);
+
+    /* set WS_VSCROLL */
+    test_hv_scroll_1(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150);
+    /* clear WS_VSCROLL */
+    test_hv_scroll_1(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0);
+
+    /* set WS_VSCROLL */
+    test_hv_scroll_2(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150);
+    /* clear WS_VSCROLL */
+    test_hv_scroll_2(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0);
+}
+
+static void test_showwindow(void)
+{
+    HWND hwnd, hchild;
+
+    hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+                           100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hwnd != 0, "Failed to create overlapped window\n");
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+                             0, 0, 10, 10, hwnd, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child\n");
+    flush_sequence();
+
+    /* ShowWindow( SW_SHOWNA) for invisible top level window */
+    trace("calling ShowWindow( SW_SHOWNA) for invisible top level window\n");
+    ok( ShowWindow(hwnd, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+    ok_sequence(WmSHOWNATopInvisible, "ShowWindow(SW_SHOWNA) on invisible top level window", TRUE);
+    trace("done\n");
+
+    /* ShowWindow( SW_SHOWNA) for now visible top level window */
+    trace("calling ShowWindow( SW_SHOWNA) for now visible top level window\n");
+    ok( ShowWindow(hwnd, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+    ok_sequence(WmSHOWNATopVisible, "ShowWindow(SW_SHOWNA) on visible top level window", FALSE);
+    trace("done\n");
+    /* back to invisible */
+    ShowWindow(hchild, SW_HIDE);
+    ShowWindow(hwnd, SW_HIDE);
+    flush_sequence();
+    /* ShowWindow(SW_SHOWNA) with child and parent invisible */ 
+    trace("calling ShowWindow( SW_SHOWNA) for invisible child with invisible parent\n");
+    ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+    ok_sequence(WmSHOWNAChildInvisParInvis, "ShowWindow(SW_SHOWNA) invisible child and parent", TRUE);
+    trace("done\n");
+    /* ShowWindow(SW_SHOWNA) with child visible and parent invisible */ 
+    ok( ShowWindow(hchild, SW_SHOW) != FALSE, "ShowWindow: window was invisible\n" );
+    flush_sequence();
+    trace("calling ShowWindow( SW_SHOWNA) for the visible child and invisible parent\n");
+    ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+    ok_sequence(WmSHOWNAChildVisParInvis, "ShowWindow(SW_SHOWNA) visible child and invisible parent", TRUE);
+    trace("done\n");
+    /* ShowWindow(SW_SHOWNA) with child visible and parent visible */
+    ShowWindow( hwnd, SW_SHOW);
+    flush_sequence();
+    trace("calling ShowWindow( SW_SHOWNA) for the visible child and parent\n");
+    ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+    ok_sequence(WmSHOWNAChildVisParVis, "ShowWindow(SW_SHOWNA) for the visible child and parent", FALSE);
+    trace("done\n");
+
+    /* ShowWindow(SW_SHOWNA) with child invisible and parent visible */
+    ShowWindow( hchild, SW_HIDE);
+    flush_sequence();
+    trace("calling ShowWindow( SW_SHOWNA) for the invisible child and visible parent\n");
+    ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+    ok_sequence(WmSHOWNAChildInvisParVis, "ShowWindow(SW_SHOWNA) for the invisible child and visible parent", FALSE);
+    trace("done\n");
+
+    SetCapture(hchild);
+    ok(GetCapture() == hchild, "wrong capture window %p\n", GetCapture());
+    DestroyWindow(hchild);
+    ok(!GetCapture(), "wrong capture window %p\n", GetCapture());
+
+    DestroyWindow(hwnd);
+    flush_sequence();
+}
+
+static void test_sys_menu(HWND hwnd)
+{
+    HMENU hmenu;
+    UINT state;
+
+    /* test existing window without CS_NOCLOSE style */
+    hmenu = GetSystemMenu(hwnd, FALSE);
+    ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError());
+
+    state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+    ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+    ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state);
+
+    EnableMenuItem(hmenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
+    ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE);
+
+    state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+    ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+    ok((state & (MF_DISABLED | MF_GRAYED)) == MF_GRAYED, "wrong SC_CLOSE state %x\n", state);
+
+    EnableMenuItem(hmenu, SC_CLOSE, 0);
+    ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE);
+
+    state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+    ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+    ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state);
+
+    /* test new window with CS_NOCLOSE style */
+    hwnd = CreateWindowExA(0, "NoCloseWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+                           100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hwnd != 0, "Failed to create overlapped window\n");
+
+    hmenu = GetSystemMenu(hwnd, FALSE);
+    ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError());
+
+    state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+    ok(state == 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+
+    DestroyWindow(hwnd);
+}
+
+/* test if we receive the right sequence of messages */
+static void test_messages(void)
+{
+    HWND hwnd, hparent, hchild;
+    HWND hchild2, hbutton;
+    HMENU hmenu;
+    MSG msg;
+    DWORD ret;
+
+    flush_sequence();
+
+    hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+                           100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hwnd != 0, "Failed to create overlapped window\n");
+    ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE);
+
+    /* test ShowWindow(SW_HIDE) on a newly created invisible window */
+    ok( ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow: window was visible\n" );
+    ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped, invisible", FALSE);
+
+    /* test WM_SETREDRAW on a not visible top level window */
+    test_WM_SETREDRAW(hwnd);
+
+    SetWindowPos(hwnd, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+    ok_sequence(WmSWP_ShowOverlappedSeq, "SetWindowPos:SWP_SHOWWINDOW:overlapped", FALSE);
+    ok(IsWindowVisible(hwnd), "window should be visible at this point\n");
+
+    ok(GetActiveWindow() == hwnd, "window should be active\n");
+    ok(GetFocus() == hwnd, "window should have input focus\n");
+    ShowWindow(hwnd, SW_HIDE);
+    ok_sequence(WmHideOverlappedSeq, "ShowWindow(SW_HIDE):overlapped", TRUE);
+    
+    ShowWindow(hwnd, SW_SHOW);
+    ok_sequence(WmShowOverlappedSeq, "ShowWindow(SW_SHOW):overlapped", TRUE);
+
+    ShowWindow(hwnd, SW_SHOW);
+    ok_sequence(WmEmptySeq, "ShowWindow(SW_SHOW):overlapped already visible", FALSE);
+
+    ok(GetActiveWindow() == hwnd, "window should be active\n");
+    ok(GetFocus() == hwnd, "window should have input focus\n");
+    SetWindowPos(hwnd, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+    ok_sequence(WmSWP_HideOverlappedSeq, "SetWindowPos:SWP_HIDEWINDOW:overlapped", FALSE);
+    ok(!IsWindowVisible(hwnd), "window should not be visible at this point\n");
+    ok(GetActiveWindow() == hwnd, "window should still be active\n");
+
+    /* test WM_SETREDRAW on a visible top level window */
+    ShowWindow(hwnd, SW_SHOW);
+    test_WM_SETREDRAW(hwnd);
+
+    trace("testing scroll APIs on a visible top level window %p\n", hwnd);
+    test_scroll_messages(hwnd);
+
+    /* test resizing and moving */
+    SetWindowPos( hwnd, 0, 0, 0, 300, 300, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
+    ok_sequence(WmSWP_ResizeSeq, "SetWindowPos:Resize", FALSE );
+    SetWindowPos( hwnd, 0, 200, 200, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE );
+    ok_sequence(WmSWP_MoveSeq, "SetWindowPos:Move", FALSE );
+
+    /* popups don't get WM_GETMINMAXINFO */
+    SetWindowLongW( hwnd, GWL_STYLE, WS_VISIBLE|WS_POPUP );
+    SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED);
+    flush_sequence();
+    SetWindowPos( hwnd, 0, 0, 0, 200, 200, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
+    ok_sequence(WmSWP_ResizePopupSeq, "SetWindowPos:ResizePopup", FALSE );
+
+    test_sys_menu(hwnd);
+
+    flush_sequence();
+    DestroyWindow(hwnd);
+    ok_sequence(WmDestroyOverlappedSeq, "DestroyWindow:overlapped", FALSE);
+
+    hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+                              100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hparent != 0, "Failed to create parent window\n");
+    flush_sequence();
+
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_MAXIMIZE,
+                             0, 0, 10, 10, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+    ok_sequence(WmCreateMaximizedChildSeq, "CreateWindow:maximized child", TRUE);
+    DestroyWindow(hchild);
+    flush_sequence();
+
+    /* visible child window with a caption */
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child",
+                             WS_CHILD | WS_VISIBLE | WS_CAPTION,
+                             0, 0, 10, 10, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+    ok_sequence(WmCreateVisibleChildSeq, "CreateWindow:visible child", FALSE);
+
+    trace("testing scroll APIs on a visible child window %p\n", hchild);
+    test_scroll_messages(hchild);
+
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+    ok_sequence(WmShowChildSeq_4, "SetWindowPos(SWP_SHOWWINDOW):child with a caption", FALSE);
+
+    DestroyWindow(hchild);
+    flush_sequence();
+
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+                             0, 0, 10, 10, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+    ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE);
+    
+    hchild2 = CreateWindowExA(0, "SimpleWindowClass", "Test child2", WS_CHILD,
+                               100, 100, 50, 50, hparent, 0, 0, NULL);
+    ok (hchild2 != 0, "Failed to create child2 window\n");
+    flush_sequence();
+
+    hbutton = CreateWindowExA(0, "TestWindowClass", "Test button", WS_CHILD,
+                              0, 100, 50, 50, hchild, 0, 0, NULL);
+    ok (hbutton != 0, "Failed to create button window\n");
+
+    /* test WM_SETREDRAW on a not visible child window */
+    test_WM_SETREDRAW(hchild);
+
+    ShowWindow(hchild, SW_SHOW);
+    ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE);
+
+    ShowWindow(hchild, SW_HIDE);
+    ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):child", FALSE);
+
+    ShowWindow(hchild, SW_SHOW);
+    ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE);
+
+    /* test WM_SETREDRAW on a visible child window */
+    test_WM_SETREDRAW(hchild);
+
+    MoveWindow(hchild, 10, 10, 20, 20, TRUE);
+    ok_sequence(WmResizingChildWithMoveWindowSeq, "MoveWindow:child", FALSE);
+
+    ShowWindow(hchild, SW_HIDE);
+    flush_sequence();
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+    ok_sequence(WmShowChildSeq_2, "SetWindowPos:show_child_2", FALSE);
+
+    ShowWindow(hchild, SW_HIDE);
+    flush_sequence();
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
+    ok_sequence(WmShowChildSeq_3, "SetWindowPos:show_child_3", FALSE);
+
+    /* DestroyWindow sequence below expects that a child has focus */
+    SetFocus(hchild);
+    flush_sequence();
+
+    DestroyWindow(hchild);
+    ok_sequence(WmDestroyChildSeq, "DestroyWindow:child", FALSE);
+    DestroyWindow(hchild2);
+    DestroyWindow(hbutton);
+
+    flush_sequence();
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test Child Popup", WS_CHILD | WS_POPUP,
+                             0, 0, 100, 100, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child popup window\n");
+    ok_sequence(WmCreateChildPopupSeq, "CreateWindow:child_popup", FALSE);
+    DestroyWindow(hchild);
+
+    /* test what happens to a window which sets WS_VISIBLE in WM_CREATE */
+    flush_sequence();
+    hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP,
+                             0, 0, 100, 100, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create popup window\n");
+    ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE);
+    ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+    ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n");
+    flush_sequence();
+    ShowWindow(hchild, SW_SHOW);
+    ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE);
+    flush_sequence();
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+    ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE);
+    flush_sequence();
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+    ok_sequence(WmShowVisiblePopupSeq_3, "SetWindowPos:show_visible_popup_3", FALSE);
+    DestroyWindow(hchild);
+
+    /* this time add WS_VISIBLE for CreateWindowEx, but this fact actually
+     * changes nothing in message sequences.
+     */
+    flush_sequence();
+    hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP | WS_VISIBLE,
+                             0, 0, 100, 100, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create popup window\n");
+    ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE);
+    ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+    ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n");
+    flush_sequence();
+    ShowWindow(hchild, SW_SHOW);
+    ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE);
+    flush_sequence();
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+    ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE);
+    DestroyWindow(hchild);
+
+    flush_sequence();
+    hwnd = CreateWindowExA(WS_EX_DLGMODALFRAME, "TestDialogClass", NULL, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_DLGFRAME,
+                           0, 0, 100, 100, hparent, 0, 0, NULL);
+    ok(hwnd != 0, "Failed to create custom dialog window\n");
+    ok_sequence(WmCreateCustomDialogSeq, "CreateCustomDialog", TRUE);
+
+    /*
+    trace("testing scroll APIs on a visible dialog %p\n", hwnd);
+    test_scroll_messages(hwnd);
+    */
+
+    flush_sequence();
+    after_end_dialog = 1;
+    EndDialog( hwnd, 0 );
+    ok_sequence(WmEndCustomDialogSeq, "EndCustomDialog", FALSE);
+
+    DestroyWindow(hwnd);
+    after_end_dialog = 0;
+
+    hwnd = CreateWindowExA(0, "TestDialogClass", NULL, WS_POPUP,
+                           0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL);
+    ok(hwnd != 0, "Failed to create custom dialog window\n");
+    flush_sequence();
+    trace("call ShowWindow(%p, SW_SHOW)\n", hwnd);
+    ShowWindow(hwnd, SW_SHOW);
+    ok_sequence(WmShowCustomDialogSeq, "ShowCustomDialog", TRUE);
+    DestroyWindow(hwnd);
+
+    flush_sequence();
+    DialogBoxA( 0, "TEST_DIALOG", hparent, TestModalDlgProcA );
+    ok_sequence(WmModalDialogSeq, "ModalDialog", TRUE);
+
+    /* test showing child with hidden parent */
+    ShowWindow( hparent, SW_HIDE );
+    flush_sequence();
+
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+                             0, 0, 10, 10, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+    ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE);
+
+    ShowWindow( hchild, SW_SHOW );
+    ok_sequence(WmShowChildInvisibleParentSeq, "ShowWindow:show child with invisible parent", TRUE);
+    ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+    ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+    ShowWindow( hchild, SW_HIDE );
+    ok_sequence(WmHideChildInvisibleParentSeq, "ShowWindow:hide child with invisible parent", TRUE);
+    ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should be not set\n");
+    ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+    ok_sequence(WmShowChildInvisibleParentSeq_2, "SetWindowPos:show child with invisible parent", FALSE);
+    ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+    ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+    SetWindowPos(hchild, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+    ok_sequence(WmHideChildInvisibleParentSeq_2, "SetWindowPos:hide child with invisible parent", FALSE);
+    ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should not be set\n");
+    ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+    DestroyWindow(hchild);
+    DestroyWindow(hparent);
+    flush_sequence();
+
+    /* Message sequence for SetMenu */
+    ok(!DrawMenuBar(hwnd), "DrawMenuBar should return FALSE for a window without a menu\n");
+    ok_sequence(WmEmptySeq, "DrawMenuBar for a window without a menu", FALSE);
+
+    hmenu = CreateMenu();
+    ok (hmenu != 0, "Failed to create menu\n");
+    ok (InsertMenuA(hmenu, -1, MF_BYPOSITION, 0x1000, "foo"), "InsertMenu failed\n");
+    hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+                           100, 100, 200, 200, 0, hmenu, 0, NULL);
+    ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE);
+    ok (SetMenu(hwnd, 0), "SetMenu\n");
+    ok_sequence(WmSetMenuNonVisibleSizeChangeSeq, "SetMenu:NonVisibleSizeChange", FALSE);
+    ok (SetMenu(hwnd, 0), "SetMenu\n");
+    ok_sequence(WmSetMenuNonVisibleNoSizeChangeSeq, "SetMenu:NonVisibleNoSizeChange", FALSE);
+    ShowWindow(hwnd, SW_SHOW);
+    flush_sequence();
+    ok (SetMenu(hwnd, 0), "SetMenu\n");
+    ok_sequence(WmSetMenuVisibleNoSizeChangeSeq, "SetMenu:VisibleNoSizeChange", TRUE);
+    ok (SetMenu(hwnd, hmenu), "SetMenu\n");
+    ok_sequence(WmSetMenuVisibleSizeChangeSeq, "SetMenu:VisibleSizeChange", TRUE);
+
+    ok(DrawMenuBar(hwnd), "DrawMenuBar\n");
+    ok_sequence(WmDrawMenuBarSeq, "DrawMenuBar", TRUE);
+
+    DestroyWindow(hwnd);
+    flush_sequence();
+
+    /* Message sequence for EnableWindow */
+    hparent = CreateWindowExA(0, "TestWindowClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+                              100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hparent != 0, "Failed to create parent window\n");
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE,
+                             0, 0, 10, 10, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+
+    SetFocus(hchild);
+    flush_sequence();
+
+    EnableWindow(hparent, FALSE);
+    ok_sequence(WmEnableWindowSeq_1, "EnableWindow(FALSE)", FALSE);
+
+    EnableWindow(hparent, TRUE);
+    ok_sequence(WmEnableWindowSeq_2, "EnableWindow(TRUE)", FALSE);
+
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    flush_sequence();
+
+    /* MsgWaitForMultipleObjects test */
+    ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+    ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret);
+
+    PostMessageA(hparent, WM_USER, 0, 0);
+
+    ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+    ok(ret == WAIT_OBJECT_0, "MsgWaitForMultipleObjects returned %lx\n", ret);
+
+    ok(PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n");
+    ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+    ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+    ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret);
+    /* end of MsgWaitForMultipleObjects test */
+
+    /* the following test causes an exception in user.exe under win9x */
+    if (!PostMessageW( hparent, WM_USER, 0, 0 )) return;
+    PostMessageW( hparent, WM_USER+1, 0, 0 );
+    /* PeekMessage(NULL) fails, but still removes the message */
+    SetLastError(0xdeadbeef);
+    ok( !PeekMessageW( NULL, 0, 0, 0, PM_REMOVE ), "PeekMessage(NULL) should fail\n" );
+    ok( GetLastError() == ERROR_NOACCESS || /* Win2k */
+        GetLastError() == 0xdeadbeef, /* NT4 */
+        "last error is %ld\n", GetLastError() );
+    ok( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n" );
+    ok( msg.message == WM_USER+1, "got %x instead of WM_USER+1\n", msg.message );
+
+    DestroyWindow(hchild);
+    DestroyWindow(hparent);
+    flush_sequence();
+
+    test_showwindow();
+}
+
+/****************** button message test *************************/
+static const struct message WmSetFocusButtonSeq[] =
+{
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam, 0 },
+    { WM_CTLCOLORBTN, sent|defwinproc },
+    { 0 }
+};
+static const struct message WmKillFocusButtonSeq[] =
+{
+    { HCBT_SETFOCUS, hook },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_KILLFOCUS, sent|wparam, 0 },
+    { WM_CTLCOLORBTN, sent|defwinproc },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { 0 }
+};
+static const struct message WmSetFocusStaticSeq[] =
+{
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam, 0 },
+    { WM_CTLCOLORSTATIC, sent|defwinproc },
+    { 0 }
+};
+static const struct message WmKillFocusStaticSeq[] =
+{
+    { HCBT_SETFOCUS, hook },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_KILLFOCUS, sent|wparam, 0 },
+    { WM_CTLCOLORSTATIC, sent|defwinproc },
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+    { 0 }
+};
+static const struct message WmLButtonDownSeq[] =
+{
+    { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
+    { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+    { HCBT_SETFOCUS, hook },
+    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+    { WM_CTLCOLORBTN, sent|defwinproc },
+    { BM_SETSTATE, sent|wparam|defwinproc, TRUE },
+    { WM_CTLCOLORBTN, sent|defwinproc },
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { 0 }
+};
+static const struct message WmLButtonUpSeq[] =
+{
+    { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
+    { BM_SETSTATE, sent|wparam|defwinproc, FALSE },
+    { WM_CTLCOLORBTN, sent|defwinproc },
+    { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+    { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 },
+    { 0 }
+};
+
+static WNDPROC old_button_proc;
+
+static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    trace("button: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    add_message(&msg);
+
+    if (message == BM_SETSTATE)
+       ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
+
+    defwndproc_counter++;
+    ret = CallWindowProcA(old_button_proc, hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
+static void subclass_button(void)
+{
+    WNDCLASSA cls;
+
+    if (!GetClassInfoA(0, "button", &cls)) assert(0);
+
+    old_button_proc = cls.lpfnWndProc;
+
+    cls.hInstance = GetModuleHandle(0);
+    cls.lpfnWndProc = button_hook_proc;
+    cls.lpszClassName = "my_button_class";
+    if (!RegisterClassA(&cls)) assert(0);
+}
+
+static void test_button_messages(void)
+{
+    static const struct
+    {
+       DWORD style;
+       DWORD dlg_code;
+       const struct message *setfocus;
+       const struct message *killfocus;
+    } button[] = {
+       { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+       { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+       { BS_CHECKBOX, DLGC_BUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_AUTOCHECKBOX, DLGC_BUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_3STATE, DLGC_BUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_AUTO3STATE, DLGC_BUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_GROUPBOX, DLGC_STATIC,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+       { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
+         WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+       { BS_OWNERDRAW, DLGC_BUTTON,
+         WmSetFocusButtonSeq, WmKillFocusButtonSeq }
+    };
+    unsigned int i;
+    HWND hwnd;
+    DWORD dlg_code;
+
+    subclass_button();
+
+    for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
+    {
+       hwnd = CreateWindowExA(0, "my_button_class", "test", button[i].style | WS_POPUP,
+                              0, 0, 50, 14, 0, 0, 0, NULL);
+       ok(hwnd != 0, "Failed to create button window\n");
+
+       dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
+       ok(dlg_code == button[i].dlg_code, "%d: wrong dlg_code %08lx\n", i, dlg_code);
+
+       ShowWindow(hwnd, SW_SHOW);
+       UpdateWindow(hwnd);
+       SetFocus(0);
+       flush_sequence();
+
+       trace("button style %08lx\n", button[i].style);
+       SetFocus(hwnd);
+       ok_sequence(button[i].setfocus, "SetFocus(hwnd) on a button", FALSE);
+
+       SetFocus(0);
+       ok_sequence(button[i].killfocus, "SetFocus(0) on a button", FALSE);
+
+       DestroyWindow(hwnd);
+    }
+
+    hwnd = CreateWindowExA(0, "my_button_class", "test", BS_PUSHBUTTON | WS_POPUP | WS_VISIBLE,
+                          0, 0, 50, 14, 0, 0, 0, NULL);
+    ok(hwnd != 0, "Failed to create button window\n");
+
+    SetFocus(0);
+    flush_sequence();
+
+    SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
+    ok_sequence(WmLButtonDownSeq, "WM_LBUTTONDOWN on a button", FALSE);
+
+    SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
+    ok_sequence(WmLButtonUpSeq, "WM_LBUTTONUP on a button", FALSE);
+    DestroyWindow(hwnd);
+}
+
+/************* painting message test ********************/
+
+void dump_region(HRGN hrgn)
+{
+    DWORD i, size;
+    RGNDATA *data = NULL;
+    RECT *rect;
+
+    if (!hrgn)
+    {
+        printf( "null region\n" );
+        return;
+    }
+    if (!(size = GetRegionData( hrgn, 0, NULL ))) return;
+    if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return;
+    GetRegionData( hrgn, size, data );
+    printf("%ld rects:", data->rdh.nCount );
+    for (i = 0, rect = (RECT *)data->Buffer; i < data->rdh.nCount; i++, rect++)
+        printf( " (%ld,%ld)-(%ld,%ld)", rect->left, rect->top, rect->right, rect->bottom );
+    printf("\n");
+    HeapFree( GetProcessHeap(), 0, data );
+}
+
+static void check_update_rgn( HWND hwnd, HRGN hrgn )
+{
+    INT ret;
+    RECT r1, r2;
+    HRGN tmp = CreateRectRgn( 0, 0, 0, 0 );
+    HRGN update = CreateRectRgn( 0, 0, 0, 0 );
+
+    ret = GetUpdateRgn( hwnd, update, FALSE );
+    ok( ret != ERROR, "GetUpdateRgn failed\n" );
+    if (ret == NULLREGION)
+    {
+        ok( !hrgn, "Update region shouldn't be empty\n" );
+    }
+    else
+    {
+        if (CombineRgn( tmp, hrgn, update, RGN_XOR ) != NULLREGION)
+        {
+            ok( 0, "Regions are different\n" );
+            if (winetest_debug > 0)
+            {
+                printf( "Update region: " );
+                dump_region( update );
+                printf( "Wanted region: " );
+                dump_region( hrgn );
+            }
+        }
+    }
+    GetRgnBox( update, &r1 );
+    GetUpdateRect( hwnd, &r2, FALSE );
+    ok( r1.left == r2.left && r1.top == r2.top && r1.right == r2.right && r1.bottom == r2.bottom,
+        "Rectangles are different: %ld,%ld-%ld,%ld / %ld,%ld-%ld,%ld\n",
+        r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom );
+
+    DeleteObject( tmp );
+    DeleteObject( update );
+}
+
+static const struct message WmInvalidateRgn[] = {
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { 0 }
+};
+
+static const struct message WmGetUpdateRect[] = {
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_PAINT, sent },
+    { 0 }
+};
+
+static const struct message WmInvalidateFull[] = {
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { 0 }
+};
+
+static const struct message WmInvalidateErase[] = {
+    { WM_NCPAINT, sent|wparam, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent },
+    { 0 }
+};
+
+static const struct message WmInvalidatePaint[] = {
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|wparam|beginpaint, 1 },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { 0 }
+};
+
+static const struct message WmInvalidateErasePaint[] = {
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|wparam|beginpaint, 1 },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|beginpaint },
+    { 0 }
+};
+
+static const struct message WmInvalidateErasePaint2[] = {
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|beginpaint },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|beginpaint },
+    { 0 }
+};
+
+static const struct message WmErase[] = {
+    { WM_ERASEBKGND, sent },
+    { 0 }
+};
+
+static const struct message WmPaint[] = {
+    { WM_PAINT, sent },
+    { 0 }
+};
+
+static const struct message WmParentOnlyPaint[] = {
+    { WM_PAINT, sent|parent },
+    { 0 }
+};
+
+static const struct message WmInvalidateParent[] = {
+    { WM_NCPAINT, sent|parent },
+    { WM_GETTEXT, sent|defwinproc|parent|optional },
+    { WM_ERASEBKGND, sent|parent },
+    { 0 }
+};
+
+static const struct message WmInvalidateParentChild[] = {
+    { WM_NCPAINT, sent|parent },
+    { WM_GETTEXT, sent|defwinproc|parent|optional },
+    { WM_ERASEBKGND, sent|parent },
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent },
+    { 0 }
+};
+
+static const struct message WmInvalidateParentChild2[] = {
+    { WM_ERASEBKGND, sent|parent },
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent },
+    { 0 }
+};
+
+static const struct message WmParentPaint[] = {
+    { WM_PAINT, sent|parent },
+    { WM_PAINT, sent },
+    { 0 }
+};
+
+static const struct message WmParentPaintNc[] = {
+    { WM_PAINT, sent|parent },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|beginpaint },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|beginpaint },
+    { 0 }
+};
+
+static const struct message WmChildPaintNc[] = {
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|beginpaint },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|beginpaint },
+    { 0 }
+};
+
+static const struct message WmParentErasePaint[] = {
+    { WM_PAINT, sent|parent },
+    { WM_NCPAINT, sent|parent|beginpaint },
+    { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|parent|beginpaint },
+    { WM_PAINT, sent },
+    { WM_NCPAINT, sent|beginpaint },
+    { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+    { WM_ERASEBKGND, sent|beginpaint },
+    { 0 }
+};
+
+static const struct message WmParentOnlyNcPaint[] = {
+    { WM_PAINT, sent|parent },
+    { WM_NCPAINT, sent|parent|beginpaint },
+    { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional },
+    { 0 }
+};
+
+static const struct message WmSetParentStyle[] = {
+    { WM_STYLECHANGING, sent|parent },
+    { WM_STYLECHANGED, sent|parent },
+    { 0 }
+};
+
+static void test_paint_messages(void)
+{
+    RECT rect;
+    POINT pt;
+    MSG msg;
+    HWND hparent, hchild;
+    HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
+    HRGN hrgn2 = CreateRectRgn( 0, 0, 0, 0 );
+    HWND hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+                                100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hwnd != 0, "Failed to create overlapped window\n");
+
+    ShowWindow( hwnd, SW_SHOW );
+    UpdateWindow( hwnd );
+
+    /* try to flush pending X expose events */
+    MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+
+    check_update_rgn( hwnd, 0 );
+    SetRectRgn( hrgn, 10, 10, 20, 20 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE );
+    check_update_rgn( hwnd, hrgn );
+    SetRectRgn( hrgn2, 20, 20, 30, 30 );
+    RedrawWindow( hwnd, NULL, hrgn2, RDW_INVALIDATE );
+    CombineRgn( hrgn, hrgn, hrgn2, RGN_OR );
+    check_update_rgn( hwnd, hrgn );
+    /* validate everything */
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    check_update_rgn( hwnd, 0 );
+    /* now with frame */
+    SetRectRgn( hrgn, -5, -5, 20, 20 );
+
+    /* flush pending messages */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+    ok_sequence( WmEmptySeq, "EmptySeq", FALSE );
+
+    SetRectRgn( hrgn, 0, 0, 20, 20 );  /* GetUpdateRgn clips to client area */
+    check_update_rgn( hwnd, hrgn );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW );
+    ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW );
+    ok_sequence( WmInvalidateFull, "InvalidateFull", FALSE );
+
+    GetClientRect( hwnd, &rect );
+    SetRectRgn( hrgn, rect.left, rect.top, rect.right, rect.bottom );
+    check_update_rgn( hwnd, hrgn );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW );
+    ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW | RDW_UPDATENOW );
+    ok_sequence( WmInvalidatePaint, "InvalidatePaint", FALSE );
+    check_update_rgn( hwnd, 0 );
+
+    flush_sequence();
+    RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_UPDATENOW );
+    ok_sequence( WmInvalidateErasePaint, "InvalidateErasePaint", FALSE );
+    check_update_rgn( hwnd, 0 );
+
+    flush_sequence();
+    SetRectRgn( hrgn, 0, 0, 100, 100 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE );
+    SetRectRgn( hrgn, 0, 0, 50, 100 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE );
+    SetRectRgn( hrgn, 50, 0, 100, 100 );
+    check_update_rgn( hwnd, hrgn );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW );
+    ok_sequence( WmEmptySeq, "EmptySeq", FALSE );  /* must not generate messages, everything is valid */
+    check_update_rgn( hwnd, 0 );
+
+    flush_sequence();
+    SetRectRgn( hrgn, 0, 0, 100, 100 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
+    SetRectRgn( hrgn, 0, 0, 100, 50 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW );
+    ok_sequence( WmErase, "Erase", FALSE );
+    SetRectRgn( hrgn, 0, 50, 100, 100 );
+    check_update_rgn( hwnd, hrgn );
+
+    flush_sequence();
+    SetRectRgn( hrgn, 0, 0, 100, 100 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
+    SetRectRgn( hrgn, 0, 0, 50, 50 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOERASE | RDW_UPDATENOW );
+    ok_sequence( WmPaint, "Paint", FALSE );
+
+    flush_sequence();
+    SetRectRgn( hrgn, -4, -4, -2, -2 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+    SetRectRgn( hrgn, -200, -200, -198, -198 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME | RDW_ERASENOW );
+    ok_sequence( WmEmptySeq, "EmptySeq", FALSE );
+
+    flush_sequence();
+    SetRectRgn( hrgn, -4, -4, -2, -2 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+    SetRectRgn( hrgn, -4, -4, -3, -3 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME );
+    SetRectRgn( hrgn, 0, 0, 1, 1 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_UPDATENOW );
+    ok_sequence( WmPaint, "Paint", FALSE );
+
+    flush_sequence();
+    SetRectRgn( hrgn, -4, -4, -1, -1 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+    RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW );
+    /* make sure no WM_PAINT was generated */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE );
+
+    flush_sequence();
+    SetRectRgn( hrgn, -4, -4, -1, -1 );
+    RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+    {
+        if (msg.hwnd == hwnd && msg.message == WM_PAINT)
+        {
+            /* GetUpdateRgn must return empty region since only nonclient area is invalidated */
+            INT ret = GetUpdateRgn( hwnd, hrgn, FALSE );
+            ok( ret == NULLREGION, "Invalid GetUpdateRgn result %d\n", ret );
+            ret = GetUpdateRect( hwnd, &rect, FALSE );
+            ok( ret, "Invalid GetUpdateRect result %d\n", ret );
+            /* this will send WM_NCPAINT and validate the non client area */
+            ret = GetUpdateRect( hwnd, &rect, TRUE );
+            ok( !ret, "Invalid GetUpdateRect result %d\n", ret );
+        }
+        DispatchMessage( &msg );
+    }
+    ok_sequence( WmGetUpdateRect, "GetUpdateRect", FALSE );
+
+    DestroyWindow( hwnd );
+
+    /* now test with a child window */
+
+    hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW,
+                              100, 100, 200, 200, 0, 0, 0, NULL);
+    ok (hparent != 0, "Failed to create parent window\n");
+
+    hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE | WS_BORDER,
+                           10, 10, 100, 100, hparent, 0, 0, NULL);
+    ok (hchild != 0, "Failed to create child window\n");
+
+    ShowWindow( hparent, SW_SHOW );
+    UpdateWindow( hparent );
+    UpdateWindow( hchild );
+    /* try to flush pending X expose events */
+    MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+
+    flush_sequence();
+    log_all_parent_messages++;
+
+    SetRect( &rect, 0, 0, 50, 50 );
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN );
+    ok_sequence( WmInvalidateParentChild, "InvalidateParentChild", FALSE );
+
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    pt.x = pt.y = 0;
+    MapWindowPoints( hchild, hparent, &pt, 1 );
+    SetRectRgn( hrgn, 0, 0, 50 - pt.x, 50 - pt.y );
+    check_update_rgn( hchild, hrgn );
+    SetRectRgn( hrgn, 0, 0, 50, 50 );
+    check_update_rgn( hparent, hrgn );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmInvalidateParent, "InvalidateParent", FALSE );
+    RedrawWindow( hchild, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmEmptySeq, "EraseNow child", FALSE );
+
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentPaintNc, "WmParentPaintNc", FALSE );
+
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmInvalidateParent, "InvalidateParent2", FALSE );
+    RedrawWindow( hchild, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmEmptySeq, "EraseNow child", FALSE );
+
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN );
+    ok_sequence( WmInvalidateParentChild2, "InvalidateParentChild2", FALSE );
+
+    SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN );
+    flush_sequence();
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmInvalidateParentChild, "InvalidateParentChild3", FALSE );
+
+    /* flush all paint messages */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    flush_sequence();
+
+    /* RDW_UPDATENOW on child with WS_CLIPCHILDREN doesn't change corresponding parent area */
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+    SetRectRgn( hrgn, 0, 0, 50, 50 );
+    check_update_rgn( hparent, hrgn );
+    RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+    ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+    SetRectRgn( hrgn, 0, 0, 50, 50 );
+    check_update_rgn( hparent, hrgn );
+
+    /* flush all paint messages */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN );
+    flush_sequence();
+
+    /* RDW_UPDATENOW on child without WS_CLIPCHILDREN will validate corresponding parent area */
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    SetRectRgn( hrgn, 0, 0, 50, 50 );
+    check_update_rgn( hparent, hrgn );
+    RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+    ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+    SetRectRgn( hrgn2, 10, 10, 50, 50 );
+    CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF );
+    check_update_rgn( hparent, hrgn );
+    /* flush all paint messages */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    flush_sequence();
+
+    /* same as above but parent gets completely validated */
+    SetRect( &rect, 20, 20, 30, 30 );
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    SetRectRgn( hrgn, 20, 20, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+    RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+    ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+    check_update_rgn( hparent, 0 );  /* no update region */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmEmptySeq, "WmEmpty", FALSE );  /* and no paint messages */
+
+    /* make sure RDW_VALIDATE on child doesn't have the same effect */
+    flush_sequence();
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    SetRectRgn( hrgn, 20, 20, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+    RedrawWindow( hchild, NULL, 0, RDW_VALIDATE | RDW_NOERASE );
+    SetRectRgn( hrgn, 20, 20, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+
+    /* same as above but normal WM_PAINT doesn't validate parent */
+    flush_sequence();
+    SetRect( &rect, 20, 20, 30, 30 );
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    SetRectRgn( hrgn, 20, 20, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+    /* no WM_PAINT in child while parent still pending */
+    while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+    while (PeekMessage( &msg, hparent, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentErasePaint, "WmParentErasePaint", FALSE );
+
+    flush_sequence();
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+    /* no WM_PAINT in child while parent still pending */
+    while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+    RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_NOERASE | RDW_NOCHILDREN );
+    /* now that parent is valid child should get WM_PAINT */
+    while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmEmptySeq, "No other message", FALSE );
+
+    /* same thing with WS_CLIPCHILDREN in parent */
+    flush_sequence();
+    SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN );
+    ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE );
+    /* changing style invalidates non client area, but we need to invalidate something else to see it */
+    RedrawWindow( hparent, &rect, 0, RDW_UPDATENOW );
+    ok_sequence( WmEmptySeq, "No message", FALSE );
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_UPDATENOW );
+    ok_sequence( WmParentOnlyNcPaint, "WmParentOnlyNcPaint", FALSE );
+
+    flush_sequence();
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
+    SetRectRgn( hrgn, 20, 20, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+    /* no WM_PAINT in child while parent still pending */
+    while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+    /* WM_PAINT in parent first */
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentPaintNc, "WmParentPaintNc2", FALSE );
+
+    /* no RDW_ERASE in parent still causes RDW_ERASE and RDW_FRAME in child */
+    flush_sequence();
+    SetRect( &rect, 0, 0, 30, 30 );
+    RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ALLCHILDREN );
+    SetRectRgn( hrgn, 0, 0, 30, 30 );
+    check_update_rgn( hparent, hrgn );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentPaintNc, "WmParentPaintNc3", FALSE );
+
+    /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */
+    flush_sequence();
+    SetRect( &rect, -10, 0, 30, 30 );
+    RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE );
+    SetRect( &rect, 0, 0, 20, 20 );
+    RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN );
+    RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW );
+    ok_sequence( WmChildPaintNc, "WmChildPaintNc", FALSE );
+
+    /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */
+    flush_sequence();
+    SetRect( &rect, -10, 0, 30, 30 );
+    RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE );
+    SetRect( &rect, 0, 0, 100, 100 );
+    RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN );
+    RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW );
+    ok_sequence( WmEmptySeq, "WmChildPaintNc2", FALSE );
+    RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+    ok_sequence( WmEmptySeq, "WmChildPaintNc3", FALSE );
+
+    /* test RDW_INTERNALPAINT behavior */
+
+    flush_sequence();
+    RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_NOCHILDREN );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE );
+
+    RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_ALLCHILDREN );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentPaint, "WmParentPaint", FALSE );
+
+    RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE );
+
+    SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN );
+    ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE );
+    RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT );
+    while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+    ok_sequence( WmParentPaint, "WmParentPaint", FALSE );
+
+    log_all_parent_messages--;
+    DestroyWindow( hparent );
+    ok(!IsWindow(hchild), "child must be destroyed with its parent\n");
+
+    DeleteObject( hrgn );
+    DeleteObject( hrgn2 );
+}
+
+struct wnd_event
+{
+    HWND hwnd;
+    HANDLE event;
+};
+
+static DWORD WINAPI thread_proc(void *param)
+{
+    MSG msg;
+    struct wnd_event *wnd_event = (struct wnd_event *)param;
+
+    wnd_event->hwnd = CreateWindowExA(0, "TestWindowClass", "window caption text", WS_OVERLAPPEDWINDOW,
+                                      100, 100, 200, 200, 0, 0, 0, NULL);
+    ok(wnd_event->hwnd != 0, "Failed to create overlapped window\n");
+
+    SetEvent(wnd_event->event);
+
+    while (GetMessage(&msg, 0, 0, 0))
+    {
+       TranslateMessage(&msg);
+       DispatchMessage(&msg);
+    }
+
+    ok(IsWindow(wnd_event->hwnd), "window should still exist\n");
+
+    return 0;
+}
+
+static void test_interthread_messages(void)
+{
+    HANDLE hThread;
+    DWORD tid;
+    WNDPROC proc;
+    MSG msg;
+    char buf[256];
+    int len, expected_len;
+    struct wnd_event wnd_event;
+    BOOL ret;
+
+    wnd_event.event = CreateEventW(NULL, 0, 0, NULL);
+    if (!wnd_event.event)
+    {
+        trace("skipping interthread message test under win9x\n");
+        return;
+    }
+
+    hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
+    ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+    ok(WaitForSingleObject(wnd_event.event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+
+    CloseHandle(wnd_event.event);
+
+    SetLastError(0xdeadbeef);
+    ok(!DestroyWindow(wnd_event.hwnd), "DestroyWindow succeded\n");
+    ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error code %ld\n", GetLastError());
+
+    proc = (WNDPROC)GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC);
+    ok(proc != NULL, "GetWindowLongPtrA(GWLP_WNDPROC) error %ld\n", GetLastError());
+
+    expected_len = lstrlenA("window caption text");
+    memset(buf, 0, sizeof(buf));
+    SetLastError(0xdeadbeef);
+    len = CallWindowProcA(proc, wnd_event.hwnd, WM_GETTEXT, sizeof(buf), (LPARAM)buf);
+    ok(len == expected_len, "CallWindowProcA(WM_GETTEXT) error %ld, len %d, expected len %d\n", GetLastError(), len, expected_len);
+    ok(!lstrcmpA(buf, "window caption text"), "window text mismatch\n");
+
+    msg.hwnd = wnd_event.hwnd;
+    msg.message = WM_GETTEXT;
+    msg.wParam = sizeof(buf);
+    msg.lParam = (LPARAM)buf;
+    memset(buf, 0, sizeof(buf));
+    SetLastError(0xdeadbeef);
+    len = DispatchMessageA(&msg);
+    ok(!len && GetLastError() == ERROR_MESSAGE_SYNC_ONLY,
+       "DispatchMessageA(WM_GETTEXT) succeded on another thread window: ret %d, error %ld\n", len, GetLastError());
+
+    /* the following test causes an exception in user.exe under win9x */
+    msg.hwnd = wnd_event.hwnd;
+    msg.message = WM_TIMER;
+    msg.wParam = 0;
+    msg.lParam = GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC);
+    SetLastError(0xdeadbeef);
+    len = DispatchMessageA(&msg);
+    ok(!len && GetLastError() == 0xdeadbeef,
+       "DispatchMessageA(WM_TIMER) failed on another thread window: ret %d, error %ld\n", len, GetLastError());
+
+    ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
+    ok( ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError());
+
+    ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+    CloseHandle(hThread);
+
+    ok(!IsWindow(wnd_event.hwnd), "window should be destroyed on thread exit\n");
+}
+
+
+static const struct message WmVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+    { WM_CHAR, wparam|lparam, 'n', 1 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1002,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+    { 0 }
+};
+static const struct message WmShiftVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+    { WM_CHAR, wparam|lparam, 'N', 1 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1001,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmCtrlVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+    { WM_CHAR, wparam|lparam, 0x000e, 1 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmCtrlVkN_2[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmAltVkN[] = {
+    { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, 'N', 0x20000001 },
+    { WM_SYSCHAR, wparam|lparam, 'n', 0x20000001 },
+    { WM_SYSCHAR, sent|wparam|lparam, 'n', 0x20000001 },
+    { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 'n' },
+    { HCBT_SYSCOMMAND, hook },
+    { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+    { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_INITMENU, sent|defwinproc },
+    { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+    { WM_MENUCHAR, sent|defwinproc|wparam, MAKEWPARAM('n',MF_SYSMENU) },
+    { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_CAPTURECHANGED, sent|defwinproc },
+    { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,0xffff) },
+    { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+    { WM_EXITMENULOOP, sent|defwinproc },
+    { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) }, /* Win95 bug */
+    { WM_EXITMENULOOP, sent|defwinproc|optional }, /* Win95 bug */
+    { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmAltVkN_2[] = {
+    { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1003,1), 0 },
+    { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmCtrlAltVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 },
+    { WM_KEYDOWN, sent|wparam|lparam, 'N', 0x20000001 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmCtrlShiftVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1004,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmCtrlAltShiftVkN[] = {
+    { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+    { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0x20000001 },
+    { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 0x20000001 },
+    { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 },
+    { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1005,1), 0 },
+    { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+    { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xe0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xe0000001 },
+    { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmAltPressRelease[] = {
+    { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 0 },
+    { HCBT_SYSCOMMAND, hook },
+    { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+    { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_INITMENU, sent|defwinproc },
+    { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+    { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,MF_SYSMENU|MF_POPUP|MF_HILITE) },
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 },
+
+    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+    { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0, },
+    { WM_CAPTURECHANGED, sent|defwinproc },
+    { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) },
+    { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+    { WM_EXITMENULOOP, sent|defwinproc },
+    { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { 0 }
+};
+static const struct message WmAltMouseButton[] = {
+    { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+    { WM_MOUSEMOVE, wparam|optional, 0, 0 },
+    { WM_MOUSEMOVE, sent|wparam|optional, 0, 0 },
+    { WM_LBUTTONDOWN, wparam, MK_LBUTTON, 0 },
+    { WM_LBUTTONDOWN, sent|wparam, MK_LBUTTON, 0 },
+    { WM_LBUTTONUP, wparam, 0, 0 },
+    { WM_LBUTTONUP, sent|wparam, 0, 0 },
+    { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+    { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+    { 0 }
+};
+
+static void pump_msg_loop(HWND hwnd, HACCEL hAccel)
+{
+    MSG msg;
+
+    while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
+    {
+        struct message log_msg;
+
+        trace("accel: %p, %04x, %08x, %08lx\n", msg.hwnd, msg.message, msg.wParam, msg.lParam);
+
+        /* ignore some unwanted messages */
+        if (msg.message == WM_MOUSEMOVE ||
+            msg.message == WM_DEVICECHANGE)
+            continue;
+
+        log_msg.message = msg.message;
+        log_msg.flags = wparam|lparam;
+        log_msg.wParam = msg.wParam;
+        log_msg.lParam = msg.lParam;
+        add_message(&log_msg);
+
+        if (!hAccel || !TranslateAccelerator(hwnd, hAccel, &msg))
+        {
+            TranslateMessage(&msg);
+            DispatchMessage(&msg);
+        }
+    }
+}
+
+static void test_accelerators(void)
+{
+    RECT rc;
+    SHORT state;
+    HACCEL hAccel;
+    HWND hwnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+                                100, 100, 200, 200, 0, 0, 0, NULL);
+    BOOL ret;
+
+    assert(hwnd != 0);
+    UpdateWindow(hwnd);
+    SetFocus(hwnd);
+    ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
+
+    state = GetKeyState(VK_SHIFT);
+    ok(!(state & 0x8000), "wrong Shift state %04x\n", state);
+    state = GetKeyState(VK_CAPITAL);
+    ok(state == 0, "wrong CapsLock state %04x\n", state);
+
+    hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(1));
+    assert(hAccel != 0);
+
+    pump_msg_loop(hwnd, 0);
+    flush_sequence();
+
+    trace("testing VK_N press/release\n");
+    flush_sequence();
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmVkN, "VK_N press/release", FALSE);
+
+    trace("testing Shift+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_SHIFT, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE);
+
+    trace("testing Ctrl+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlVkN, "Ctrl+VK_N press/release", FALSE);
+
+    trace("testing Alt+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmAltVkN, "Alt+VK_N press/release", FALSE);
+
+    trace("testing Ctrl+Alt+VK_N press/release 1\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 1", FALSE);
+
+    ret = DestroyAcceleratorTable(hAccel);
+    ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError());
+
+    hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(2));
+    assert(hAccel != 0);
+
+    trace("testing VK_N press/release\n");
+    flush_sequence();
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmVkN, "VK_N press/release", FALSE);
+
+    trace("testing Shift+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_SHIFT, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE);
+
+    trace("testing Ctrl+VK_N press/release 2\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlVkN_2, "Ctrl+VK_N press/release 2", FALSE);
+
+    trace("testing Alt+VK_N press/release 2\n");
+    flush_sequence();
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmAltVkN_2, "Alt+VK_N press/release 2", FALSE);
+
+    trace("testing Ctrl+Alt+VK_N press/release 2\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 2", FALSE);
+
+    trace("testing Ctrl+Shift+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event(VK_SHIFT, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlShiftVkN, "Ctrl+Shift+VK_N press/release", FALSE);
+
+    trace("testing Ctrl+Alt+Shift+VK_N press/release\n");
+    flush_sequence();
+    keybd_event(VK_CONTROL, 0, 0, 0);
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event(VK_SHIFT, 0, 0, 0);
+    keybd_event('N', 0, 0, 0);
+    keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, hAccel);
+    ok_sequence(WmCtrlAltShiftVkN, "Ctrl+Alt+Shift+VK_N press/release", FALSE);
+
+    ret = DestroyAcceleratorTable(hAccel);
+    ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError());
+
+    trace("testing Alt press/release\n");
+    flush_sequence();
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    keybd_event(VK_MENU, 0, 0, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, 0);
+    /* this test doesn't pass in Wine for managed windows */
+    ok_sequence(WmAltPressRelease, "Alt press/release", TRUE);
+
+    trace("testing Alt+MouseButton press/release\n");
+    /* first, move mouse pointer inside of the window client area */
+    GetClientRect(hwnd, &rc);
+    MapWindowPoints(hwnd, 0, (LPPOINT)&rc, 2);
+    rc.left += (rc.right - rc.left)/2;
+    rc.top += (rc.bottom - rc.top)/2;
+    SetCursorPos(rc.left, rc.top);
+
+    pump_msg_loop(hwnd, 0);
+    flush_sequence();
+    keybd_event(VK_MENU, 0, 0, 0);
+    mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+    keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+    pump_msg_loop(hwnd, 0);
+    ok_sequence(WmAltMouseButton, "Alt+MouseButton press/release", FALSE);
+
+    DestroyWindow(hwnd);
+}
+
+/************* window procedures ********************/
+
+static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    static long beginpaint_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    trace("%p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+    switch (message)
+    {
+       case WM_ENABLE:
+       {
+           LONG style = GetWindowLongA(hwnd, GWL_STYLE);
+           ok((BOOL)wParam == !(style & WS_DISABLED),
+               "wrong WS_DISABLED state: %d != %d\n", wParam, !(style & WS_DISABLED));
+           break;
+       }
+
+       case WM_CAPTURECHANGED:
+           if (test_DestroyWindow_flag)
+           {
+               DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+               if (style & WS_CHILD)
+                   lParam = GetWindowLongA(hwnd, GWL_ID);
+               else if (style & WS_POPUP)
+                   lParam = WND_POPUP_ID;
+               else
+                   lParam = WND_PARENT_ID;
+           }
+           break;
+
+       case WM_NCDESTROY:
+       {
+           HWND capture;
+
+           ok(!GetWindow(hwnd, GW_CHILD), "children should be unlinked at this point\n");
+           capture = GetCapture();
+           if (capture)
+           {
+               ok(capture == hwnd, "capture should NOT be released at this point (capture %p)\n", capture);
+               trace("current capture %p, releasing...\n", capture);
+               ReleaseCapture();
+           }
+       }
+       /* fall through */
+       case WM_DESTROY:
+            if (pGetAncestor)
+               ok(pGetAncestor(hwnd, GA_PARENT) != 0, "parent should NOT be unlinked at this point\n");
+           if (test_DestroyWindow_flag)
+           {
+               DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+               if (style & WS_CHILD)
+                   lParam = GetWindowLongA(hwnd, GWL_ID);
+               else if (style & WS_POPUP)
+                   lParam = WND_POPUP_ID;
+               else
+                   lParam = WND_PARENT_ID;
+           }
+           break;
+
+       /* test_accelerators() depends on this */
+       case WM_NCHITTEST:
+           return HTCLIENT;
+    
+       /* ignore */
+       case WM_MOUSEMOVE:
+       case WM_SETCURSOR:
+       case WM_DEVICECHANGE:
+           return 0;
+
+        case WM_WINDOWPOSCHANGING:
+        case WM_WINDOWPOSCHANGED:
+        {
+            WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+            trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+            trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+                  winpos->hwnd, winpos->hwndInsertAfter,
+                  winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+            /* Log only documented flags, win2k uses 0x1000 and 0x2000
+             * in the high word for internal purposes
+             */
+            wParam = winpos->flags & 0xffff;
+            break;
+        }
+    }
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    if (beginpaint_counter) msg.flags |= beginpaint;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    add_message(&msg);
+
+    if (message == WM_GETMINMAXINFO && (GetWindowLongA(hwnd, GWL_STYLE) & WS_CHILD))
+    {
+       HWND parent = GetParent(hwnd);
+       RECT rc;
+       MINMAXINFO *minmax = (MINMAXINFO *)lParam;
+
+       GetClientRect(parent, &rc);
+       trace("parent %p client size = (%ld x %ld)\n", parent, rc.right, rc.bottom);
+
+       trace("ptReserved = (%ld,%ld)\n"
+             "ptMaxSize = (%ld,%ld)\n"
+             "ptMaxPosition = (%ld,%ld)\n"
+             "ptMinTrackSize = (%ld,%ld)\n"
+             "ptMaxTrackSize = (%ld,%ld)\n",
+             minmax->ptReserved.x, minmax->ptReserved.y,
+             minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+             minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+             minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+             minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+
+       ok(minmax->ptMaxSize.x == rc.right, "default width of maximized child %ld != %ld\n",
+          minmax->ptMaxSize.x, rc.right);
+       ok(minmax->ptMaxSize.y == rc.bottom, "default height of maximized child %ld != %ld\n",
+          minmax->ptMaxSize.y, rc.bottom);
+    }
+
+    if (message == WM_PAINT)
+    {
+        PAINTSTRUCT ps;
+        beginpaint_counter++;
+        BeginPaint( hwnd, &ps );
+        beginpaint_counter--;
+        EndPaint( hwnd, &ps );
+        return 0;
+    }
+
+    defwndproc_counter++;
+    ret = DefWindowProcA(hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
+static LRESULT WINAPI PopupMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    trace("popup: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    add_message(&msg);
+
+    if (message == WM_CREATE)
+    {
+       DWORD style = GetWindowLongA(hwnd, GWL_STYLE) | WS_VISIBLE;
+       SetWindowLongA(hwnd, GWL_STYLE, style);
+    }
+
+    defwndproc_counter++;
+    ret = DefWindowProcA(hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
+static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static long defwndproc_counter = 0;
+    static long beginpaint_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    trace("parent: %p, %04x, %08x, %08lx\n", hwnd, message, wParam