- Update user32_winetest to Wine-1.1.31.
authorAleksey Bragin <aleksey@reactos.org>
Thu, 15 Oct 2009 20:00:27 +0000 (20:00 +0000)
committerAleksey Bragin <aleksey@reactos.org>
Thu, 15 Oct 2009 20:00:27 +0000 (20:00 +0000)
svn path=/trunk/; revision=43499

rostests/winetests/user32/class.c
rostests/winetests/user32/cursoricon.c
rostests/winetests/user32/dde.c
rostests/winetests/user32/dialog.c
rostests/winetests/user32/input.c
rostests/winetests/user32/msg.c
rostests/winetests/user32/resource.rc
rostests/winetests/user32/sysparams.c
rostests/winetests/user32/text.c
rostests/winetests/user32/win.c

index f8a4cd8..0e7b0e4 100755 (executable)
@@ -598,8 +598,8 @@ static void test_builtinproc(void)
     HWND hwnd;
     int i;
 
     HWND hwnd;
     int i;
 
-    pDefWindowProcA = GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcA");
-    pDefWindowProcW = GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcW");
+    pDefWindowProcA = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcA");
+    pDefWindowProcW = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcW");
 
     for (i = 0; i < 4; i++)
     {
 
     for (i = 0; i < 4; i++)
     {
index 3f8bd82..ff86cd0 100644 (file)
@@ -1039,6 +1039,8 @@ static void check_DrawIcon(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, COLOR
     HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
     if (!hicon) return;
     SetPixelV(hdc, 0, 0, background);
     HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
     if (!hicon) return;
     SetPixelV(hdc, 0, 0, background);
+    SetPixelV(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1, background);
+    SetPixelV(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), background);
     DrawIcon(hdc, 0, 0, hicon);
     result = GetPixel(hdc, 0, 0);
 
     DrawIcon(hdc, 0, 0, hicon);
     result = GetPixel(hdc, 0, 0);
 
@@ -1047,6 +1049,21 @@ static void check_DrawIcon(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, COLOR
         "Overlaying Mask %d on Color %06X with DrawIcon. "
         "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n",
         maskvalue, color, modern_expected, legacy_expected, result, line);
         "Overlaying Mask %d on Color %06X with DrawIcon. "
         "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n",
         maskvalue, color, modern_expected, legacy_expected, result, line);
+
+    result = GetPixel(hdc, GetSystemMetrics(SM_CXICON)-1, GetSystemMetrics(SM_CYICON)-1);
+
+    ok (color_match(result, modern_expected) ||         /* Windows 2000 and up */
+        broken(color_match(result, legacy_expected)),   /* Windows NT 4.0, 9X and below */
+        "Overlaying Mask %d on Color %06X with DrawIcon. "
+        "Expected a close match to %06X (modern), or %06X (legacy). Got %06X from line %d\n",
+        maskvalue, color, modern_expected, legacy_expected, result, line);
+
+    result = GetPixel(hdc, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
+
+    ok (color_match(result, background),
+        "Overlaying Mask %d on Color %06X with DrawIcon. "
+        "Expected unchanged background color %06X. Got %06X from line %d\n",
+        maskvalue, color, background, result, line);
 }
 
 static void test_DrawIcon(void)
 }
 
 static void test_DrawIcon(void)
@@ -1070,8 +1087,8 @@ static void test_DrawIcon(void)
 
     memset(&bitmapInfo, 0, sizeof(bitmapInfo));
     bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 
     memset(&bitmapInfo, 0, sizeof(bitmapInfo));
     bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
-    bitmapInfo.bmiHeader.biWidth = 1;
-    bitmapInfo.bmiHeader.biHeight = 1;
+    bitmapInfo.bmiHeader.biWidth = GetSystemMetrics(SM_CXICON)+1;
+    bitmapInfo.bmiHeader.biHeight = GetSystemMetrics(SM_CYICON)+1;
     bitmapInfo.bmiHeader.biBitCount = 32;
     bitmapInfo.bmiHeader.biPlanes = 1;
     bitmapInfo.bmiHeader.biCompression = BI_RGB;
     bitmapInfo.bmiHeader.biBitCount = 32;
     bitmapInfo.bmiHeader.biPlanes = 1;
     bitmapInfo.bmiHeader.biCompression = BI_RGB;
@@ -1212,6 +1229,130 @@ cleanup:
         DeleteDC(hdcDst);
 }
 
         DeleteDC(hdcDst);
 }
 
+static void check_DrawState_Size(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, UINT flags, int line)
+{
+    COLORREF result, background;
+    BOOL passed[2];
+    HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
+    background = 0x00FFFFFF;
+    /* Set color of the 2 pixels that will be checked afterwards */
+    SetPixelV(hdc, 0, 0, background);
+    SetPixelV(hdc, 2, 2, background);
+
+    /* Let DrawState calculate the size of the icon (it's 1x1) */
+    DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, (DST_ICON | flags ));
+
+    result = GetPixel(hdc, 0, 0);
+    passed[0] = color_match(result, background);
+    result = GetPixel(hdc, 2, 2);
+    passed[0] = passed[0] & color_match(result, background);
+
+    /* Check if manually specifying the icon size DOESN'T work */
+
+    /* IMPORTANT: For Icons, DrawState wants the size of the source image, not the
+     *            size in which it should be ultimately drawn. Therefore giving
+     *            width/height 2x2 if the icon is only 1x1 pixels in size should
+     *            result in drawing it with size 1x1. The size parameters must be
+     *            ignored if a Icon has to be drawn! */
+    DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 2, 2, (DST_ICON | flags ));
+
+    result = GetPixel(hdc, 0, 0);
+    passed[1] = color_match(result, background);
+    result = GetPixel(hdc, 2, 2);
+    passed[1] = passed[0] & color_match(result, background);
+
+    if(!passed[0]&&!passed[1])
+        ok (passed[1],
+        "DrawState failed to draw a 1x1 Icon in the correct size, independent of the "
+        "width and height settings passed to it, for Icon with: Overlaying Mask %d on "
+        "Color %06X with flags %08X. Line %d\n",
+        maskvalue, color, (DST_ICON | flags), line);
+    else if(!passed[1])
+        ok (passed[1],
+        "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height "
+        "parameters passed to it are bigger than the real Icon size, for Icon with: Overlaying "
+        "Mask %d on Color %06X with flags %08X. Line %d\n",
+        maskvalue, color, (DST_ICON | flags), line);
+    else
+        ok (passed[0],
+        "DrawState failed to draw a 1x1 Icon in the correct size, if the width and height "
+        "parameters passed to it are 0, for Icon with: Overlaying Mask %d on "
+        "Color %06X with flags %08X. Line %d\n",
+        maskvalue, color, (DST_ICON | flags), line);
+}
+
+static void check_DrawState_Color(HDC hdc, BOOL maskvalue, UINT32 color, int bpp, HBRUSH hbr, UINT flags,
+                             COLORREF background, COLORREF modern_expected, COLORREF legacy_expected, int line)
+{
+    COLORREF result;
+    HICON hicon = create_test_icon(hdc, 1, 1, bpp, maskvalue, &color, sizeof(color));
+    if (!hicon) return;
+    /* Set color of the pixel that will be checked afterwards */
+    SetPixelV(hdc, 1, 1, background);
+
+    DrawState(hdc, hbr, NULL, (LPARAM) hicon, 0, 1, 1, 0, 0, ( DST_ICON | flags ));
+
+    /* Check the color of the pixel is correct */
+    result = GetPixel(hdc, 1, 1);
+
+    ok (color_match(result, modern_expected) ||         /* Windows 2000 and up */
+        broken(color_match(result, legacy_expected)),   /* Windows NT 4.0, 9X and below */
+        "DrawState drawing Icon with Overlaying Mask %d on Color %06X with flags %08X. "
+        "Expected a close match to %06X (modern) or %06X (legacy). Got %06X from line %d\n",
+        maskvalue, color, (DST_ICON | flags), modern_expected, legacy_expected, result, line);
+}
+
+static void test_DrawState(void)
+{
+    BITMAPINFO bitmapInfo;
+    HDC hdcDst = NULL;
+    HBITMAP bmpDst = NULL;
+    HBITMAP bmpOld = NULL;
+    UINT32 bits = 0;
+
+    hdcDst = CreateCompatibleDC(0);
+    ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n");
+    if (!hdcDst)
+        return;
+
+    if(GetDeviceCaps(hdcDst, BITSPIXEL) <= 8)
+    {
+        skip("Windows will distort DrawIconEx colors at 8-bpp and less due to palletizing.\n");
+        goto cleanup;
+    }
+
+    memset(&bitmapInfo, 0, sizeof(bitmapInfo));
+    bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bitmapInfo.bmiHeader.biWidth = 3;
+    bitmapInfo.bmiHeader.biHeight = 3;
+    bitmapInfo.bmiHeader.biBitCount = 32;
+    bitmapInfo.bmiHeader.biPlanes = 1;
+    bitmapInfo.bmiHeader.biCompression = BI_RGB;
+    bitmapInfo.bmiHeader.biSizeImage = sizeof(UINT32);
+    bmpDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+    ok (bmpDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n");
+    if (!bmpDst || !bits)
+        goto cleanup;
+    bmpOld = SelectObject(hdcDst, bmpDst);
+
+    /* potential flags to test with DrawState are: */
+    /* DSS_DISABLED embosses the icon */
+    /* DSS_MONO draw Icon using a brush as parameter 5 */
+    /* DSS_NORMAL draw Icon without any modifications */
+    /* DSS_UNION draw the Icon dithered */
+
+    check_DrawState_Size(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, __LINE__);
+    check_DrawState_Color(hdcDst, FALSE, 0x00A0B0C0, 32, 0, DSS_NORMAL, 0x00FFFFFF, 0x00C0B0A0, 0x00C0B0A0, __LINE__);
+
+cleanup:
+    if(bmpOld)
+        SelectObject(hdcDst, bmpOld);
+    if(bmpDst)
+        DeleteObject(bmpDst);
+    if(hdcDst)
+        DeleteDC(hdcDst);
+}
+
 static void test_DestroyCursor(void)
 {
     static const BYTE bmp_bits[4096];
 static void test_DestroyCursor(void)
 {
     static const BYTE bmp_bits[4096];
@@ -1321,6 +1462,7 @@ START_TEST(cursoricon)
     test_CreateIconFromResource();
     test_DrawIcon();
     test_DrawIconEx();
     test_CreateIconFromResource();
     test_DrawIcon();
     test_DrawIconEx();
+    test_DrawState();
     test_DestroyCursor();
     do_parent();
     test_child_process();
     test_DestroyCursor();
     do_parent();
     test_child_process();
index 6625837..9e1da70 100755 (executable)
@@ -2390,23 +2390,33 @@ START_TEST(dde)
 
     test_end_to_end_server(proc.hProcess, proc.hThread, TRUE);
 
 
     test_end_to_end_server(proc.hProcess, proc.hThread, TRUE);
 
-    sprintf(buffer, "%s dde endw", argv[0]);
-    CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
-                   CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
+    /* Don't bother testing W interfaces on Win9x/WinMe */
+    SetLastError(0xdeadbeef);
+    lstrcmpW(NULL, NULL);
+    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        win_skip("Skipping W-interface tests\n");
+    }
+    else
+    {
+        sprintf(buffer, "%s dde endw", argv[0]);
+        CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
+                       CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
 
 
-    test_end_to_end_server(proc.hProcess, proc.hThread, FALSE);
+        test_end_to_end_server(proc.hProcess, proc.hThread, FALSE);
 
 
-    sprintf(buffer, "%s dde enda", argv[0]);
-    CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
-                   CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
+        sprintf(buffer, "%s dde enda", argv[0]);
+        CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
+                       CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
 
 
-    test_end_to_end_server(proc.hProcess, proc.hThread, FALSE);
+        test_end_to_end_server(proc.hProcess, proc.hThread, FALSE);
 
 
-    sprintf(buffer, "%s dde endw", argv[0]);
-    CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
-                   CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
+        sprintf(buffer, "%s dde endw", argv[0]);
+        CreateProcessA(NULL, buffer, NULL, NULL, FALSE,
+                       CREATE_SUSPENDED, NULL, NULL, &startup, &proc);
 
 
-    test_end_to_end_server(proc.hProcess, proc.hThread, TRUE);
+        test_end_to_end_server(proc.hProcess, proc.hThread, TRUE);
+    }
 
     test_dde_aw_transaction();
 
 
     test_dde_aw_transaction();
 
index dfb2483..5879fbc 100755 (executable)
@@ -930,6 +930,19 @@ static INT_PTR CALLBACK DestroyOnCloseDlgWinProc (HWND hDlg, UINT uiMsg,
     return FALSE;
 }
 
     return FALSE;
 }
 
+
+static INT_PTR CALLBACK TestDefButtonDlgProc (HWND hDlg, UINT uiMsg,
+                                              WPARAM wParam, LPARAM lParam)
+{
+    switch (uiMsg)
+    {
+    case WM_INITDIALOG:
+        EndDialog(hDlg, LOWORD(SendMessage(hDlg, DM_GETDEFID, 0, 0)));
+        return TRUE;
+    }
+    return FALSE;
+}
+
 static void test_DialogBoxParamA(void)
 {
     INT_PTR ret;
 static void test_DialogBoxParamA(void)
 {
     INT_PTR ret;
@@ -965,8 +978,12 @@ static void test_DialogBoxParamA(void)
     SetLastError(0xdeadbeef);
     ret = DefDlgProcA(0, WM_ERASEBKGND, 0, 0);
     ok(ret == 0, "DefDlgProcA returned %ld, expected 0\n", ret);
     SetLastError(0xdeadbeef);
     ret = DefDlgProcA(0, WM_ERASEBKGND, 0, 0);
     ok(ret == 0, "DefDlgProcA returned %ld, expected 0\n", ret);
-    ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE,
+    ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
+       broken(GetLastError() == 0xdeadbeef),
        "got %d, expected ERROR_INVALID_WINDOW_HANDLE\n", GetLastError());
        "got %d, expected ERROR_INVALID_WINDOW_HANDLE\n", GetLastError());
+
+    ret = DialogBoxParamA(GetModuleHandle(NULL), "TEST_EMPTY_DIALOG", 0, TestDefButtonDlgProc, 0);
+    ok(ret == IDOK, "Expected IDOK\n");
 }
 
 static void test_DisabledDialogTest(void)
 }
 
 static void test_DisabledDialogTest(void)
index 7506535..aaeefb2 100755 (executable)
@@ -72,12 +72,13 @@ static struct {
     LONG last_hook_syskey_down;
     LONG last_hook_syskey_up;
     BOOL expect_alt;
     LONG last_hook_syskey_down;
     LONG last_hook_syskey_up;
     BOOL expect_alt;
+    BOOL sendinput_broken;
 } key_status;
 
 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
 static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD);
 
 } key_status;
 
 static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
 static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD);
 
-#define MAXKEYEVENTS 10
+#define MAXKEYEVENTS 12
 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
                                        and only one message */
 
 #define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
                                        and only one message */
 
@@ -891,6 +892,11 @@ static void test_Input_blackbox(void)
     HWND window;
     HHOOK hook;
 
     HWND window;
     HHOOK hook;
 
+    if (GetKeyboardLayout(0) != (HKL)(ULONG_PTR)0x04090409)
+    {
+        skip("Skipping Input_blackbox test on non-US keyboard\n");
+        return;
+    }
     window = CreateWindow("Static", NULL, WS_POPUP|WS_HSCROLL|WS_VSCROLL
         |WS_VISIBLE, 0, 0, 200, 60, NULL, NULL,
         NULL, NULL);
     window = CreateWindow("Static", NULL, WS_POPUP|WS_HSCROLL|WS_VSCROLL
         |WS_VISIBLE, 0, 0, 200, 60, NULL, NULL,
         NULL, NULL);
@@ -950,6 +956,7 @@ static void reset_key_status(void)
     key_status.last_hook_syskey_down = -1;
     key_status.last_hook_syskey_up = -1;
     key_status.expect_alt = FALSE;
     key_status.last_hook_syskey_down = -1;
     key_status.last_hook_syskey_up = -1;
     key_status.expect_alt = FALSE;
+    key_status.sendinput_broken = FALSE;
 }
 
 static void test_unicode_keys(HWND hwnd, HHOOK hook)
 }
 
 static void test_unicode_keys(HWND hwnd, HHOOK hook)
@@ -968,38 +975,42 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
     inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
 
     reset_key_status();
     inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
 
     reset_key_status();
-    SendInput(1, (INPUT*)inputs, sizeof(INPUT));
+    pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
-    ok(key_status.last_key_down == VK_PACKET,
-        "Last keydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_down);
-    ok(key_status.last_char == 0x3c0,
-        "Last char msg wparam should have been 0x3c0 (was: 0x%x)\n", key_status.last_char);
-    if(hook)
-        ok(key_status.last_hook_down == 0x3c0,
-            "Last hookdown msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_down);
+    if(!key_status.sendinput_broken){
+        ok(key_status.last_key_down == VK_PACKET,
+            "Last keydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_down);
+        ok(key_status.last_char == 0x3c0,
+            "Last char msg wparam should have been 0x3c0 (was: 0x%x)\n", key_status.last_char);
+        if(hook)
+            ok(key_status.last_hook_down == 0x3c0,
+                "Last hookdown msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_down);
+    }
 
     inputs[1].u.ki.wVk = 0;
     inputs[1].u.ki.wScan = 0x3c0;
     inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
 
     reset_key_status();
 
     inputs[1].u.ki.wVk = 0;
     inputs[1].u.ki.wScan = 0x3c0;
     inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
 
     reset_key_status();
-    SendInput(1, (INPUT*)(inputs+1), sizeof(INPUT));
+    pSendInput(1, (INPUT*)(inputs+1), sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
-    ok(key_status.last_key_up == VK_PACKET,
-        "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
-    if(hook)
-        ok(key_status.last_hook_up == 0x3c0,
-            "Last hookup msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_up);
+    if(!key_status.sendinput_broken){
+        ok(key_status.last_key_up == VK_PACKET,
+            "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
+        if(hook)
+            ok(key_status.last_hook_up == 0x3c0,
+                "Last hookup msg should have been 0x3c0, was: 0x%x\n", key_status.last_hook_up);
+    }
 
     /* holding alt, pressing & releasing a unicode character, releasing alt */
     inputs[0].u.ki.wVk = VK_LMENU;
 
     /* holding alt, pressing & releasing a unicode character, releasing alt */
     inputs[0].u.ki.wVk = VK_LMENU;
@@ -1012,20 +1023,22 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
 
     reset_key_status();
     key_status.expect_alt = TRUE;
 
     reset_key_status();
     key_status.expect_alt = TRUE;
-    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    pSendInput(2, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
-    ok(key_status.last_syskey_down == VK_PACKET,
-        "Last syskeydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_syskey_down);
-    ok(key_status.last_syschar == 0x3041,
-        "Last syschar msg should have been 0x3041 (was: 0x%x)\n", key_status.last_syschar);
-    if(hook)
-        ok(key_status.last_hook_syskey_down == 0x3041,
-            "Last hooksysdown msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_syskey_down);
+    if(!key_status.sendinput_broken){
+        ok(key_status.last_syskey_down == VK_PACKET,
+            "Last syskeydown msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_syskey_down);
+        ok(key_status.last_syschar == 0x3041,
+            "Last syschar msg should have been 0x3041 (was: 0x%x)\n", key_status.last_syschar);
+        if(hook)
+            ok(key_status.last_hook_syskey_down == 0x3041,
+                "Last hooksysdown msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_syskey_down);
+    }
 
     inputs[1].u.ki.wVk = 0;
     inputs[1].u.ki.wScan = 0x3041;
 
     inputs[1].u.ki.wVk = 0;
     inputs[1].u.ki.wScan = 0x3041;
@@ -1037,18 +1050,20 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
 
     reset_key_status();
     key_status.expect_alt = TRUE;
 
     reset_key_status();
     key_status.expect_alt = TRUE;
-    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    pSendInput(2, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
         }
         DispatchMessageW(&msg);
     }
-    ok(key_status.last_key_up == VK_PACKET,
-        "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
-    if(hook)
-        ok(key_status.last_hook_up == 0x3041,
-            "Last hook up msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_up);
+    if(!key_status.sendinput_broken){
+        ok(key_status.last_key_up == VK_PACKET,
+            "Last keyup msg should have been VK_PACKET[0x%04x] (was: 0x%x)\n", VK_PACKET, key_status.last_key_up);
+        if(hook)
+            ok(key_status.last_hook_up == 0x3041,
+                "Last hook up msg should have been 0x3041, was: 0x%x\n", key_status.last_hook_up);
+    }
 }
 
 static LRESULT CALLBACK unicode_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
 }
 
 static LRESULT CALLBACK unicode_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
@@ -1081,8 +1096,16 @@ static LRESULT CALLBACK llkbd_unicode_hook(int nCode, WPARAM wParam, LPARAM lPar
 {
     if(nCode == HC_ACTION){
         LPKBDLLHOOKSTRUCT info = (LPKBDLLHOOKSTRUCT)lParam;
 {
     if(nCode == HC_ACTION){
         LPKBDLLHOOKSTRUCT info = (LPKBDLLHOOKSTRUCT)lParam;
-        ok(info->vkCode == VK_PACKET || (key_status.expect_alt && info->vkCode == VK_LMENU), "vkCode should have been VK_PACKET[%04x], was: %04x\n", VK_PACKET, info->vkCode);
-        key_status.expect_alt = FALSE;
+        if(!info->vkCode){
+            key_status.sendinput_broken = TRUE;
+            win_skip("SendInput doesn't support unicode on this platform\n");
+        }else{
+            if(key_status.expect_alt){
+                ok(info->vkCode == VK_LMENU, "vkCode should have been VK_LMENU[0x%04x], was: 0x%x\n", VK_LMENU, info->vkCode);
+                key_status.expect_alt = FALSE;
+            }else
+                ok(info->vkCode == VK_PACKET, "vkCode should have been VK_PACKET[0x%04x], was: 0x%x\n", VK_PACKET, info->vkCode);
+        }
         switch(wParam){
         case WM_KEYDOWN:
             key_status.last_hook_down = info->scanCode;
         switch(wParam){
         case WM_KEYDOWN:
             key_status.last_hook_down = info->scanCode;
index 30acaa2..761f3aa 100755 (executable)
@@ -5257,7 +5257,7 @@ static WNDPROC old_button_proc;
 
 static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 
 static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    static long defwndproc_counter = 0;
+    static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct recvd_message msg;
 
     LRESULT ret;
     struct recvd_message msg;
 
@@ -5458,7 +5458,7 @@ static WNDPROC old_static_proc;
 
 static LRESULT CALLBACK static_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 
 static LRESULT CALLBACK static_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    static long defwndproc_counter = 0;
+    static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct recvd_message msg;
 
     LRESULT ret;
     struct recvd_message msg;
 
@@ -5553,7 +5553,7 @@ static WNDPROC old_combobox_proc;
 
 static LRESULT CALLBACK combobox_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 
 static LRESULT CALLBACK combobox_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    static long defwndproc_counter = 0;
+    static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct recvd_message msg;
 
     LRESULT ret;
     struct recvd_message msg;
 
@@ -7813,6 +7813,12 @@ static VOID CALLBACK tfunc(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime)
 {
 }
 
 {
 }
 
+static VOID CALLBACK tfunc_crash(HWND hwnd, UINT uMsg, UINT_PTR id, DWORD dwTime)
+{
+    /* Crash on purpose */
+    *(volatile int *)0 = 2;
+}
+
 #define TIMER_ID  0x19
 
 static DWORD WINAPI timer_thread_proc(LPVOID x)
 #define TIMER_ID  0x19
 
 static DWORD WINAPI timer_thread_proc(LPVOID x)
@@ -7834,6 +7840,7 @@ static void test_timers(void)
 {
     struct timer_info info;
     DWORD id;
 {
     struct timer_info info;
     DWORD id;
+    MSG msg;
 
     info.hWnd = CreateWindow ("TestWindowClass", NULL,
        WS_OVERLAPPEDWINDOW ,
 
     info.hWnd = CreateWindow ("TestWindowClass", NULL,
        WS_OVERLAPPEDWINDOW ,
@@ -7856,6 +7863,26 @@ static void test_timers(void)
     ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n");
 
     ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
     ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n");
 
     ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
+
+    /* Test timer callback with crash */
+    SetLastError(0xdeadbeef);
+    info.hWnd = CreateWindowW(testWindowClassW, NULL,
+                              WS_OVERLAPPEDWINDOW ,
+                              CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0,
+                              NULL, NULL, 0);
+    if ((!info.hWnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) || /* Win9x/Me */
+        (!pGetMenuInfo)) /* Win95/NT4 */
+    {
+        win_skip("Test would crash on Win9x/WinMe/NT4\n");
+        DestroyWindow(info.hWnd);
+        return;
+    }
+    info.id = SetTimer(info.hWnd, TIMER_ID, 0, tfunc_crash);
+    ok(info.id, "SetTimer failed\n");
+    Sleep(150);
+    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+
+    ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
 }
 
 static int count = 0;
 }
 
 static int count = 0;
@@ -8984,7 +9011,7 @@ static WNDPROC old_edit_proc;
 
 static LRESULT CALLBACK edit_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 
 static LRESULT CALLBACK edit_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    static long defwndproc_counter = 0;
+    static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct recvd_message msg;
 
     LRESULT ret;
     struct recvd_message msg;
 
@@ -11724,6 +11751,171 @@ static void test_defwinproc(void)
     DestroyWindow( hwnd);
 }
 
     DestroyWindow( hwnd);
 }
 
+#define clear_clipboard(hwnd)  clear_clipboard_(__LINE__, (hwnd))
+static void clear_clipboard_(int line, HWND hWnd)
+{
+    BOOL succ;
+    succ = OpenClipboard(hWnd);
+    ok_(__FILE__, line)(succ, "OpenClipboard failed, err=%u\n", GetLastError());
+    succ = EmptyClipboard();
+    ok_(__FILE__, line)(succ, "EmptyClipboard failed, err=%u\n", GetLastError());
+    succ = CloseClipboard();
+    ok_(__FILE__, line)(succ, "CloseClipboard failed, err=%u\n", GetLastError());
+}
+
+#define expect_HWND(expected, got) expect_HWND_(__LINE__, (expected), (got))
+static void expect_HWND_(int line, HWND expected, HWND got)
+{
+    ok_(__FILE__, line)(got==expected, "Expected %p, got %p\n", expected, got);
+}
+
+static WNDPROC pOldViewerProc;
+
+static LRESULT CALLBACK recursive_viewer_proc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static BOOL recursion_guard;
+
+    if (message == WM_DRAWCLIPBOARD && !recursion_guard)
+    {
+        recursion_guard = TRUE;
+        clear_clipboard(hWnd);
+        recursion_guard = FALSE;
+    }
+    return CallWindowProcA(pOldViewerProc, hWnd, message, wParam, lParam);
+}
+
+static void test_clipboard_viewers(void)
+{
+    static struct message wm_change_cb_chain[] =
+    {
+        { WM_CHANGECBCHAIN, sent|wparam|lparam, 0, 0 },
+        { 0 }
+    };
+    static const struct message wm_clipboard_destroyed[] =
+    {
+        { WM_DESTROYCLIPBOARD, sent|wparam|lparam, 0, 0 },
+        { 0 }
+    };
+    static struct message wm_clipboard_changed[] =
+    {
+        { WM_DRAWCLIPBOARD, sent|wparam|lparam, 0, 0 },
+        { 0 }
+    };
+    static struct message wm_clipboard_changed_and_owned[] =
+    {
+        { WM_DESTROYCLIPBOARD, sent|wparam|lparam, 0, 0 },
+        { WM_DRAWCLIPBOARD, sent|wparam|lparam, 0, 0 },
+        { 0 }
+    };
+
+    HINSTANCE hInst = GetModuleHandleA(NULL);
+    HWND hWnd1, hWnd2, hWnd3;
+    HWND hOrigViewer;
+    HWND hRet;
+
+    hWnd1 = CreateWindowExA(0, "TestWindowClass", "Clipboard viewer test wnd 1",
+        WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
+        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+        GetDesktopWindow(), NULL, hInst, NULL);
+    hWnd2 = CreateWindowExA(0, "SimpleWindowClass", "Clipboard viewer test wnd 2",
+        WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
+        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+        GetDesktopWindow(), NULL, hInst, NULL);
+    hWnd3 = CreateWindowExA(0, "SimpleWindowClass", "Clipboard viewer test wnd 3",
+        WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
+        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+        GetDesktopWindow(), NULL, hInst, NULL);
+    trace("clipbd viewers: hWnd1=%p, hWnd2=%p, hWnd3=%p\n", hWnd1, hWnd2, hWnd3);
+    assert(hWnd1 && hWnd2 && hWnd3);
+
+    flush_sequence();
+
+    /* Test getting the clipboard viewer and setting the viewer to NULL. */
+    hOrigViewer = GetClipboardViewer();
+    hRet = SetClipboardViewer(NULL);
+    ok_sequence(WmEmptySeq, "set viewer to NULL", FALSE);
+    expect_HWND(hOrigViewer, hRet);
+    expect_HWND(NULL, GetClipboardViewer());
+
+    /* Test registering hWnd1 as a viewer. */
+    hRet = SetClipboardViewer(hWnd1);
+    wm_clipboard_changed[0].wParam = (WPARAM) GetClipboardOwner();
+    ok_sequence(wm_clipboard_changed, "set viewer NULL->1", FALSE);
+    expect_HWND(NULL, hRet);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    /* Test that changing the clipboard actually refreshes the registered viewer. */
+    clear_clipboard(hWnd1);
+    wm_clipboard_changed[0].wParam = (WPARAM) GetClipboardOwner();
+    ok_sequence(wm_clipboard_changed, "clear clipbd (viewer=owner=1)", FALSE);
+
+    /* Again, but with different owner. */
+    clear_clipboard(hWnd2);
+    wm_clipboard_changed_and_owned[1].wParam = (WPARAM) GetClipboardOwner();
+    ok_sequence(wm_clipboard_changed_and_owned, "clear clipbd (viewer=1, owner=2)", FALSE);
+
+    /* Test re-registering same window. */
+    hRet = SetClipboardViewer(hWnd1);
+    wm_clipboard_changed[0].wParam = (WPARAM) GetClipboardOwner();
+    ok_sequence(wm_clipboard_changed, "set viewer 1->1", FALSE);
+    expect_HWND(hWnd1, hRet);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    /* Test ChangeClipboardChain. */
+    ChangeClipboardChain(hWnd2, hWnd3);
+    wm_change_cb_chain[0].wParam = (WPARAM) hWnd2;
+    wm_change_cb_chain[0].lParam = (LPARAM) hWnd3;
+    ok_sequence(wm_change_cb_chain, "change chain (viewer=1, remove=2, next=3)", FALSE);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    ChangeClipboardChain(hWnd2, NULL);
+    wm_change_cb_chain[0].wParam = (WPARAM) hWnd2;
+    wm_change_cb_chain[0].lParam = 0;
+    ok_sequence(wm_change_cb_chain, "change chain (viewer=1, remove=2, next=NULL)", FALSE);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    ChangeClipboardChain(NULL, hWnd2);
+    ok_sequence(WmEmptySeq, "change chain (viewer=1, remove=NULL, next=2)", TRUE);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    /* Actually change clipboard viewer with ChangeClipboardChain. */
+    ChangeClipboardChain(hWnd1, hWnd2);
+    ok_sequence(WmEmptySeq, "change chain (viewer=remove=1, next=2)", FALSE);
+    expect_HWND(hWnd2, GetClipboardViewer());
+
+    /* Test that no refresh messages are sent when viewer has unregistered. */
+    clear_clipboard(hWnd2);
+    ok_sequence(WmEmptySeq, "clear clipd (viewer=2, owner=1)", FALSE);
+
+    /* Register hWnd1 again. */
+    ChangeClipboardChain(hWnd2, hWnd1);
+    ok_sequence(WmEmptySeq, "change chain (viewer=remove=2, next=1)", FALSE);
+    expect_HWND(hWnd1, GetClipboardViewer());
+
+    /* Subclass hWnd1 so that when it receives a WM_DRAWCLIPBOARD message, it
+     * changes the clipboard. When this happens, the system shouldn't send
+     * another WM_DRAWCLIPBOARD (as this could cause an infinite loop).
+     */
+    pOldViewerProc = (WNDPROC) SetWindowLongPtrA(hWnd1, GWLP_WNDPROC, (LONG_PTR) recursive_viewer_proc);
+    clear_clipboard(hWnd2);
+    /* The clipboard owner is changed in recursive_viewer_proc: */
+    wm_clipboard_changed[0].wParam = (WPARAM) hWnd2;
+    ok_sequence(wm_clipboard_changed, "recursive clear clipbd (viewer=1, owner=2)", TRUE);
+
+    /* Test unregistering. */
+    ChangeClipboardChain(hWnd1, NULL);
+    ok_sequence(WmEmptySeq, "change chain (viewer=remove=1, next=NULL)", FALSE);
+    expect_HWND(NULL, GetClipboardViewer());
+
+    clear_clipboard(hWnd1);
+    ok_sequence(wm_clipboard_destroyed, "clear clipbd (no viewer, owner=1)", FALSE);
+
+    DestroyWindow(hWnd1);
+    DestroyWindow(hWnd2);
+    DestroyWindow(hWnd3);
+    SetClipboardViewer(hOrigViewer);
+}
+
 static void test_PostMessage(void)
 {
     static const struct
 static void test_PostMessage(void)
 {
     static const struct
@@ -11787,7 +11979,7 @@ static void test_PostMessage(void)
 START_TEST(msg)
 {
     BOOL ret;
 START_TEST(msg)
 {
     BOOL ret;
-    FARPROC pIsWinEventHookInstalled = 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
+    BOOL (WINAPI *pIsWinEventHookInstalled)(DWORD)= 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
 
     init_procs();
 
 
     init_procs();
 
@@ -11867,6 +12059,7 @@ START_TEST(msg)
     test_menu_messages();
     test_paintingloop();
     test_defwinproc();
     test_menu_messages();
     test_paintingloop();
     test_defwinproc();
+    test_clipboard_viewers();
     /* keep it the last test, under Windows it tends to break the tests
      * which rely on active/foreground windows being correct.
      */
     /* keep it the last test, under Windows it tends to break the tests
      * which rely on active/foreground windows being correct.
      */
index 79c036c..1869475 100755 (executable)
@@ -98,6 +98,13 @@ BEGIN
     PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
 END
 
     PUSHBUTTON      "Cancel",IDCANCEL,129,24,50,14
 END
 
+TEST_EMPTY_DIALOG DIALOG DISCARDABLE  0, 0, 186, 95
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog"
+FONT 8, "MS Sans Serif"
+BEGIN
+END
+
 MULTI_EDIT_DIALOG DIALOG DISCARDABLE 0, 0, 160, 75
 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER
 CAPTION "Multiple Edit Test"
 MULTI_EDIT_DIALOG DIALOG DISCARDABLE 0, 0, 160, 75
 STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER
 CAPTION "Multiple Edit Test"
index 7b2edaf..dd1e708 100755 (executable)
@@ -942,7 +942,8 @@ static void test_SPI_SETSCREENSAVEACTIVE( void )       /*     17 */
 
         rc=SystemParametersInfoA( SPI_GETSCREENSAVEACTIVE, 0, &v, 0 );
         ok(rc!=0,"%d: rc=%d err=%d\n",i,rc,GetLastError());
 
         rc=SystemParametersInfoA( SPI_GETSCREENSAVEACTIVE, 0, &v, 0 );
         ok(rc!=0,"%d: rc=%d err=%d\n",i,rc,GetLastError());
-        eq( v, vals[i], "SPI_{GET,SET}SCREENSAVEACTIVE", "%d" );
+        ok(v == vals[i] || broken(! v) /* Win 7 */,
+           "SPI_{GET,SET}SCREENSAVEACTIVE: got %d instead of %d\n", v, vals[i]);
     }
 
     rc=SystemParametersInfoA( SPI_SETSCREENSAVEACTIVE, old_b, 0, SPIF_UPDATEINIFILE );
     }
 
     rc=SystemParametersInfoA( SPI_SETSCREENSAVEACTIVE, old_b, 0, SPIF_UPDATEINIFILE );
index 080e5dd..88ea6b5 100755 (executable)
@@ -80,7 +80,7 @@ static void test_DrawTextCalcRect(void)
     ok( textheight, "DrawTextA error %u\n", GetLastError());
 
     trace("MM_HIENGLISH rect.bottom %d\n", rect.bottom);
     ok( textheight, "DrawTextA error %u\n", GetLastError());
 
     trace("MM_HIENGLISH rect.bottom %d\n", rect.bottom);
-    todo_wine ok(rect.bottom < 0, "In MM_HIENGLISH, DrawText with "
+    ok(rect.bottom < 0, "In MM_HIENGLISH, DrawText with "
        "DT_CALCRECT should return a negative rectangle bottom. "
        "(bot=%d)\n", rect.bottom);
 
        "DT_CALCRECT should return a negative rectangle bottom. "
        "(bot=%d)\n", rect.bottom);
 
index cf1de25..63f25bc 100644 (file)
@@ -3218,6 +3218,59 @@ static void test_window_styles(void)
     check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE);
 }
 
     check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|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)
 {
     HDC hdc;
 static void test_scrollvalidate( HWND parent)
 {
     HDC hdc;
@@ -5724,6 +5777,7 @@ START_TEST(win)
     test_mouse_input(hwndMain);
     test_validatergn(hwndMain);
     test_nccalcscroll( hwndMain);
     test_mouse_input(hwndMain);
     test_validatergn(hwndMain);
     test_nccalcscroll( hwndMain);
+    test_scrollwindow( hwndMain);
     test_scrollvalidate( hwndMain);
     test_scrolldc( hwndMain);
     test_scroll();
     test_scrollvalidate( hwndMain);
     test_scrolldc( hwndMain);
     test_scroll();