- [User32_winetest]
[reactos.git] / rostests / winetests / user32 / win.c
index 1f0420e..cc41bae 100644 (file)
@@ -52,6 +52,7 @@ static BOOL (WINAPI *pSetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD);
 static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
 static HMONITOR (WINAPI *pMonitorFromPoint)(POINT,DWORD);
 static int  (WINAPI *pGetWindowRgnBox)(HWND,LPRECT);
+static BOOL (WINAPI *pGetGUIThreadInfo)(DWORD, GUITHREADINFO*);
 
 static BOOL test_lbuttondown_flag;
 static HWND hwndMessage;
@@ -62,6 +63,8 @@ static const char* szAWRClass = "Winsize";
 static HMENU hmenu;
 static DWORD our_pid;
 
+static BOOL is_win9x = FALSE;
+
 #define COUNTOF(arr) (sizeof(arr)/sizeof(arr[0]))
 
 static void dump_minmax_info( const MINMAXINFO *minmax )
@@ -118,6 +121,16 @@ static void check_parents( HWND hwnd, HWND ga_parent, HWND gwl_parent, HWND get_
     }
 }
 
+static BOOL ignore_message( UINT message )
+{
+    /* these are always ignored */
+    return (message >= 0xc000 ||
+            message == WM_GETICON ||
+            message == WM_GETOBJECT ||
+            message == WM_TIMECHANGE ||
+            message == WM_DEVICECHANGE);
+}
+
 static BOOL CALLBACK EnumChildProc( HWND hwndChild, LPARAM lParam)
 {
     (*(LPINT)lParam)++;
@@ -434,6 +447,17 @@ static void test_parent_owner(void)
     ret = SetParent( test, child );
     ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
     check_parents( test, child, child, 0, 0, hwndMain, test );
+
+    if (!is_win9x)
+    {
+        ShowWindow( test, SW_SHOW );
+        ret = SetParent( test, test );
+        ok( ret == NULL, "SetParent return value %p expected %p\n", ret, NULL );
+        ok( GetWindowLongA( test, GWL_STYLE ) & WS_VISIBLE, "window is not visible after SetParent\n" );
+        check_parents( test, child, child, 0, 0, hwndMain, test );
+    }
+    else
+        win_skip( "Test crashes on Win9x/WinMe\n" );
     DestroyWindow( test );
 
     /* owned popup */
@@ -559,6 +583,60 @@ static void test_parent_owner(void)
     DestroyWindow( owner );
 }
 
+static BOOL CALLBACK enum_proc( HWND hwnd, LPARAM lParam)
+{
+    (*(LPINT)lParam)++;
+    if (*(LPINT)lParam > 2) return FALSE;
+    return TRUE;
+}
+static DWORD CALLBACK enum_thread( void *arg )
+{
+    INT count;
+    HWND hwnd[3];
+    BOOL ret;
+    MSG msg;
+
+    PeekMessage( &msg, 0, 0, 0, PM_NOREMOVE );  /* make sure we have a message queue */
+
+    count = 0;
+    ret = EnumThreadWindows( GetCurrentThreadId(), enum_proc, (LPARAM)&count );
+    ok( ret, "EnumThreadWindows should have returned TRUE\n" );
+    ok( count == 0, "count should be 0 got %d\n", count );
+
+    hwnd[0] = CreateWindowExA(0, "ToolWindowClass", "Tool window 1", WS_POPUP,
+                              0, 0, 100, 100, 0, 0, 0, NULL );
+    count = 0;
+    ret = EnumThreadWindows( GetCurrentThreadId(), enum_proc, (LPARAM)&count );
+    ok( ret, "EnumThreadWindows should have returned TRUE\n" );
+    if (count != 2)  /* Vista gives us two windows for the price of one */
+    {
+        ok( count == 1, "count should be 1 got %d\n", count );
+        hwnd[2] = CreateWindowExA(0, "ToolWindowClass", "Tool window 2", WS_POPUP,
+                                  0, 0, 100, 100, 0, 0, 0, NULL );
+    }
+    else hwnd[2] = 0;
+
+    hwnd[1] = CreateWindowExA(0, "ToolWindowClass", "Tool window 3", WS_POPUP,
+                              0, 0, 100, 100, 0, 0, 0, NULL );
+    count = 0;
+    ret = EnumThreadWindows( GetCurrentThreadId(), enum_proc, (LPARAM)&count );
+    ok( !ret, "EnumThreadWindows should have returned FALSE\n" );
+    ok( count == 3, "count should be 3 got %d\n", count );
+
+    if (hwnd[2]) DestroyWindow(hwnd[2]);
+    DestroyWindow(hwnd[1]);
+    DestroyWindow(hwnd[0]);
+    return 0;
+}
+
+/* test EnumThreadWindows in a separate thread */
+static void test_enum_thread_windows(void)
+{
+    DWORD id;
+    HANDLE handle = CreateThread( NULL, 0, enum_thread, 0, 0, &id );
+    ok( !WaitForSingleObject( handle, 10000 ), "wait failed\n" );
+    CloseHandle( handle );
+}
 
 static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 {
@@ -575,7 +653,6 @@ static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPAR
        }
        case WM_WINDOWPOSCHANGING:
        {
-           BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
            WINDOWPOS *winpos = (WINDOWPOS *)lparam;
            trace("main: WM_WINDOWPOSCHANGING %p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
                   winpos->hwnd, winpos->hwndInsertAfter,
@@ -784,7 +861,6 @@ static void test_nonclient_area(HWND hwnd)
     DWORD style, exstyle;
     RECT rc_window, rc_client, rc;
     BOOL menu;
-    BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
     LRESULT ret;
 
     style = GetWindowLongA(hwnd, GWL_STYLE);
@@ -968,9 +1044,9 @@ static void test_shell_window(void)
     HWND hwnd1, hwnd2, hwnd3, hwnd4, hwnd5;
     HWND shellWindow, nextWnd;
 
-    if (!GetWindowLongW(GetDesktopWindow(), GWL_STYLE))
+    if (is_win9x)
     {
-        trace("Skipping shell window test on Win9x\n");
+        win_skip("Skipping shell window test on Win9x\n");
         return;
     }
 
@@ -1835,7 +1911,6 @@ static void test_SetWindowPos(HWND hwnd)
 {
     RECT orig_win_rc, rect;
     LONG_PTR old_proc;
-    BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
 
     SetRect(&rect, 111, 222, 333, 444);
     ok(!GetWindowRect(0, &rect), "GetWindowRect succeeded\n");
@@ -1898,7 +1973,6 @@ static void test_SetMenu(HWND parent)
 {
     HWND child;
     HMENU hMenu, ret;
-    BOOL is_win9x = GetWindowLongPtrW(parent, GWLP_WNDPROC) == 0;
     BOOL retok;
     DWORD style;
 
@@ -2085,7 +2159,7 @@ static void check_z_order_debug(HWND hwnd, HWND next, HWND prev, HWND owner,
         /*trace("skipping next %p (%p)\n", test, UlongToHandle(GetWindowLongPtr(test, GWLP_HINSTANCE)));*/
         test = GetWindow(test, GW_HWNDNEXT);
     }
-    ok_(file, line)(next == test, "expected next %p, got %p\n", next, test);
+    ok_(file, line)(next == test, "%p: expected next %p, got %p\n", hwnd, next, test);
 
     test = GetWindow(hwnd, GW_HWNDPREV);
     /* skip foreign windows */
@@ -2097,13 +2171,14 @@ static void check_z_order_debug(HWND hwnd, HWND next, HWND prev, HWND owner,
         /*trace("skipping prev %p (%p)\n", test, UlongToHandle(GetWindowLongPtr(test, GWLP_HINSTANCE)));*/
         test = GetWindow(test, GW_HWNDPREV);
     }
-    ok_(file, line)(prev == test, "expected prev %p, got %p\n", prev, test);
+    ok_(file, line)(prev == test, "%p: expected prev %p, got %p\n", hwnd, prev, test);
 
     test = GetWindow(hwnd, GW_OWNER);
-    ok_(file, line)(owner == test, "expected owner %p, got %p\n", owner, test);
+    ok_(file, line)(owner == test, "%p: expected owner %p, got %p\n", hwnd, owner, test);
 
     ex_style = GetWindowLong(hwnd, GWL_EXSTYLE);
-    ok_(file, line)(!(ex_style & WS_EX_TOPMOST) == !topmost, "expected %stopmost\n", topmost ? "" : "NOT ");
+    ok_(file, line)(!(ex_style & WS_EX_TOPMOST) == !topmost, "%p: expected %stopmost\n",
+                    hwnd, topmost ? "" : "NOT ");
 }
 
 static void test_popup_zorder(HWND hwnd_D, HWND hwnd_E)
@@ -2184,6 +2259,20 @@ static void test_popup_zorder(HWND hwnd_D, HWND hwnd_E)
     check_z_order(hwnd_A, hwnd_D, 0, 0, TRUE);
 #endif
 
+    /* make hwnd_C owned by a topmost window */
+    DestroyWindow( hwnd_C );
+    hwnd_C = CreateWindowEx(0, "MainWindowClass", NULL,
+                            WS_POPUP,
+                            100, 100, 100, 100,
+                            hwnd_A, 0, GetModuleHandle(0), NULL);
+    trace("hwnd_C %p\n", hwnd_C);
+    check_z_order(hwnd_E, 0, hwnd_D, 0, FALSE);
+    check_z_order(hwnd_D, hwnd_E, hwnd_F, 0, FALSE);
+    check_z_order(hwnd_F, hwnd_D, hwnd_B, 0, FALSE);
+    check_z_order(hwnd_B, hwnd_F, hwnd_A, hwnd_F, TRUE);
+    check_z_order(hwnd_A, hwnd_B, hwnd_C, 0, TRUE);
+    check_z_order(hwnd_C, hwnd_A, 0, hwnd_A, TRUE);
+
     DestroyWindow(hwnd_A);
     DestroyWindow(hwnd_B);
     DestroyWindow(hwnd_C);
@@ -2201,7 +2290,7 @@ static void test_vis_rgn( HWND hwnd )
     ok( GetRandomRgn( hdc, hrgn, SYSRGN ) != 0, "GetRandomRgn failed\n" );
     GetWindowRect( hwnd, &win_rect );
     GetRgnBox( hrgn, &rgn_rect );
-    if (GetVersion() & 0x80000000)
+    if (is_win9x)
     {
         trace("win9x, mapping to screen coords\n");
         MapWindowPoints( hwnd, 0, (POINT *)&rgn_rect, 2 );
@@ -2234,7 +2323,7 @@ static LRESULT WINAPI set_focus_on_activate_proc(HWND hwnd, UINT msg, WPARAM wp,
 
 static void test_SetFocus(HWND hwnd)
 {
-    HWND child;
+    HWND child, child2;
     WNDPROC old_wnd_proc;
 
     /* check if we can set focus to non-visible windows */
@@ -2261,6 +2350,14 @@ static void test_SetFocus(HWND hwnd)
     ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child );
     ok( GetFocus() == hwnd, "Focus should be on parent %p, not %p\n", hwnd, GetFocus() );
     ShowWindow(child, SW_SHOW);
+    child2 = CreateWindowExA(0, "static", NULL, WS_CHILD, 0, 0, 0, 0, child, 0, 0, NULL);
+    assert(child2);
+    ShowWindow(child2, SW_SHOW);
+    SetFocus(child2);
+    ShowWindow(child, SW_HIDE);
+    ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child );
+    ok( GetFocus() == child2, "Focus should be on %p, not %p\n", child2, GetFocus() );
+    ShowWindow(child, SW_SHOW);
     SetFocus(child);
     ok( GetFocus() == child, "Focus should be on child %p\n", child );
     SetWindowPos(child,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_HIDEWINDOW);
@@ -2304,6 +2401,7 @@ todo_wine
 
     SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)old_wnd_proc);
 
+    DestroyWindow( child2 );
     DestroyWindow( child );
 }
 
@@ -2312,7 +2410,10 @@ static void check_wnd_state_(const char *file, int line,
                              HWND active, HWND foreground, HWND focus, HWND capture)
 {
     ok_(file, line)(active == GetActiveWindow(), "GetActiveWindow() = %p\n", GetActiveWindow());
-    if (foreground && GetForegroundWindow())
+    /* only check foreground if it belongs to the current thread */
+    /* foreground can be moved to a different app pretty much at any time */
+    if (foreground && GetForegroundWindow() &&
+        GetWindowThreadProcessId(GetForegroundWindow(), NULL) == GetCurrentThreadId())
         ok_(file, line)(foreground == GetForegroundWindow(), "GetForegroundWindow() = %p\n", GetForegroundWindow());
     ok_(file, line)(focus == GetFocus(), "GetFocus() = %p\n", GetFocus());
     ok_(file, line)(capture == GetCapture(), "GetCapture() = %p\n", GetCapture());
@@ -2621,6 +2722,133 @@ static void test_capture_3(HWND hwnd1, HWND hwnd2)
     ok (ret, "releasecapture did not return TRUE after second try.\n");
 }
 
+static LRESULT CALLBACK test_capture_4_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    GUITHREADINFO gti;
+    HWND cap_wnd, cap_wnd2, set_cap_wnd;
+    BOOL status;
+    switch (msg)
+    {
+        case WM_CAPTURECHANGED:
+
+            /* now try to release capture from menu. this should fail */
+            if (pGetGUIThreadInfo)
+            {
+                memset(&gti, 0, sizeof(GUITHREADINFO));
+                gti.cbSize = sizeof(GUITHREADINFO);
+                status = pGetGUIThreadInfo(GetCurrentThreadId(), &gti);
+                ok(status, "GetGUIThreadInfo() failed!\n");
+                ok(gti.flags & GUI_INMENUMODE, "Thread info incorrect (flags=%08X)!\n", gti.flags);
+            }
+            cap_wnd = GetCapture();
+
+            /* check that re-setting the capture for the menu fails */
+            set_cap_wnd = SetCapture(cap_wnd);
+            ok(!set_cap_wnd || broken(set_cap_wnd == cap_wnd), /* nt4 */
+               "SetCapture should have failed!\n");
+            if (set_cap_wnd)
+            {
+                DestroyWindow(hWnd);
+                break;
+            }
+
+            /* check that SetCapture fails for another window and that it does not touch the error code */
+            set_cap_wnd = SetCapture(hWnd);
+            ok(!set_cap_wnd, "SetCapture should have failed!\n");
+
+            /* check that ReleaseCapture fails and does not touch the error code */
+            status = ReleaseCapture();
+            ok(!status, "ReleaseCapture should have failed!\n");
+
+            /* check that thread info did not change */
+            if (pGetGUIThreadInfo)
+            {
+                memset(&gti, 0, sizeof(GUITHREADINFO));
+                gti.cbSize = sizeof(GUITHREADINFO);
+                status = pGetGUIThreadInfo(GetCurrentThreadId(), &gti);
+                ok(status, "GetGUIThreadInfo() failed!\n");
+                ok(gti.flags & GUI_INMENUMODE, "Thread info incorrect (flags=%08X)!\n", gti.flags);
+            }
+
+            /* verify that no capture change took place */
+            cap_wnd2 = GetCapture();
+            ok(cap_wnd2 == cap_wnd, "Capture changed!\n");
+
+            /* we are done. kill the window */
+            DestroyWindow(hWnd);
+            break;
+
+        default:
+            return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
+    }
+    return 0;
+}
+
+/* Test that no-one can mess around with the current capture while a menu is open */
+static void test_capture_4(void)
+{
+    BOOL ret;
+    HMENU hmenu;
+    HWND hwnd;
+    WNDCLASSA wclass;
+    HINSTANCE hInstance = GetModuleHandleA( NULL );
+
+    if (!pGetGUIThreadInfo)
+    {
+        win_skip("GetGUIThreadInfo is not available\n");
+        return;
+    }
+    wclass.lpszClassName = "TestCapture4Class";
+    wclass.style         = CS_HREDRAW | CS_VREDRAW;
+    wclass.lpfnWndProc   = test_capture_4_proc;
+    wclass.hInstance     = hInstance;
+    wclass.hIcon         = LoadIconA( 0, 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 ));
+    assert (hwnd = CreateWindowA( wclass.lpszClassName, "MenuTest",
+                                  WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0,
+                                  400, 200, NULL, NULL, hInstance, NULL) );
+    ok(hwnd != NULL, "CreateWindowEx failed with error %d\n", GetLastError());
+    if (!hwnd) return;
+    hmenu = CreatePopupMenu();
+
+    ret = AppendMenuA( hmenu, MF_STRING, 1, "winetest2");
+    ok( ret, "AppendMenuA has failed!\n");
+
+    /* set main window to have initial capture */
+    SetCapture(hwnd);
+
+    if (is_win9x)
+    {
+        win_skip("TrackPopupMenu test crashes on Win9x/WinMe\n");
+    }
+    else
+    {
+        /* create popup (it will self-destruct) */
+        ret = TrackPopupMenu(hmenu, TPM_RETURNCMD, 100, 100, 0, hwnd, NULL);
+        ok( ret == 0, "TrackPopupMenu returned %d expected zero\n", ret);
+    }
+
+    /* clean up */
+    DestroyMenu(hmenu);
+    DestroyWindow(hwnd);
+}
+
+/* PeekMessage wrapper that ignores the messages we don't care about */
+static BOOL peek_message( MSG *msg )
+{
+    BOOL ret;
+    do
+    {
+        ret = PeekMessageA(msg, 0, 0, 0, PM_REMOVE);
+    } while (ret && (msg->message == WM_TIMER || ignore_message(msg->message)));
+    return ret;
+}
+
 static void test_keyboard_input(HWND hwnd)
 {
     MSG msg;
@@ -2639,40 +2867,31 @@ static void test_keyboard_input(HWND hwnd)
     flush_events( TRUE );
 
     PostMessageA(hwnd, WM_KEYDOWN, 0, 0);
-    do
-    {
-        ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
-        ok( ret, "no message available\n");
-    }
-    while (ret && msg.message >= 0xc000);
+    ret = peek_message(&msg);
+    ok( ret, "no message available\n");
     ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    do
-        ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
-    while (ret && (msg.message == WM_TIMER || msg.message >= 0xc000));
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
 
     PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ret = peek_message(&msg);
+    ok(ret, "no message available\n");
     ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
 
     keybd_event(VK_SPACE, 0, 0, 0);
-    do
-    {
-        ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
-    } while (ret && msg.message >= 0xc000);
-    if (!ret)
+    if (!peek_message(&msg))
     {
         skip( "keybd_event didn't work, skipping keyboard test\n" );
         return;
     }
     ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     SetFocus(0);
@@ -2681,25 +2900,28 @@ static void test_keyboard_input(HWND hwnd)
     flush_events( TRUE );
 
     PostMessageA(hwnd, WM_KEYDOWN, 0, 0);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ret = peek_message(&msg);
+    ok(ret, "no message available\n");
     ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
 
     PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ret = peek_message(&msg);
+    ok(ret, "no message available\n");
     ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
 
     keybd_event(VK_SPACE, 0, 0, 0);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ret = peek_message(&msg);
+    ok(ret, "no message available\n");
     ok(msg.hwnd == hwnd && msg.message == WM_SYSKEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 }
 
@@ -2709,11 +2931,11 @@ static BOOL wait_for_message( MSG *msg )
 
     for (;;)
     {
-        ret = PeekMessageA(msg, 0, 0, 0, PM_REMOVE);
+        ret = peek_message(msg);
         if (ret)
         {
             if (msg->message == WM_PAINT) DispatchMessage(msg);
-            else if (msg->message < 0xc000) break;  /* skip registered messages */
+            else break;
         }
         else if (MsgWaitForMultipleObjects( 0, NULL, FALSE, 100, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
     }
@@ -2766,9 +2988,7 @@ static void test_mouse_input(HWND hwnd)
     /* Check that setting the same position may generate WM_MOUSEMOVE */
     SetCursorPos(x, y);
     msg.message = 0;
-    do
-        ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
-    while (ret && msg.message >= 0xc000);  /* skip registered messages */
+    ret = peek_message(&msg);
     if (ret)
     {
         ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n",
@@ -2789,11 +3009,12 @@ static void test_mouse_input(HWND hwnd)
     /* FIXME: SetCursorPos in Wine generates additional WM_MOUSEMOVE message */
     while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
     {
-        if (msg.message == WM_TIMER || msg.message >= 0xc000) continue;  /* skip registered messages */
+        if (msg.message == WM_TIMER || ignore_message(msg.message)) continue;
         ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE,
            "hwnd %p message %04x\n", msg.hwnd, msg.message);
+        DispatchMessage(&msg);
     }
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok( !ret, "message %04x available\n", msg.message);
 
     mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0);
@@ -2853,7 +3074,7 @@ static void test_mouse_input(HWND hwnd)
     ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p/%p message %04x\n",
        msg.hwnd, popup, msg.message);
 
-    ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+    ret = peek_message(&msg);
     ok(!ret, "message %04x available\n", msg.message);
 
     ShowWindow(popup, SW_HIDE);
@@ -2881,10 +3102,10 @@ static void test_mouse_input(HWND hwnd)
     ok(ret, "no message available\n");
     ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p/%p message %04x\n",
        msg.hwnd, popup, msg.message);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ok(peek_message(&msg), "no message available\n");
     ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p/%p message %04x\n",
        msg.hwnd, popup, msg.message);
-    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+    ok(peek_message(&msg), "no message available\n");
 
     /* Test WM_MOUSEACTIVATE */
 #define TEST_MOUSEACTIVATE(A,B)                                                          \
@@ -3017,6 +3238,7 @@ static void test_SetParent(void)
     BOOL ret;
     HWND desktop = GetDesktopWindow();
     HMENU hMenu;
+    /* FIXME: This detection is not correct as it also covers (all?) XP+ */
     BOOL is_win9x = GetWindowLongPtrW(desktop, GWLP_WNDPROC) == 0;
     HWND parent, child1, child2, child3, child4, sibling;
 
@@ -3077,6 +3299,8 @@ static void test_SetParent(void)
         check_parents(child3, child2, child2, child2, 0, child2, parent);
         check_parents(child4, desktop, child2, child2, child2, child4, parent);
     }
+    else
+        skip("Win9x/WinMe crash\n");
 
     hMenu = CreateMenu();
     sibling = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW,
@@ -3200,6 +3424,67 @@ static void test_window_styles(void)
     check_window_style(WS_CHILD, WS_EX_DLGMODALFRAME|WS_EX_STATICEDGE, WS_CHILD, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME);
     check_window_style(WS_CAPTION, WS_EX_STATICEDGE, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE);
     check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE);
+
+    if (pGetLayeredWindowAttributes)
+    {
+        check_window_style(0, WS_EX_LAYERED, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_LAYERED|WS_EX_WINDOWEDGE);
+        check_window_style(0, WS_EX_LAYERED|WS_EX_TRANSPARENT, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_WINDOWEDGE);
+        check_window_style(0, WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOOLWINDOW, WS_CLIPSIBLINGS|WS_CAPTION,
+                                                      WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOOLWINDOW|WS_EX_WINDOWEDGE);
+    }
+}
+
+static void test_scrollwindow( HWND hwnd)
+{
+    HDC hdc;
+    RECT rc, rc2, rc3;
+    COLORREF colr;
+
+    ShowWindow( hwnd, SW_SHOW);
+    UpdateWindow( hwnd);
+    flush_events( TRUE );
+    GetClientRect( hwnd, &rc);
+    hdc = GetDC( hwnd);
+    /* test ScrollWindow(Ex) with no clip rectangle */
+    /* paint the lower half of the window black */
+    rc2 = rc;
+    rc2.top = ( rc2.top + rc2.bottom) / 2;
+    FillRect( hdc, &rc2, GetStockObject(BLACK_BRUSH));
+    /* paint the upper half of the window white */
+    rc2.bottom = rc2.top;
+    rc2.top =0;
+    FillRect( hdc, &rc2, GetStockObject(WHITE_BRUSH));
+    /* scroll lower half up */
+    rc2 = rc;
+    rc2.top = ( rc2.top + rc2.bottom) / 2;
+    ScrollWindowEx( hwnd, 0, - rc2.top, &rc2, NULL, NULL, NULL, SW_ERASE);
+    flush_events(FALSE);
+    /* expected: black should have scrolled to the upper half */
+    colr = GetPixel( hdc, (rc2.left+rc2.right)/ 2,  rc2.bottom / 4 );
+    ok ( colr == 0, "pixel should be black, color is %08x\n", colr);
+    /* Repeat that test of ScrollWindow(Ex) now with clip rectangle */
+    /* paint the lower half of the window black */
+    rc2 = rc;
+    rc2.top = ( rc2.top + rc2.bottom) / 2;
+    FillRect( hdc, &rc2, GetStockObject(BLACK_BRUSH));
+    /* paint the upper half of the window white */
+    rc2.bottom = rc2.top;
+    rc2.top =0;
+    FillRect( hdc, &rc2, GetStockObject(WHITE_BRUSH));
+    /* scroll lower half up */
+    rc2 = rc;
+    rc2.top = ( rc2.top + rc2.bottom) / 2;
+    rc3 = rc;
+    rc3.left = rc3.right / 4;
+    rc3.right -= rc3.right / 4;
+    ScrollWindowEx( hwnd, 0, - rc2.top, &rc2, &rc3, NULL, NULL, SW_ERASE);
+    flush_events(FALSE);
+    /* expected: black should have scrolled to the upper half */
+    colr = GetPixel( hdc, (rc2.left+rc2.right)/ 2,  rc2.bottom / 4 );
+    ok ( colr == 0, "pixel should be black, color is %08x\n", colr);
+
+    /* clean up */
+    ReleaseDC( hwnd, hdc);
 }
 
 static void test_scrollvalidate( HWND parent)
@@ -4843,6 +5128,75 @@ static void test_Expose(void)
     DestroyWindow(mw);
 }
 
+static LRESULT CALLBACK TestNCRedraw_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    static UINT ncredrawflags;
+    PAINTSTRUCT ps;
+
+    switch(msg)
+    {
+    case WM_CREATE:
+        ncredrawflags = *(UINT *) (((CREATESTRUCT *)lParam)->lpCreateParams);
+        return 0;
+    case WM_NCPAINT:
+        RedrawWindow(hwnd, NULL, NULL, ncredrawflags);
+        break;
+    case WM_PAINT:
+        BeginPaint(hwnd, &ps);
+        EndPaint(hwnd, &ps);
+        return 0;
+    }
+    return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+static void run_NCRedrawLoop(UINT flags)
+{
+    HWND hwnd;
+    MSG msg;
+
+    UINT loopcount = 0;
+
+    hwnd = CreateWindowA("TestNCRedrawClass", "MainWindow",
+                         WS_OVERLAPPEDWINDOW, 0, 0, 200, 100,
+                         NULL, NULL, 0, &flags);
+    ShowWindow(hwnd, SW_SHOW);
+    UpdateWindow(hwnd);
+    while(PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE) != 0)
+    {
+        if (msg.message == WM_PAINT) loopcount++;
+        if (loopcount >= 100) break;
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+        MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT);
+    }
+    if (flags == (RDW_INVALIDATE | RDW_FRAME))
+        todo_wine ok(loopcount < 100, "Detected infinite WM_PAINT loop (%x).\n", flags);
+    else
+        ok(loopcount < 100, "Detected infinite WM_PAINT loop (%x).\n", flags);
+    DestroyWindow(hwnd);
+}
+
+static void test_NCRedraw(void)
+{
+    WNDCLASSA wndclass;
+
+    wndclass.lpszClassName = "TestNCRedrawClass";
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = TestNCRedraw_WndProc;
+    wndclass.cbClsExtra = 0;
+    wndclass.cbWndExtra = 0;
+    wndclass.hInstance = 0;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
+    wndclass.lpszMenuName = NULL;
+
+    RegisterClassA(&wndclass);
+
+    run_NCRedrawLoop(RDW_INVALIDATE | RDW_FRAME);
+    run_NCRedrawLoop(RDW_INVALIDATE);
+}
+
 static void test_GetWindowModuleFileName(void)
 {
     HWND hwnd;
@@ -5589,9 +5943,14 @@ START_TEST(win)
     pGetMonitorInfoA = (void *)GetProcAddress( user32,  "GetMonitorInfoA" );
     pMonitorFromPoint = (void *)GetProcAddress( user32,  "MonitorFromPoint" );
     pGetWindowRgnBox = (void *)GetProcAddress( user32, "GetWindowRgnBox" );
+    pGetGUIThreadInfo = (void *)GetProcAddress( user32, "GetGUIThreadInfo" );
 
     if (!RegisterWindowClasses()) assert(0);
 
+    SetLastError(0xdeafbeef);
+    GetWindowLongPtrW(GetDesktopWindow(), GWLP_WNDPROC);
+    is_win9x = (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED);
+
     hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId());
     if (!hhook) win_skip( "Cannot set CBT hook, skipping some tests\n" );
 
@@ -5620,10 +5979,12 @@ START_TEST(win)
     test_capture_1();
     test_capture_2();
     test_capture_3(hwndMain, hwndMain2);
+//    test_capture_4();
 
     test_CreateWindow();
     test_parent_owner();
     test_SetParent();
+    test_enum_thread_windows();
 
     test_mdi();
     test_icons();
@@ -5631,6 +5992,7 @@ START_TEST(win)
     test_SetMenu(hwndMain);
     test_SetFocus(hwndMain);
     test_SetActiveWindow(hwndMain);
+    test_NCRedraw();
 
     test_children_zorder(hwndMain);
     test_popup_zorder(hwndMain2, hwndMain);
@@ -5638,6 +6000,7 @@ START_TEST(win)
     test_mouse_input(hwndMain);
     test_validatergn(hwndMain);
     test_nccalcscroll( hwndMain);
+    test_scrollwindow( hwndMain);
     test_scrollvalidate( hwndMain);
     test_scrolldc( hwndMain);
     test_scroll();
@@ -5656,7 +6019,7 @@ START_TEST(win)
     test_layered_window();
 
     test_SetForegroundWindow(hwndMain);
-    test_shell_window();
+//    test_shell_window();
     test_handles( hwndMain );
     test_winregion();