[USER32_WINETEST] Sync with Wine Staging 4.18 except win.c (PR #1980). CORE-16441
authorAmine Khaldi <amine.khaldi@reactos.org>
Sun, 1 Dec 2019 18:40:48 +0000 (19:40 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sun, 1 Dec 2019 18:40:48 +0000 (19:40 +0100)
22 files changed:
modules/rostests/winetests/user32/CMakeLists.txt
modules/rostests/winetests/user32/broadcast.c
modules/rostests/winetests/user32/class.c
modules/rostests/winetests/user32/clipboard.c
modules/rostests/winetests/user32/cursoricon.c
modules/rostests/winetests/user32/dde.c
modules/rostests/winetests/user32/dialog.c
modules/rostests/winetests/user32/edit.c
modules/rostests/winetests/user32/input.c
modules/rostests/winetests/user32/listbox.c
modules/rostests/winetests/user32/menu.c
modules/rostests/winetests/user32/monitor.c
modules/rostests/winetests/user32/msg.c
modules/rostests/winetests/user32/rawinput.c [new file with mode: 0644]
modules/rostests/winetests/user32/resource.c
modules/rostests/winetests/user32/resource.rc
modules/rostests/winetests/user32/scroll.c
modules/rostests/winetests/user32/sysparams.c
modules/rostests/winetests/user32/testlist.c
modules/rostests/winetests/user32/text.c
modules/rostests/winetests/user32/uitools.c
modules/rostests/winetests/user32/wsprintf.c

index bf635ae..4bd5253 100644 (file)
@@ -1,6 +1,6 @@
 
 remove_definitions(-DWINVER=0x502 -D_WIN32_WINNT=0x502)
-add_definitions(-DWINVER=0x600 -D_WIN32_WINNT=0x600)
+add_definitions(-DWINVER=0x602 -D_WIN32_WINNT=0x602)
 
 if(MSVC)
     # Disable warning C4477 (printf format warnings)
@@ -25,6 +25,7 @@ list(APPEND SOURCE
     menu.c
     monitor.c
     msg.c
+    rawinput.c
     resource.c
     scroll.c
     static.c
index 949d1e8..91afcc2 100644 (file)
@@ -130,39 +130,15 @@ if (0) /* TODO: Check the hang flags */
     ok(0, "Returned: %d\n", ret);
 }
 
-    recips = BSM_APPLICATIONS;
-    ResetEvent(hevent);
-    ret = broadcast( BSF_POSTMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, 0 );
-    ok(ret==1, "Returned: %d\n", ret);
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    PulseEvent(hevent);
-
     SetLastError( 0xdeadbeef );
     recips = BSM_APPLICATIONS;
     ret = broadcast( BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, 0 );
-    if (ret)
-    {
-        ok(ret==1, "Returned: %d\n", ret);
-        ok(WaitForSingleObject(hevent, 0) != WAIT_OBJECT_0, "Synchronous message sent instead\n");
-        PulseEvent(hevent);
-
-        recips = BSM_APPLICATIONS;
-        ret = broadcast( BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY );
-        ok(ret==1, "Returned: %d\n", ret);
-        ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-        PulseEvent(hevent);
-
-        recips = BSM_APPLICATIONS;
-        ret = broadcast( BSF_SENDNOTIFYMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY );
-        ok(!ret, "Returned: %d\n", ret);
-        ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-        PulseEvent(hevent);
-    }
-    else  /* BSF_SENDNOTIFYMESSAGE not supported on NT4 */
-        ok( GetLastError() == ERROR_INVALID_PARAMETER, "failed with err %u\n", GetLastError() );
+    ok(ret==1, "Returned: %d\n", ret);
+    ok(WaitForSingleObject(hevent, 0) != WAIT_OBJECT_0, "Synchronous message sent instead\n");
+    PulseEvent(hevent);
 
     recips = BSM_APPLICATIONS;
-    ret = broadcast( 0, &recips, WM_NULL, 100, 0 );
+    ret = broadcast( BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY );
     ok(ret==1, "Returned: %d\n", ret);
     ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
     PulseEvent(hevent);
@@ -216,13 +192,6 @@ if (0) /* TODO: Check the hang flags */
     ok(0, "Returned: %d\n", ret);
 }
 
-    recips = BSM_APPLICATIONS;
-    ResetEvent(hevent);
-    ret = broadcastex( BSF_POSTMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, 0, NULL );
-    ok(ret==1, "Returned: %d\n", ret);
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    PulseEvent(hevent);
-
     recips = BSM_APPLICATIONS;
     ret = broadcastex( BSF_POSTMESSAGE|BSF_SENDNOTIFYMESSAGE, &recips, WM_NULL, 100, 0, NULL );
     ok(ret==1, "Returned: %d\n", ret);
@@ -234,84 +203,6 @@ if (0) /* TODO: Check the hang flags */
     ok(ret==1, "Returned: %d\n", ret);
     ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
     PulseEvent(hevent);
-
-    recips = BSM_APPLICATIONS;
-    ret = broadcastex( BSF_SENDNOTIFYMESSAGE|BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY, NULL );
-    ok(!ret, "Returned: %d\n", ret);
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    PulseEvent(hevent);
-
-    recips = BSM_APPLICATIONS;
-    ret = broadcastex( 0, &recips, WM_NULL, 100, 0, NULL );
-    ok(ret==1, "Returned: %d\n", ret);
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    PulseEvent(hevent);
-}
-
-static void test_noprivileges(void)
-{
-    HANDLE token;
-    DWORD recips;
-    BOOL ret;
-
-    static const DWORD BSM_ALL_RECIPS = BSM_VXDS | BSM_NETDRIVER |
-                                        BSM_INSTALLABLEDRIVERS | BSM_APPLICATIONS;
-
-    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
-    {
-        skip("Can't open security token for process\n");
-        return;
-    }
-    if (!AdjustTokenPrivileges(token, TRUE, NULL, 0, NULL, NULL))
-    {
-        skip("Can't adjust security token for process\n");
-        return;
-    }
-
-    trace("Trying privileged edition!\n");
-    SetLastError(0xcafebabe);
-    recips = BSM_ALLDESKTOPS;
-    ResetEvent(hevent);
-    ret = BroadcastSystemMessageExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL );
-    ok(ret==1, "Returned: %d error %u\n", ret, GetLastError());
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    ok(recips == BSM_ALLDESKTOPS ||
-       recips == BSM_ALL_RECIPS, /* win2k3 */
-       "Received by: %08x\n", recips);
-    PulseEvent(hevent);
-
-    SetLastError(0xcafebabe);
-    recips = BSM_ALLCOMPONENTS;
-    ResetEvent(hevent);
-    ret = BroadcastSystemMessageExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL );
-    ok(ret==1, "Returned: %d error %u\n", ret, GetLastError());
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    ok(recips == BSM_ALLCOMPONENTS ||
-       recips == BSM_ALL_RECIPS, /* win2k3 */
-       "Received by: %08x\n", recips);
-    PulseEvent(hevent);
-
-    SetLastError(0xcafebabe);
-    recips = BSM_ALLDESKTOPS|BSM_APPLICATIONS;
-    ResetEvent(hevent);
-    ret = BroadcastSystemMessageExW( BSF_QUERY, &recips, WM_NULL, 100, 0, NULL );
-    ok(ret==1, "Returned: %d error %u\n", ret, GetLastError());
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    ok(recips == (BSM_ALLDESKTOPS|BSM_APPLICATIONS) ||
-       recips == BSM_APPLICATIONS, /* win2k3 */
-       "Received by: %08x\n", recips);
-    PulseEvent(hevent);
-
-    SetLastError(0xcafebabe);
-    recips = BSM_ALLDESKTOPS|BSM_APPLICATIONS;
-    ResetEvent(hevent);
-    ret = BroadcastSystemMessageExW( BSF_QUERY, &recips, WM_NULL, 100, BROADCAST_QUERY_DENY, NULL );
-    ok(!ret, "Returned: %d\n", ret);
-    ok(WaitForSingleObject(hevent, 0) != WAIT_TIMEOUT, "Asynchronous message sent instead\n");
-    ok(recips == (BSM_ALLDESKTOPS|BSM_APPLICATIONS) ||
-       recips == BSM_APPLICATIONS, /* win2k3 */
-       "Received by: %08x\n", recips);
-    PulseEvent(hevent);
 }
 
 START_TEST(broadcast)
@@ -330,7 +221,4 @@ START_TEST(broadcast)
 
     trace("Running BroadcastSystemMessageExW tests\n");
     test_parametersEx(BroadcastSystemMessageExW);
-
-    trace("Attempting privileges checking tests\n");
-    test_noprivileges();
 }
index 934ea0b..d8cf14a 100644 (file)
@@ -117,6 +117,8 @@ static void ClassTest(HINSTANCE hInstance, BOOL global)
         return;
     ok(classatom, "failed to register class\n");
 
+    ok(GetClipboardFormatNameW(classatom, str, ARRAY_SIZE(str)) != 0, "atom not found\n");
+
     ok(!RegisterClassW (&cls),
         "RegisterClass of the same class should fail for the second time\n");
 
@@ -171,7 +173,7 @@ static void ClassTest(HINSTANCE hInstance, BOOL global)
     }
 
     /* check GetClassName */
-    i = GetClassNameW(hTestWnd, str, sizeof(str)/sizeof(str[0]));
+    i = GetClassNameW(hTestWnd, str, ARRAY_SIZE(str));
     ok(i == lstrlenW(className),
         "GetClassName returned incorrect length\n");
     ok(!lstrcmpW(className,str),
@@ -232,6 +234,8 @@ static void ClassTest(HINSTANCE hInstance, BOOL global)
     ok(UnregisterClassW(className, hInstance),
         "UnregisterClass() failed\n");
 
+    ok(GetClipboardFormatNameW(classatom, str, ARRAY_SIZE(str)) == 0,
+        "atom still found\n");
     return;
 }
 
@@ -647,7 +651,6 @@ static void test_builtinproc(void)
         "ScrollBar",
         "#32770",  /* dialog */
     };
-    static const int NUM_NORMAL_CLASSES = (sizeof(NORMAL_CLASSES)/sizeof(NORMAL_CLASSES[0]));
     static const char classA[] = "deftest";
     static const WCHAR classW[] = {'d','e','f','t','e','s','t',0};
     WCHAR unistring[] = {0x142, 0x40e, 0x3b4, 0};  /* a string that would be destroyed by a W->A->W conversion */
@@ -658,7 +661,7 @@ static void test_builtinproc(void)
     WCHAR buf[128];
     ATOM atom;
     HWND hwnd;
-    int i;
+    unsigned int i;
 
     pDefWindowProcA = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "DefWindowProcA");
     pDefWindowProcW = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "DefWindowProcW");
@@ -728,7 +731,7 @@ static void test_builtinproc(void)
     ok(IsWindowUnicode(hwnd) ||
        broken(!IsWindowUnicode(hwnd)) /* Windows 8 and 10 */,
        "Windows should be Unicode\n");
-    SendMessageW(hwnd, WM_GETTEXT, sizeof(buf) / sizeof(buf[0]), (LPARAM)buf);
+    SendMessageW(hwnd, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf);
     if (IsWindowUnicode(hwnd))
         ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n");
     else
@@ -773,7 +776,7 @@ static void test_builtinproc(void)
 
     /* For most of the builtin controls both GetWindowLongPtrA and W returns a pointer that is executed directly
      * by CallWindowProcA/W */
-    for (i = 0; i < NUM_NORMAL_CLASSES; i++)
+    for (i = 0; i < ARRAY_SIZE(NORMAL_CLASSES); i++)
     {
         WNDPROC procA, procW;
         hwnd = CreateWindowExA(0, NORMAL_CLASSES[i], classA, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 680, 260,
@@ -880,7 +883,7 @@ static void test_builtinproc(void)
 
 static LRESULT WINAPI TestDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    return DefWindowProcA(hWnd, uMsg, wParam, lParam);
+    return DefDlgProcA(hWnd, uMsg, wParam, lParam);
 }
 
 static BOOL RegisterTestDialog(HINSTANCE hInstance)
@@ -942,7 +945,7 @@ static const struct
 static void test_extra_values(void)
 {
     int i;
-    for(i=0; i< sizeof(extra_values)/sizeof(extra_values[0]); i++)
+    for(i = 0; i < ARRAY_SIZE(extra_values); i++)
     {
         WNDCLASSEXA wcx;
         BOOL ret = GetClassInfoExA(NULL,extra_values[i].name,&wcx);
@@ -1000,6 +1003,7 @@ if (0) { /* crashes under XP */
     SetLastError(0xdeadbeef);
     ret = GetClassInfoExA(0, "static", &wcx);
     ok(ret, "GetClassInfoExA() error %d\n", GetLastError());
+    ok(GetLastError() == 0xdeadbeef, "Unexpected error code %d\n", GetLastError());
     ok(wcx.cbSize == 0, "expected 0, got %u\n", wcx.cbSize);
     ok(wcx.lpfnWndProc != NULL, "got null proc\n");
 
@@ -1135,7 +1139,7 @@ static void test_comctl32_class( const char *name )
 
         name++;
 
-        GetTempPathA(sizeof(path)/sizeof(path[0]), path);
+        GetTempPathA(ARRAY_SIZE(path), path);
         strcat(path, "comctl32_class.manifest");
 
         create_manifest_file(path, comctl32_manifest);
@@ -1163,7 +1167,7 @@ static void test_comctl32_class( const char *name )
         if (!ret)
             goto skiptest;
 
-        MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, sizeof(nameW)/sizeof(WCHAR) );
+        MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, ARRAY_SIZE(nameW));
         ret = GetClassInfoW( 0, nameW, &wcW );
         ok( ret, "GetClassInfoW failed for %s\n", name );
         module = GetModuleHandleA( "comctl32" );
@@ -1189,7 +1193,7 @@ static void test_comctl32_class( const char *name )
         ret = GetClassInfoA( 0, name, &wcA );
         ok( ret || broken(!ret) /* <= winxp */, "GetClassInfoA failed for %s\n", name );
         if (!ret) return;
-        MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, sizeof(nameW)/sizeof(WCHAR) );
+        MultiByteToWideChar( CP_ACP, 0, name, -1, nameW, ARRAY_SIZE(nameW));
         ret = GetClassInfoW( 0, nameW, &wcW );
         ok( ret, "GetClassInfoW failed for %s\n", name );
         module = GetModuleHandleA( "comctl32" );
@@ -1245,7 +1249,7 @@ static void test_comctl32_classes(void)
     };
 
     winetest_get_mainargs( &argv );
-    for (i = 0; i < sizeof(classes) / sizeof(classes[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(classes); i++)
     {
         memset( &startup, 0, sizeof(startup) );
         startup.cb = sizeof( startup );
@@ -1321,10 +1325,10 @@ static void test_actctx_classes(void)
     ATOM class;
     HINSTANCE hinst;
     char buff[64];
-    HWND hwnd;
+    HWND hwnd, hwnd2;
     char path[MAX_PATH];
 
-    GetTempPathA(sizeof(path)/sizeof(path[0]), path);
+    GetTempPathA(ARRAY_SIZE(path), path);
     strcat(path, "actctx_classes.manifest");
 
     create_manifest_file(path, main_manifest);
@@ -1355,6 +1359,12 @@ static void test_actctx_classes(void)
     hwnd = CreateWindowExA(0, testclass, "test", 0, 0, 0, 0, 0, 0, 0, hinst, 0);
     ok(hwnd != NULL, "Failed to create a window.\n");
 
+    hwnd2 = FindWindowExA(NULL, NULL, "MyTestClass", NULL);
+    ok(hwnd2 == hwnd, "Failed to find test window.\n");
+
+    hwnd2 = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL);
+    ok(hwnd2 == NULL, "Unexpected find result %p.\n", hwnd2);
+
     ret = GetClassNameA(hwnd, buff, sizeof(buff));
     ok(ret, "Failed to get class name.\n");
     ok(!strcmp(buff, testclass), "Unexpected class name.\n");
@@ -1380,6 +1390,17 @@ static void test_actctx_classes(void)
 
     DestroyWindow(hwnd);
 
+    hwnd = CreateWindowExA(0, "4.3.2.1!MyTestClass", "test", 0, 0, 0, 0, 0, 0, 0, hinst, 0);
+    ok(hwnd != NULL, "Failed to create a window.\n");
+
+    hwnd2 = FindWindowExA(NULL, NULL, "MyTestClass", NULL);
+    ok(hwnd2 == hwnd, "Failed to find test window.\n");
+
+    hwnd2 = FindWindowExA(NULL, NULL, "4.3.2.1!MyTestClass", NULL);
+    ok(hwnd2 == NULL, "Unexpected find result %p.\n", hwnd2);
+
+    DestroyWindow(hwnd);
+
     ret = UnregisterClassA("MyTestClass", hinst);
     ok(!ret, "Unexpected ret value %d.\n", ret);
 
index 2dced09..b001512 100644 (file)
@@ -715,7 +715,7 @@ static void test_synthesized(void)
     r = CloseClipboard();
     ok(r, "gle %d\n", GetLastError());
 
-    for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
     {
         r = OpenClipboard(NULL);
         ok(r, "%u: gle %d\n", i, GetLastError());
@@ -2054,7 +2054,7 @@ static void test_string_data(void)
     char bufferA[12];
     WCHAR bufferW[12];
 
-    for (i = 0; i < sizeof(test_data) / sizeof(test_data[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(test_data); i++)
     {
         /* 1-byte Unicode strings crash on Win64 */
 #ifdef _WIN64
index e42875f..8ce1eb1 100644 (file)
@@ -1053,10 +1053,6 @@ static const DWORD biSize_tests[] = {
     0xffffffff
 };
 
-#ifndef __REACTOS__
-#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-#endif
-
 static void test_LoadImageBitmap(const char * test_desc, HBITMAP hbm)
 {
     BITMAP bm;
@@ -1207,6 +1203,126 @@ static void create_ico_file(const char *filename, const test_icon_entries_t *tes
     HeapFree(GetProcessHeap(), 0, buf);
 }
 
+static void create_bitmap_file(const char *filename, const BITMAPINFO *bmi, const unsigned char *bits)
+{
+    unsigned int clr_used, bmi_size, bits_size, stride;
+    const BITMAPINFOHEADER *h = &bmi->bmiHeader;
+    BITMAPFILEHEADER hdr;
+    DWORD bytes_written;
+    HANDLE file;
+    BOOL ret;
+
+    clr_used = h->biBitCount <= 8 ? 1u << h->biBitCount : 0;
+    stride = ((h->biBitCount * h->biWidth + 7) / 8 + 3) & ~3;
+    bits_size = h->biHeight * stride;
+    bmi_size = h->biSize + clr_used * sizeof(RGBQUAD);
+
+    hdr.bfType = 0x4d42;
+    hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + bmi_size;
+    hdr.bfSize = hdr.bfOffBits + bits_size;
+    hdr.bfReserved1 = 0;
+    hdr.bfReserved2 = 0;
+
+    file = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA failed, result %u.\n", GetLastError());
+    ret = WriteFile(file, &hdr, sizeof(hdr), &bytes_written, NULL);
+    ok(ret && bytes_written == sizeof(hdr), "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n",
+            ret, bytes_written);
+    ret = WriteFile(file, bmi, bmi_size, &bytes_written, NULL);
+    ok(ret && bytes_written == bmi_size, "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n",
+            ret, bytes_written);
+    ret = WriteFile(file, bits, bits_size, &bytes_written, NULL);
+    ok(ret && bytes_written == bits_size, "Unexpected WriteFile() result, ret %#x, bytes_written %u.\n",
+            ret, bytes_written);
+    CloseHandle(file);
+}
+
+static void test_LoadImage_working_directory_run(char *path)
+{
+    DWORD bytes_written;
+    HANDLE handle;
+    BOOL ret;
+    char path_icon[MAX_PATH];
+    char path_image[MAX_PATH];
+    static const test_icon_entries_t icon_desc = {32, 32};
+
+    sprintf(path_icon, "%s\\icon.ico", path);
+    sprintf(path_image,  "%s\\test.bmp", path);
+
+    /* Create Files */
+    create_ico_file(path_icon, &icon_desc, 1);
+
+    handle = CreateFileA(path_image, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+    ok(handle != INVALID_HANDLE_VALUE, "run %s: CreateFileA failed. %u\n", path, GetLastError());
+    ret = WriteFile(handle, bmpimage, sizeof(bmpimage), &bytes_written, NULL);
+    ok(ret && bytes_written == sizeof(bmpimage), "run %s: Test file created improperly.\n", path);
+    CloseHandle(handle);
+
+    /* Test cursor */
+    handle = LoadImageA(NULL, "icon.ico", IMAGE_CURSOR, 0, 0, LR_LOADFROMFILE);
+    ok(handle != NULL, "run %s: LoadImage() failed.\n", path);
+
+    ret = DestroyIcon(handle);
+    ok(ret, "run %s: DestroyIcon failed: %d\n", path, GetLastError());
+
+    /* Test image */
+    handle = LoadImageA(NULL, "test.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
+    ok(handle != NULL, "run %s: LoadImageA failed.\n", path);
+
+    ret = DeleteObject(handle);
+    ok(ret, "run %s: DeleteObject failed: %d\n", path, GetLastError());
+
+    /* Cleanup */
+    ret = DeleteFileA(path_image);
+    ok(ret, "run %s: DeleteFileA failed: %d\n", path, GetLastError());
+    ret = DeleteFileA(path_icon);
+    ok(ret, "run %s: DeleteFileA failed: %d\n", path, GetLastError());
+}
+
+static void test_LoadImage_working_directory(void)
+{
+    char old_working_dir[MAX_PATH];
+    char temp_dir_current[MAX_PATH];
+    char temp_dir_PATH[MAX_PATH];
+    char executable_path[MAX_PATH];
+    int pos_slash;
+    char old_PATH[10000];
+    char new_PATH[10000];
+    BOOL ret;
+
+    GetCurrentDirectoryA(ARRAY_SIZE(old_working_dir), old_working_dir);
+
+    GetTempPathA(ARRAY_SIZE(temp_dir_current), temp_dir_current);
+    strcat(temp_dir_current, "wine-test-dir-current\\");
+    GetTempPathA(ARRAY_SIZE(temp_dir_PATH), temp_dir_PATH);
+    strcat(temp_dir_PATH,    "wine-test-dir-path\\");
+
+    GetModuleFileNameA(NULL, executable_path, ARRAY_SIZE(executable_path));
+    pos_slash = strrchr(executable_path, '\\') - executable_path;
+    executable_path[pos_slash + 1] = 0;
+
+    CreateDirectoryA(temp_dir_current, NULL);
+    CreateDirectoryA(temp_dir_PATH, NULL);
+
+    SetCurrentDirectoryA(temp_dir_current);
+
+    GetEnvironmentVariableA("PATH", old_PATH, ARRAY_SIZE(old_PATH));
+    sprintf(new_PATH, "%s;%s", old_PATH, temp_dir_PATH);
+    SetEnvironmentVariableA("PATH", new_PATH);
+
+    test_LoadImage_working_directory_run(temp_dir_current);
+    test_LoadImage_working_directory_run(executable_path);
+    test_LoadImage_working_directory_run(temp_dir_PATH);
+
+    SetCurrentDirectoryA(old_working_dir);
+    SetEnvironmentVariableA("PATH", old_PATH);
+
+    ret = RemoveDirectoryA(temp_dir_current);
+    ok(ret, "RemoveDirectoryA failed: %d\n", GetLastError());
+    ret = RemoveDirectoryA(temp_dir_PATH);
+    ok(ret, "RemoveDirectoryA failed: %d\n", GetLastError());
+}
+
 static void test_LoadImage(void)
 {
     HANDLE handle;
@@ -1330,9 +1446,10 @@ static void test_LoadImage(void)
     bitmap_header->biSize = sizeof(BITMAPINFOHEADER);
 
     test_LoadImageFile("Cursor (invalid dwDIBOffset)", invalid_dwDIBOffset, sizeof(invalid_dwDIBOffset), "cur", 0);
-}
 
-#undef ARRAY_SIZE
+    /* Test in which paths images with a relative path can be found */
+    test_LoadImage_working_directory();
+}
 
 static void test_CreateIconFromResource(void)
 {
@@ -2539,7 +2656,7 @@ static void test_PrivateExtractIcons(void)
 
     static const test_icon_entries_t icon_desc[] = {{0,0,TRUE}, {16,16,TRUE}, {32,32}, {64,64,TRUE}};
 
-    create_ico_file("extract.ico", icon_desc, sizeof(icon_desc)/sizeof(*icon_desc));
+    create_ico_file("extract.ico", icon_desc, ARRAY_SIZE(icon_desc));
 
     ret = PrivateExtractIconsA("extract.ico", 0, 32, 32, &icon, NULL, 1, 0);
     ok(ret == 1, "PrivateExtractIconsA returned %u\n", ret);
@@ -2668,6 +2785,205 @@ static void test_monochrome_icon(void)
     HeapFree(GetProcessHeap(), 0, icon_data);
 }
 
+static COLORREF get_color_from_bits(const unsigned char *bits, const BITMAPINFO *bmi,
+        unsigned int row, unsigned int column)
+{
+    const BITMAPINFOHEADER *h = &bmi->bmiHeader;
+    unsigned int stride, shift, mask;
+    const unsigned char *data;
+    RGBQUAD color;
+    WORD color16;
+
+    stride = ((h->biBitCount * h->biWidth + 7) / 8 + 3) & ~3;
+    data = bits + row * stride + column * h->biBitCount / 8;
+    if (h->biBitCount >= 24)
+        return RGB(data[2], data[1], data[0]);
+
+    if (h->biBitCount == 16)
+    {
+        color16 = ((WORD)data[1] << 8) | data[0];
+        return RGB(((color16 >> 10) & 0x1f) << 3, ((color16 >> 5) & 0x1f) << 3,
+                (color16 & 0x1f) << 3);
+    }
+    shift = 8 - h->biBitCount - (column * h->biBitCount) % 8;
+    mask = ~(~0u << h->biBitCount);
+    color = bmi->bmiColors[(data[0] >> shift) & mask];
+    return RGB(color.rgbRed, color.rgbGreen, color.rgbBlue);
+}
+
+#define compare_bitmap_bits(a, b, c, d, e, f, g) compare_bitmap_bits_(__LINE__, a, b, c, d, e, f, g)
+static void compare_bitmap_bits_(unsigned int line, HDC hdc, HBITMAP bitmap, BITMAPINFO *bmi,
+        size_t result_bits_size, const unsigned char *expected_bits, unsigned int test_index, BOOL todo)
+{
+    unsigned char *result_bits;
+    unsigned int row, column;
+    int ret;
+
+    result_bits = HeapAlloc(GetProcessHeap(), 0, result_bits_size);
+    ret = GetDIBits(hdc, bitmap, 0, bmi->bmiHeader.biHeight,
+            result_bits, bmi, DIB_RGB_COLORS);
+    ok(ret == bmi->bmiHeader.biHeight, "Unexpected GetDIBits result %d, GetLastError() %u.\n",
+            ret, GetLastError());
+    for (row = 0; row < bmi->bmiHeader.biHeight; ++row)
+        for (column = 0; column < bmi->bmiHeader.biWidth; ++column)
+        {
+            COLORREF result, expected;
+
+            result = get_color_from_bits(result_bits, bmi, row, column);
+            expected = get_color_from_bits(expected_bits, bmi, row, column);
+
+            todo_wine_if(todo)
+            ok_(__FILE__, line)(result == expected, "Colors do not match, "
+                    "got 0x%06x, expected 0x%06x, test_index %u, row %u, column %u.\n",
+                    result, expected, test_index, row, column);
+        }
+    HeapFree(GetProcessHeap(), 0, result_bits);
+}
+
+static void test_Image_StretchMode(void)
+{
+    static const unsigned char test_bits_24[] =
+    {
+        0x00, 0xff, 0x00,  0x00, 0xff, 0x00,  0x00, 0xff, 0xff,  0x00, 0xff, 0x00,
+        0x00, 0xff, 0x00,  0xff, 0xff, 0x00,  0xff, 0xff, 0x00,  0x00, 0xff, 0x00,
+        0x00, 0xff, 0xff,  0x00, 0xff, 0x00,  0x00, 0xff, 0xff,  0x00, 0xff, 0x00,
+        0xff, 0xff, 0x00,  0x00, 0xff, 0xff,  0x00, 0xff, 0x00,  0x00, 0xff, 0x00,
+    };
+    static const unsigned char expected_bits_24[] =
+    {
+        0x3f, 0xff, 0x00,  0x3f, 0xff, 0x3f,  0x00, 0x00,
+        0x3f, 0xff, 0x7f,  0x00, 0xff, 0x3f,  0x00, 0x00,
+    };
+#define rgb16(r, g, b) ((WORD)(((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3)))
+    static const WORD test_bits_16[] =
+    {
+        rgb16(0x00, 0x20, 0x00), rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x40, 0xff), rgb16(0x00, 0x20, 0x00),
+        rgb16(0x00, 0x60, 0x00), rgb16(0xff, 0x80, 0x00), rgb16(0xff, 0x60, 0x00), rgb16(0x00, 0x80, 0x00),
+        rgb16(0x00, 0x20, 0xff), rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x40, 0xff), rgb16(0x00, 0x20, 0x00),
+        rgb16(0xff, 0x80, 0x00), rgb16(0x00, 0x60, 0xff), rgb16(0x00, 0x80, 0x00), rgb16(0x00, 0x60, 0x00),
+    };
+    static const WORD expected_bits_16[] =
+    {
+        rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x20, 0x00),
+        rgb16(0x00, 0x40, 0x00), rgb16(0x00, 0x20, 0x00),
+    };
+#undef rgb16
+    static const unsigned char test_bits_8[] =
+    {
+        0x00, 0xff, 0x00, 0xff,
+        0x00, 0x00, 0x00, 0x00,
+        0xff, 0x55, 0x00, 0xff,
+        0x00, 0xff, 0xff, 0x00,
+    };
+    static const unsigned char expected_bits_8[] =
+    {
+        0xff, 0xff, 0x00, 0x00,
+        0x55, 0xff, 0x00, 0x00,
+    };
+    static const unsigned char test_bits_1[] =
+    {
+        0x30, 0x0, 0x0, 0x0,
+        0x30, 0x0, 0x0, 0x0,
+        0x40, 0x0, 0x0, 0x0,
+        0xc0, 0x0, 0x0, 0x0,
+    };
+    static const unsigned char expected_bits_1[] =
+    {
+        0x40, 0x0, 0x0, 0x0,
+        0x0,  0x0, 0x0, 0x0,
+    };
+    static const RGBQUAD colors_bits_1[] =
+    {
+        {0, 0, 0},
+        {0xff, 0xff, 0xff},
+    };
+    static RGBQUAD colors_bits_8[256];
+
+    static const struct
+    {
+        LONG width, height, output_width, output_height;
+        WORD bit_count;
+        const unsigned char *test_bits, *expected_bits;
+        size_t test_bits_size, result_bits_size;
+        const RGBQUAD *bmi_colors;
+        size_t bmi_colors_size;
+        BOOL todo;
+    }
+    tests[] =
+    {
+        {4, 4, 2, 2, 24, test_bits_24, expected_bits_24,
+                sizeof(test_bits_24), sizeof(expected_bits_24), NULL, 0, TRUE},
+        {4, 4, 2, 2, 1, test_bits_1, expected_bits_1,
+                sizeof(test_bits_1), sizeof(expected_bits_1), colors_bits_1,
+                sizeof(colors_bits_1), FALSE},
+        {4, 4, 2, 2, 8, test_bits_8, expected_bits_8,
+                sizeof(test_bits_8), sizeof(expected_bits_8), colors_bits_8,
+                sizeof(colors_bits_8), FALSE},
+        {4, 4, 2, 2, 16, (const unsigned char *)test_bits_16, (const unsigned char *)expected_bits_16,
+                sizeof(test_bits_16), sizeof(expected_bits_16), NULL, 0, FALSE},
+    };
+    static const char filename[] = "test.bmp";
+    BITMAPINFO *bmi, *bmi_output;
+    HBITMAP bitmap, bitmap_copy;
+    unsigned int test_index;
+    unsigned char *bits;
+    size_t bmi_size;
+    unsigned int i;
+    HDC hdc;
+
+    bmi_size = sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
+    bmi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bmi_size);
+    bmi_output = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bmi_size);
+    bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+    bmi->bmiHeader.biPlanes = 1;
+    bmi->bmiHeader.biCompression = BI_RGB;
+
+    for (i = 0; i < 256; ++i)
+        colors_bits_8[i].rgbRed = colors_bits_8[i].rgbGreen = colors_bits_8[i].rgbBlue = i;
+
+    hdc = GetDC(NULL);
+
+    for (test_index = 0; test_index < ARRAY_SIZE(tests); ++test_index)
+    {
+        if (tests[test_index].bmi_colors)
+            memcpy(bmi->bmiColors, tests[test_index].bmi_colors, tests[test_index].bmi_colors_size);
+        else
+            memset(bmi->bmiColors, 0, 256 * sizeof(RGBQUAD));
+
+        bmi->bmiHeader.biWidth = tests[test_index].width;
+        bmi->bmiHeader.biHeight = tests[test_index].height;
+        bmi->bmiHeader.biBitCount = tests[test_index].bit_count;
+        memcpy(bmi_output, bmi, bmi_size);
+        bmi_output->bmiHeader.biWidth = tests[test_index].output_width;
+        bmi_output->bmiHeader.biHeight = tests[test_index].output_height;
+
+        bitmap = CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, (void **)&bits, NULL, 0);
+        ok(bitmap && bits, "CreateDIBSection() failed, result %u.\n", GetLastError());
+        memcpy(bits, tests[test_index].test_bits, tests[test_index].test_bits_size);
+
+        bitmap_copy = CopyImage(bitmap, IMAGE_BITMAP, tests[test_index].output_width,
+                tests[test_index].output_height, LR_CREATEDIBSECTION);
+        ok(!!bitmap_copy, "CopyImage() failed, result %u.\n", GetLastError());
+
+        compare_bitmap_bits(hdc, bitmap_copy, bmi_output, tests[test_index].result_bits_size,
+                tests[test_index].expected_bits, test_index, tests[test_index].todo);
+        DeleteObject(bitmap);
+        DeleteObject(bitmap_copy);
+
+        create_bitmap_file(filename, bmi, tests[test_index].test_bits);
+        bitmap = LoadImageA(NULL, filename, IMAGE_BITMAP, tests[test_index].output_width,
+                tests[test_index].output_height, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
+        ok(!!bitmap, "LoadImageA() failed, result %u.\n", GetLastError());
+        DeleteFileA(filename);
+        compare_bitmap_bits(hdc, bitmap, bmi_output, tests[test_index].result_bits_size,
+                tests[test_index].expected_bits, test_index, tests[test_index].todo);
+        DeleteObject(bitmap);
+    }
+    ReleaseDC(0, hdc);
+    HeapFree(GetProcessHeap(), 0, bmi_output);
+    HeapFree(GetProcessHeap(), 0, bmi);
+}
+
 START_TEST(cursoricon)
 {
     pGetCursorInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetCursorInfo" );
@@ -2695,6 +3011,7 @@ START_TEST(cursoricon)
     test_CopyImage_Bitmap(16);
     test_CopyImage_Bitmap(24);
     test_CopyImage_Bitmap(32);
+    test_Image_StretchMode();
     test_initial_cursor();
     test_CreateIcon();
     test_LoadImage();
index d63a0f6..eed2ac4 100644 (file)
@@ -2396,7 +2396,7 @@ static WCHAR test_cmd_w_to_w[][32] = {
     { 0x4efa, 0x4efc, 0x0061, 0x4efe, 0 },  /* some Chinese chars */
     { 0x0061, 0x0062, 0x0063, 0x9152, 0 },  /* Chinese with latin characters begin */
 };
-static const int nb_callbacks = 5 + sizeof(test_cmd_w_to_w)/sizeof(test_cmd_w_to_w[0]);
+static const int nb_callbacks = 5 + ARRAY_SIZE(test_cmd_w_to_w);
 
 static HDDEDATA CALLBACK server_end_to_end_callback(UINT uType, UINT uFmt, HCONV hconv,
                                                HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
@@ -2480,7 +2480,7 @@ static HDDEDATA CALLBACK server_end_to_end_callback(UINT uType, UINT uFmt, HCONV
         size_a = strlen(test_cmd_a_to_a) + 1;
         size_w = (lstrlenW(cmd_w) + 1) * sizeof(WCHAR);
         size_a_to_w = MultiByteToWideChar( CP_ACP, 0, test_cmd_a_to_a, -1, test_cmd_a_to_w,
-                                           sizeof(test_cmd_a_to_w)/sizeof(WCHAR) ) * sizeof(WCHAR);
+                                           ARRAY_SIZE(test_cmd_a_to_w)) * sizeof(WCHAR);
         size_w_to_a = WideCharToMultiByte( CP_ACP, 0, cmd_w, -1,
                                            test_cmd_w_to_a, sizeof(test_cmd_w_to_a), NULL, NULL );
         switch (str_index)
@@ -2528,7 +2528,7 @@ static HDDEDATA CALLBACK server_end_to_end_callback(UINT uType, UINT uFmt, HCONV
                 /* double A->W mapping */
                 /* NT uses the full size, XP+ only until the first null */
                 DWORD nt_size = MultiByteToWideChar( CP_ACP, 0, (char *)cmd_w, size_w, test_cmd_a_to_w,
-                                                     sizeof(test_cmd_a_to_w)/sizeof(WCHAR) ) * sizeof(WCHAR);
+                                                     ARRAY_SIZE(test_cmd_a_to_w)) * sizeof(WCHAR);
                 DWORD xp_size = MultiByteToWideChar( CP_ACP, 0, (char *)cmd_w, -1, NULL, 0 ) * sizeof(WCHAR);
                 ok(size == xp_size || broken(size == nt_size) ||
                    broken(str_index == 4 && IsDBCSLeadByte(cmd_w[0])) /* East Asian */,
@@ -2554,7 +2554,7 @@ static HDDEDATA CALLBACK server_end_to_end_callback(UINT uType, UINT uFmt, HCONV
             {
                 todo_wine ok(size == size_w, "Wrong size %d expected %d, msg_index=%d\n", size, size_w, msg_index);
                 MultiByteToWideChar(CP_ACP, 0, test_cmd_w_to_a, size_w, test_cmd_a_to_w,
-                                    sizeof(test_cmd_a_to_w)/sizeof(WCHAR));
+                                    ARRAY_SIZE(test_cmd_a_to_w));
                 todo_wine ok(!lstrcmpW((WCHAR*)buffer, cmd_w),
                              "Expected %s got %s, msg_index=%d\n", wine_dbgstr_w(cmd_w), wine_dbgstr_w((WCHAR *)buffer), msg_index);
             }
@@ -2562,7 +2562,7 @@ static HDDEDATA CALLBACK server_end_to_end_callback(UINT uType, UINT uFmt, HCONV
             {
                 todo_wine ok(size == size_w, "Wrong size %d expected %d, msg_index=%d\n", size, size_w, msg_index);
                 MultiByteToWideChar(CP_ACP, 0, test_cmd_w_to_a, size_w, test_cmd_a_to_w,
-                                    sizeof(test_cmd_a_to_w)/sizeof(WCHAR));
+                                    ARRAY_SIZE(test_cmd_a_to_w));
                 if (!is_cjk())
                     todo_wine ok(!lstrcmpW((WCHAR*)buffer, test_cmd_a_to_w), "Expected %s, got %s, msg_index=%d\n",
                                  wine_dbgstr_w(test_cmd_a_to_w), wine_dbgstr_w((WCHAR*)buffer), msg_index);
@@ -2663,7 +2663,7 @@ static void test_end_to_end_client(BOOL type_a)
     err = DdeGetLastError(client_pid);
     ok(err == DMLERR_NO_ERROR, "wrong dde error %x\n", err);
 
-    for (i = 0; i < sizeof(test_cmd_w_to_w)/sizeof(test_cmd_w_to_w[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(test_cmd_w_to_w); i++)
     {
         hdata = DdeClientTransaction((LPBYTE)test_cmd_w_to_w[i],
                                      (lstrlenW(test_cmd_w_to_w[i]) + 1) * sizeof(WCHAR),
index 679a762..3360b68 100644 (file)
@@ -42,6 +42,7 @@
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winnls.h"
 
 #define MAXHWNDS 1024
 static HWND hwnd [MAXHWNDS];
@@ -57,6 +58,7 @@ static LONG g_styleInitialFocusT1, g_styleInitialFocusT2;
 static BOOL g_bInitialFocusInitDlgResult, g_bReceivedCommand;
 
 static BOOL g_terminated;
+static BOOL g_button1Clicked;
 
 typedef struct {
     INT_PTR id;
@@ -149,6 +151,11 @@ static const h_entry hierarchy [] = {
     {0, 0, 0, 0}
 };
 
+static DWORD get_button_style(HWND button)
+{
+    return GetWindowLongW(button, GWL_STYLE) & BS_TYPEMASK;
+}
+
 static BOOL CreateWindows (HINSTANCE hinst)
 {
     const h_entry *p = hierarchy;
@@ -156,14 +163,14 @@ static BOOL CreateWindows (HINSTANCE hinst)
     while (p->id != 0)
     {
         DWORD style, exstyle;
-        char ctrlname[9];
+        char ctrlname[16];
 
         /* 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]))
+            if (p->id >=  ARRAY_SIZE(hwnd))
             {
                 trace ("Control %ld is out of range\n", p->id);
                 return FALSE;
@@ -476,6 +483,10 @@ static LRESULT CALLBACK main_window_procA (HWND hwnd, UINT uiMsg, WPARAM wParam,
                 g_terminated = TRUE;
                 return 0;
             }
+            else if ((wParam == 100 || wParam == 0xFFFF) && lParam)
+            {
+                g_button1Clicked = TRUE;
+            }
             break;
     }
 
@@ -630,76 +641,47 @@ static void test_WM_NEXTDLGCTL(void)
      * BS_DEFPUSHBUTTON with out making it default.
      */
 
-    /*
-     * Keep the focus on Edit control.
-     */
+    /* Keep the focus on Edit control. */
+    SetFocus(g_hwndTestDlgEdit);
+    ok((GetFocus() == g_hwndTestDlgEdit), "Focus didn't set on Edit control\n");
 
-    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");
+    /* 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 ( ((GetWindowLongA( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
-                        "Button1 style not set to BS_DEFPUSHBUTTON\n" );
-
-                ok ( !((GetWindowLongA( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
-                        "Button2's style not changed to BS_PUSHBUTTON\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");
 
-        /*
-         * 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 style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
+     * the style of default button changed to BS_PUSHBUTTON.
+     */
+    ok(get_button_style(g_hwndTestDlgBut1) == BS_DEFPUSHBUTTON, "Button1's style not set to BS_DEFPUSHBUTTON");
+    ok(get_button_style(g_hwndTestDlgBut2) == BS_PUSHBUTTON, "Button2's style not set to BS_PUSHBUTTON");
 
-        /*
-         * 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");
+    /* 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 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 ( ((GetWindowLongA( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
-                        "Button2 style not set to BS_DEFPUSHBUTTON\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");
 
-                ok ( !((GetWindowLongA( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
-                        "Button1's style not changed to BS_PUSHBUTTON\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.
+     */
+    ok(get_button_style(g_hwndTestDlgBut1) == BS_PUSHBUTTON, "Button1's style not set to BS_PUSHBUTTON");
+    ok(get_button_style(g_hwndTestDlgBut2) == BS_DEFPUSHBUTTON, "Button2's style not set to BS_DEFPUSHBUTTON");
 
-        /*
-         * 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");
+    /* 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");
-    }
+    /* 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");
 
     /* test nested default buttons */
 
@@ -812,6 +794,34 @@ static void test_IsDialogMessage(void)
     ok (!IsDialogMessageA(msg.hwnd, &msg), "expected failure\n");
 
     UnhookWindowsHookEx(hook);
+    DestroyWindow(g_hwndMain);
+
+    g_hwndMain = CreateWindowA("IsDialogMessageWindowClass", "IsDialogMessageWindowClass", WS_OVERLAPPEDWINDOW,
+                               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hinst, 0);
+    SetFocus(g_hwndButton1);
+    g_button1Clicked = FALSE;
+    FormEnterMsg(&msg, g_hwndButton1);
+    ok(IsDialogMessageA(g_hwndMain, &msg), "Did not handle the ENTER\n");
+    ok(g_button1Clicked, "Did not receive button 1 click notification\n");
+
+    g_button1Clicked = FALSE;
+    FormEnterMsg(&msg, g_hwndMain);
+    ok(IsDialogMessageA(g_hwndMain, &msg), "Did not handle the ENTER\n");
+    ok(g_button1Clicked, "Did not receive button 1 click notification\n");
+
+    g_button1Clicked = FALSE;
+    FormEnterMsg(&msg, g_hwndButton2);
+    ok(IsDialogMessageA(g_hwndMain, &msg), "Did not handle the ENTER\n");
+    ok(g_button1Clicked, "Did not receive button 1 click notification\n");
+
+    /* Button with id larger than 0xFFFF should also work */
+    g_button1Clicked = FALSE;
+    FormEnterMsg(&msg, g_hwndMain);
+    SetWindowLongPtrW(g_hwndButton1, GWLP_ID, 0x1FFFF);
+    ok(IsDialogMessageA(g_hwndMain, &msg), "Did not handle the ENTER\n");
+    ok(g_button1Clicked, "Did not receive button 1 click notification\n");
+
+    DestroyWindow(g_hwndMain);
 }
 
 
@@ -876,6 +886,56 @@ static INT_PTR CALLBACK focusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
     return FALSE;
 }
 
+static INT_PTR CALLBACK EmptyProcUserTemplate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg) {
+    case WM_INITDIALOG:
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static INT_PTR CALLBACK focusChildDlgWinProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
+        LPARAM lParam)
+{
+    static HWND hChildDlg;
+
+    switch (uiMsg)
+    {
+    case WM_INITDIALOG:
+    {
+        RECT rectHwnd;
+        struct  {
+            DLGTEMPLATE tmplate;
+            WORD menu,class,title;
+        } temp;
+
+        SetFocus( GetDlgItem(hwnd, 200) );
+
+        GetClientRect(hwnd,&rectHwnd);
+        temp.tmplate.style = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE | DS_CONTROL | DS_3DLOOK;
+        temp.tmplate.dwExtendedStyle = 0;
+        temp.tmplate.cdit = 0;
+        temp.tmplate.x = 0;
+        temp.tmplate.y = 0;
+        temp.tmplate.cx = 0;
+        temp.tmplate.cy = 0;
+        temp.menu = temp.class = temp.title = 0;
+
+        hChildDlg = CreateDialogIndirectParamA(g_hinst, &temp.tmplate,
+                  hwnd, (DLGPROC)EmptyProcUserTemplate, 0);
+        ok(hChildDlg != 0, "Failed to create test dialog.\n");
+
+        return FALSE;
+    }
+    case WM_CLOSE:
+        DestroyWindow(hChildDlg);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
 /* Helper for InitialFocusTest */
 static const char * GetHwndString(HWND hw)
 {
@@ -1062,6 +1122,29 @@ static void test_focus(void)
 
         DestroyWindow(hDlg);
     }
+
+    /* Test 6:
+     * Select textbox's text on creation when WM_INITDIALOG creates a child dialog. */
+    {
+        HWND hDlg;
+        HRSRC hResource;
+        HANDLE hTemplate;
+        DLGTEMPLATE* pTemplate;
+        HWND edit;
+
+        hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG_3", (LPCSTR)RT_DIALOG);
+        hTemplate = LoadResource(g_hinst, hResource);
+        pTemplate = LockResource(hTemplate);
+
+        hDlg = CreateDialogIndirectParamA(g_hinst, pTemplate, NULL, focusChildDlgWinProc, 0);
+        ok(hDlg != 0, "Failed to create test dialog.\n");
+        edit = GetDlgItem(hDlg, 200);
+
+        ok(GetFocus() == edit, "Focus not set to edit, focus=%p, dialog=%p, edit=%p\n",
+                GetFocus(), hDlg, edit);
+
+        DestroyWindow(hDlg);
+    }
 }
 
 static void test_GetDlgItemText(void)
@@ -1070,16 +1153,54 @@ static void test_GetDlgItemText(void)
     BOOL ret;
 
     strcpy(string, "Overwrite Me");
-    ret = GetDlgItemTextA(NULL, 0, string, sizeof(string)/sizeof(string[0]));
+    ret = GetDlgItemTextA(NULL, 0, string, ARRAY_SIZE(string));
     ok(!ret, "GetDlgItemText(NULL) shouldn't have succeeded\n");
 
     ok(string[0] == '\0' || broken(!strcmp(string, "Overwrite Me")),
        "string retrieved using GetDlgItemText should have been NULL terminated\n");
 }
 
+static INT_PTR CALLBACK getdlgitem_test_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    if (msg == WM_INITDIALOG)
+    {
+        char text[64];
+        LONG_PTR val;
+        HWND hwnd;
+        BOOL ret;
+
+        hwnd = GetDlgItem(hdlg, -1);
+        ok(hwnd != NULL, "Expected dialog item.\n");
+
+        *text = 0;
+        ret = GetDlgItemTextA(hdlg, -1, text, ARRAY_SIZE(text));
+        ok(ret && !strcmp(text, "Text1"), "Unexpected item text.\n");
+
+        val = GetWindowLongA(hwnd, GWLP_ID);
+        ok(val == -1, "Unexpected id.\n");
+
+        val = GetWindowLongPtrA(hwnd, GWLP_ID);
+        ok(val == -1, "Unexpected id %ld.\n", val);
+
+        hwnd = GetDlgItem(hdlg, -2);
+        ok(hwnd != NULL, "Expected dialog item.\n");
+
+        val = GetWindowLongA(hwnd, GWLP_ID);
+        ok(val == -2, "Unexpected id.\n");
+
+        val = GetWindowLongPtrA(hwnd, GWLP_ID);
+        ok(val == -2, "Unexpected id %ld.\n", val);
+
+        EndDialog(hdlg, 0xdead);
+    }
+
+    return FALSE;
+}
+
 static void test_GetDlgItem(void)
 {
     HWND hwnd, child1, child2, hwnd2;
+    INT_PTR retval;
     BOOL ret;
 
     hwnd = CreateWindowA("button", "parent", WS_VISIBLE, 0, 0, 100, 100, NULL, 0, g_hinst, NULL);
@@ -1126,6 +1247,9 @@ static void test_GetDlgItem(void)
     DestroyWindow(child1);
     DestroyWindow(child2);
     DestroyWindow(hwnd);
+
+    retval = DialogBoxParamA(g_hinst, "GETDLGITEM_TEST_DIALOG", NULL, getdlgitem_test_dialog_proc, 0);
+    ok(retval == 0xdead, "Unexpected return value.\n");
 }
 
 static INT_PTR CALLBACK DestroyDlgWinProc (HWND hDlg, UINT uiMsg,
@@ -1486,7 +1610,7 @@ static INT_PTR CALLBACK test_aw_conversion_dlgproc(HWND hdlg, UINT msg, WPARAM w
            (BYTE)buff[0], (BYTE)buff[1], len);
 
         memset(buffW, 0xff, sizeof(buffW));
-        len = GetWindowTextW(hdlg, buffW, sizeof(buffW)/sizeof(buffW[0]));
+        len = GetWindowTextW(hdlg, buffW, ARRAY_SIZE(buffW));
         ok(buffW[0] == 'W' && buffW[1] == 0xffff && len == 0, "Unexpected window text %#x, %#x, len %d\n",
             buffW[0], buffW[1], len);
 
@@ -1592,7 +1716,7 @@ static INT_PTR CALLBACK test_aw_conversion_dlgproc2(HWND hdlg, UINT msg, WPARAM
         ok(!strcmp(buff, testtext) && len == 0, "Unexpected window text %s, len %d\n", buff, len);
 
         memset(buffW, 0xff, sizeof(buffW));
-        len = GetWindowTextW(hdlg, buffW, sizeof(buffW)/sizeof(buffW[0]));
+        len = GetWindowTextW(hdlg, buffW, ARRAY_SIZE(buffW));
         ok(buffW[0] == 0 && buffW[1] == 0xffff && len == 0, "Unexpected window text %#x, %#x, len %d\n",
             buffW[0], buffW[1], len);
 
@@ -1869,6 +1993,187 @@ static void test_MessageBoxFontTest(void)
     DestroyWindow(hDlg);
 }
 
+static const char msgbox_title[] = "%5!z9ZXw*ia;57n/FGl.bCH,Su\"mfKN;foCqAU\'j6AmoJgAc_D:Z0A\'E6PF_O/w";
+static WCHAR expectedOK[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'O','K',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedOkCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'O','K',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedAbortRetryIgnore[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'A','b','o','r','t',' ',' ',' ','R','e','t','r','y',' ',' ',' ','I','g','n','o','r','e',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+
+static WCHAR expectedYesNo[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'Y','e','s',' ',' ',' ','N','o',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedYesNoCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'Y','e','s',' ',' ',' ','N','o',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedRetryCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'R','e','t','r','y',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedCancelTryContinue[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'C','a','n','c','e','l',' ',' ',' ','T','r','y',' ','A','g','a','i','n',' ',' ',' ','C','o','n','t','i','n','u','e',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+
+BOOL non_english = FALSE;
+
+DWORD WINAPI WorkerThread(void *param)
+{
+    WCHAR *expected = param;
+    char windowTitle[sizeof(msgbox_title)];
+    HWND hwndMbox;
+    BOOL succeeded = FALSE;
+
+    Sleep(200);
+
+    hwndMbox = GetForegroundWindow();
+
+    /* Find the Window, if it doesn't have focus */
+    if (!(IsWindow(hwndMbox) &&
+        GetWindowTextA(hwndMbox, windowTitle, sizeof(msgbox_title)) &&
+        lstrcmpA(msgbox_title, windowTitle) == 0))
+    {
+        hwndMbox = FindWindowA(NULL, msgbox_title);
+
+        if (!IsWindow(hwndMbox))
+            goto cleanup;
+    }
+
+    SendMessageA(hwndMbox, WM_COPY, 0, 0);
+
+    if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL))
+    {
+        HANDLE textHandle = GetClipboardData(CF_UNICODETEXT);
+        WCHAR *text = GlobalLock(textHandle);
+
+        if (text != NULL)
+        {
+            if(non_english)
+                ok(lstrlenW(text) > 0, "Empty string on clipboard\n");
+            else
+            {
+                succeeded = lstrcmpW(expected, text) == 0;
+                if(!succeeded)
+                {
+                    ok(0, "%s\n", wine_dbgstr_w(text));
+                    ok(0, "%s\n", wine_dbgstr_w(expected));
+                }
+            }
+
+            GlobalUnlock(textHandle);
+        }
+        else
+            ok(0, "No text on clipboard.\n");
+
+        CloseClipboard();
+
+    }
+    else
+        trace("Clipboard error\n");
+
+    PostMessageA(hwndMbox, WM_COMMAND, IDIGNORE, 0); /* For MB_ABORTRETRYIGNORE dialog. */
+    PostMessageA(hwndMbox, WM_CLOSE, 0, 0);
+
+cleanup:
+    ok(succeeded || non_english, "Failed to get string.\n");
+
+    return 0;
+}
+
+static void test_MessageBox_WM_COPY_Test(void)
+{
+    DWORD tid = 0;
+
+    non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
+    trace("non_english %d\n", non_english);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedOK, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_OK);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedOkCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_OKCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedAbortRetryIgnore, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_ABORTRETRYIGNORE);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedYesNo, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_YESNO);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedYesNoCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_YESNOCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedRetryCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_RETRYCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedCancelTryContinue, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_CANCELTRYCONTINUE);
+}
+
 static void test_SaveRestoreFocus(void)
 {
     HWND hDlg;
@@ -2178,4 +2483,5 @@ START_TEST(dialog)
     test_SaveRestoreFocus();
     test_timer_message();
     test_MessageBox();
+    test_MessageBox_WM_COPY_Test();
 }
index 01418bf..d0374c0 100644 (file)
@@ -1435,16 +1435,27 @@ static void test_edit_control_scroll(void)
     DestroyWindow (hwEdit);
 }
 
+static BOOL is_cjk_charset(HDC dc)
+{
+    switch (GdiGetCodePage(dc)) {
+    case 932: case 936: case 949: case 950: case 1361:
+        return TRUE;
+    default:
+        return FALSE;
+    }
+}
+
 static void test_margins_usefontinfo(UINT charset)
 {
     HWND hwnd;
     HDC hdc;
+    TEXTMETRICW tm;
     SIZE size;
-    BOOL cjk = FALSE;
     LOGFONTA lf;
     HFONT hfont;
     RECT rect;
-    INT margins, threshold, expect, empty_expect, small_expect;
+    INT margins, threshold, expect, empty_expect;
+    const UINT small_margins = MAKELONG(1, 5);
 
     memset(&lf, 0, sizeof(lf));
     lf.lfHeight = -11;
@@ -1463,40 +1474,31 @@ static void test_margins_usefontinfo(UINT charset)
 
     hdc = GetDC(hwnd);
     hfont = SelectObject(hdc, hfont);
-    size.cx = GdiGetCharDimensions( hdc, NULL, &size.cy );
-    expect = MAKELONG(size.cx / 2, size.cx / 2);
-    small_expect = 0;
-    empty_expect = size.cx >= 28 ? small_expect : expect;
-
-    charset = GetTextCharset(hdc);
-    switch (charset)
-    {
-    case SHIFTJIS_CHARSET:
-    case HANGUL_CHARSET:
-    case GB2312_CHARSET:
-    case CHINESEBIG5_CHARSET:
-        cjk = TRUE;
+    size.cx = GdiGetCharDimensions( hdc, &tm, &size.cy );
+    if ((charset != tm.tmCharSet && charset != DEFAULT_CHARSET) ||
+        !(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))) {
+        skip("%s for charset %d isn't available\n", lf.lfFaceName, charset);
+        hfont = SelectObject(hdc, hfont);
+        ReleaseDC(hwnd, hdc);
+        DestroyWindow(hwnd);
+        DeleteObject(hfont);
+        return;
     }
-
+    expect = MAKELONG(size.cx / 2, size.cx / 2);
     hfont = SelectObject(hdc, hfont);
     ReleaseDC(hwnd, hdc);
 
     margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
     ok(margins == 0, "got %x\n", margins);
     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
-    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
-    if (!cjk)
-        ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
-    else
-    {
-        ok(HIWORD(margins) > 0 && LOWORD(margins) > 0, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
-        expect = empty_expect = small_expect = margins;
-    }
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
+    expect = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
     DestroyWindow(hwnd);
 
-    threshold = (size.cx / 2 + size.cx) * 2;
+    threshold = HIWORD(expect) + LOWORD(expect) + size.cx * 2;
+    empty_expect = threshold > 80 ? small_margins : expect;
 
-    /* Size below which non-cjk margins are zero */
+    /* Size below the threshold, margins remain unchanged */
     hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold - 1, 100, NULL, NULL, NULL, NULL);
     ok(hwnd != NULL, "got %p\n", hwnd);
     GetClientRect(hwnd, &rect);
@@ -1506,11 +1508,13 @@ static void test_margins_usefontinfo(UINT charset)
     ok(margins == 0, "got %x\n", margins);
 
     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
     margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
-    ok(margins == small_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
+    ok(margins == small_margins, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
     DestroyWindow(hwnd);
 
-    /* Size at which non-cjk margins become non-zero */
+    /* Size at the threshold, margins become non-zero */
     hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold, 100, NULL, NULL, NULL, NULL);
     ok(hwnd != NULL, "got %p\n", hwnd);
     GetClientRect(hwnd, &rect);
@@ -1520,6 +1524,8 @@ static void test_margins_usefontinfo(UINT charset)
     ok(margins == 0, "got %x\n", margins);
 
     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
     margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
     ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
     DestroyWindow(hwnd);
@@ -1534,6 +1540,8 @@ static void test_margins_usefontinfo(UINT charset)
     ok(margins == 0, "got %x\n", margins);
 
     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
     margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
     ok(margins == empty_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins));
     DestroyWindow(hwnd);
@@ -1541,6 +1549,180 @@ static void test_margins_usefontinfo(UINT charset)
     DeleteObject(hfont);
 }
 
+static BOOL is_cjk_font(HDC dc)
+{
+    const DWORD FS_DBCS_MASK = FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB;
+    FONTSIGNATURE fs;
+    return (GetTextCharsetInfo(dc, &fs, 0) != DEFAULT_CHARSET &&
+            (fs.fsCsb[0] & FS_DBCS_MASK));
+}
+
+static INT get_cjk_fontinfo_margin(INT width, INT side_bearing)
+{
+    INT margin;
+    if (side_bearing < 0)
+        margin = min(-side_bearing, width/2);
+    else
+        margin = 0;
+    return margin;
+}
+
+static DWORD get_cjk_font_margins(HDC hdc, BOOL unicode)
+{
+    ABC abc[256];
+    SHORT left, right;
+    UINT i;
+
+    left = right = 0;
+    if (unicode) {
+        if (!GetCharABCWidthsW(hdc, 0, 255, abc))
+            return 0;
+    }
+    else {
+        if (!GetCharABCWidthsA(hdc, 0, 255, abc))
+            return 0;
+    }
+    for (i = 0; i < ARRAY_SIZE(abc); i++) {
+        if (-abc[i].abcA > right) right = -abc[i].abcA;
+        if (-abc[i].abcC > left)  left  = -abc[i].abcC;
+    }
+    return MAKELONG(left, right);
+}
+
+static void test_margins_default(const char* facename, UINT charset)
+{
+    HWND hwnd;
+    HDC hdc;
+    TEXTMETRICW tm;
+    SIZE size;
+    BOOL cjk_charset, cjk_font;
+    LOGFONTA lf;
+    HFONT hfont;
+    RECT rect;
+    INT margins, expect, font_expect;
+    const UINT small_margins = MAKELONG(1, 5);
+    const WCHAR EditW[] = {'E','d','i','t',0}, strW[] = {'W',0};
+    struct char_width_info {
+        INT lsb, rsb, unknown;
+    } info;
+    HMODULE hgdi32;
+    BOOL (WINAPI *pGetCharWidthInfo)(HDC, struct char_width_info *);
+
+    hgdi32 = GetModuleHandleA("gdi32.dll");
+    pGetCharWidthInfo = (void *)GetProcAddress(hgdi32, "GetCharWidthInfo");
+
+    memset(&lf, 0, sizeof(lf));
+    lf.lfHeight = -11;
+    lf.lfWeight = FW_NORMAL;
+    lf.lfCharSet = charset;
+    strcpy(lf.lfFaceName, facename);
+
+    hfont = CreateFontIndirectA(&lf);
+    ok(hfont != NULL, "got %p\n", hfont);
+
+    /* Unicode version */
+    hwnd = CreateWindowExW(0, EditW, strW, WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    hdc = GetDC(hwnd);
+    hfont = SelectObject(hdc, hfont);
+    size.cx = GdiGetCharDimensions( hdc, &tm, &size.cy );
+    if ((charset != tm.tmCharSet && charset != DEFAULT_CHARSET) ||
+        !(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))) {
+        skip("%s for charset %d isn't available\n", lf.lfFaceName, charset);
+        hfont = SelectObject(hdc, hfont);
+        ReleaseDC(hwnd, hdc);
+        DestroyWindow(hwnd);
+        DeleteObject(hfont);
+        return;
+    }
+    cjk_charset = is_cjk_charset(hdc);
+    cjk_font = is_cjk_font(hdc);
+    if ((cjk_charset || cjk_font) &&
+        pGetCharWidthInfo && pGetCharWidthInfo(hdc, &info)) {
+        short left, right;
+
+        left  = get_cjk_fontinfo_margin(size.cx, info.lsb);
+        right = get_cjk_fontinfo_margin(size.cx, info.rsb);
+        expect = MAKELONG(left, right);
+
+        font_expect = get_cjk_font_margins(hdc, TRUE);
+        if (!font_expect)
+            /* In this case, margins aren't updated */
+            font_expect = small_margins;
+    }
+    else
+        font_expect = expect = MAKELONG(size.cx / 2, size.cx / 2);
+
+    hfont = SelectObject(hdc, hfont);
+    ReleaseDC(hwnd, hdc);
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == font_expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(font_expect), LOWORD(font_expect), HIWORD(margins), LOWORD(margins));
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(expect), LOWORD(expect), HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    /* ANSI version */
+    hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "got %p\n", hwnd);
+    GetClientRect(hwnd, &rect);
+    ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect));
+
+    if (cjk_charset) {
+        hdc = GetDC(hwnd);
+        hfont = SelectObject(hdc, hfont);
+        font_expect = get_cjk_font_margins(hdc, FALSE);
+        if (!font_expect)
+            /* In this case, margins aren't updated */
+            font_expect = small_margins;
+        hfont = SelectObject(hdc, hfont);
+        ReleaseDC(hwnd, hdc);
+    }
+    else
+        /* we expect EC_USEFONTINFO size */
+        font_expect = expect;
+
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == 0, "got %x\n", margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == font_expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(font_expect), LOWORD(font_expect), HIWORD(margins), LOWORD(margins));
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins);
+    SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO));
+    margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0);
+    ok(margins == expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(expect), LOWORD(expect), HIWORD(margins), LOWORD(margins));
+    DestroyWindow(hwnd);
+
+    DeleteObject(hfont);
+}
+
+static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
+{
+    return 0;
+}
+
+static BOOL is_font_installed(const char*name)
+{
+    HDC hdc = GetDC(NULL);
+    BOOL ret = FALSE;
+
+    if (!EnumFontFamiliesA(hdc, name, find_font_proc, 0))
+        ret = TRUE;
+
+    ReleaseDC(NULL, hdc);
+    return ret;
+}
+
 static void test_margins(void)
 {
     HWND hwEdit;
@@ -1615,11 +1797,33 @@ static void test_margins(void)
        but not by < Win 8 and Win 10. */
 
     test_margins_usefontinfo(DEFAULT_CHARSET);
-}
 
-static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam)
-{
-    return 0;
+    test_margins_default("Tahoma", ANSI_CHARSET);
+    test_margins_default("Tahoma", EASTEUROPE_CHARSET);
+
+    test_margins_default("Tahoma", HANGUL_CHARSET);
+    test_margins_default("Tahoma", CHINESEBIG5_CHARSET);
+
+    if (is_font_installed("MS PGothic")) {
+        test_margins_default("MS PGothic", SHIFTJIS_CHARSET);
+        test_margins_default("MS PGothic", GREEK_CHARSET);
+    }
+    else
+        skip("MS PGothic is not available, skipping some margin tests\n");
+
+    if (is_font_installed("Ume P Gothic")) {
+        test_margins_default("Ume P Gothic", SHIFTJIS_CHARSET);
+        test_margins_default("Ume P Gothic", GREEK_CHARSET);
+    }
+    else
+        skip("Ume P Gothic is not available, skipping some margin tests\n");
+
+    if (is_font_installed("SimSun")) {
+        test_margins_default("SimSun", GB2312_CHARSET);
+        test_margins_default("SimSun", ANSI_CHARSET);
+    }
+    else
+        skip("SimSun is not available, skipping some margin tests\n");
 }
 
 static void test_margins_font_change(void)
@@ -1628,15 +1832,12 @@ static void test_margins_font_change(void)
     DWORD margins, font_margins;
     LOGFONTA lf;
     HFONT hfont, hfont2;
-    HDC hdc = GetDC(0);
 
-    if(EnumFontFamiliesA(hdc, "Arial", find_font_proc, 0))
+    if (!is_font_installed("Arial"))
     {
-        trace("Arial not found - skipping font change margin tests\n");
-        ReleaseDC(0, hdc);
+        skip("Arial not found - skipping font change margin tests\n");
         return;
     }
-    ReleaseDC(0, hdc);
 
     hwEdit = create_child_editcontrol(0, 0);
 
@@ -1645,7 +1846,7 @@ static void test_margins_font_change(void)
     memset(&lf, 0, sizeof(lf));
     strcpy(lf.lfFaceName, "Arial");
     lf.lfHeight = 16;
-    lf.lfCharSet = DEFAULT_CHARSET;
+    lf.lfCharSet = GREEK_CHARSET; /* to avoid associated charset feature */
     hfont = CreateFontIndirectA(&lf);
     lf.lfHeight = 30;
     hfont2 = CreateFontIndirectA(&lf);
@@ -1660,39 +1861,39 @@ static void test_margins_font_change(void)
     SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0,0));
     SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) == 0 || broken(LOWORD(margins) == LOWORD(font_margins)), /* win95 */
+    ok(LOWORD(margins) == 0,
        "got %d\n", LOWORD(margins));
-    ok(HIWORD(margins) == 0 || broken(HIWORD(margins) == HIWORD(font_margins)), /* win95 */
+    ok(HIWORD(margins) == 0,
        "got %d\n", HIWORD(margins));
 
     SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,0));
     SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) == 1 || broken(LOWORD(margins) == LOWORD(font_margins)), /* win95 */
+    ok(LOWORD(margins) == 1,
        "got %d\n", LOWORD(margins));
-    ok(HIWORD(margins) == 0 || broken(HIWORD(margins) == HIWORD(font_margins)), /* win95 */
+    ok(HIWORD(margins) == 0,
        "got %d\n", HIWORD(margins));
 
     SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,1));
     SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0);
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) == 1 || broken(LOWORD(margins) == LOWORD(font_margins)), /* win95 */
+    ok(LOWORD(margins) == 1,
        "got %d\n", LOWORD(margins));
-    ok(HIWORD(margins) == 1 || broken(HIWORD(margins) == HIWORD(font_margins)), /* win95 */
+    ok(HIWORD(margins) == 1,
        "got %d\n", HIWORD(margins));
 
     SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO,EC_USEFONTINFO));
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) == 1 || broken(LOWORD(margins) == LOWORD(font_margins)), /* win95 */
+    ok(LOWORD(margins) == 1,
        "got %d\n", LOWORD(margins));
-    ok(HIWORD(margins) == 1 || broken(HIWORD(margins) == HIWORD(font_margins)), /* win95 */
+    ok(HIWORD(margins) == 1,
        "got %d\n", HIWORD(margins));
 
     SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0);
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) == 1 || broken(LOWORD(margins) != 1 && LOWORD(margins) != LOWORD(font_margins)), /* win95 */
+    ok(LOWORD(margins) == 1,
        "got %d\n", LOWORD(margins));
-    ok(HIWORD(margins) == 1 || broken(HIWORD(margins) != 1 && HIWORD(margins) != HIWORD(font_margins)), /* win95 */
+    ok(HIWORD(margins) == 1,
        "got %d\n", HIWORD(margins));
 
     /* Above a certain size threshold then the margin is updated */
@@ -1716,7 +1917,7 @@ static void test_margins_font_change(void)
     ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins)); 
     SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0);
     margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0);
-    ok(LOWORD(margins) != LOWORD(font_margins) || broken(LOWORD(margins) == LOWORD(font_margins)), /* win98 */
+    ok(LOWORD(margins) != LOWORD(font_margins),
        "got %d\n", LOWORD(margins));
     ok(HIWORD(margins) != HIWORD(font_margins), "got %d\n", HIWORD(margins)); 
 
@@ -2956,7 +3157,7 @@ static void test_EM_GETLINE(void)
     hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
     hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
 
-    for (i = 0; i < sizeof(hwnd)/sizeof(hwnd[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(hwnd); i++)
     {
         static const WCHAR strW[] = {'t','e','x','t',0};
         static const char *str = "text";
@@ -2984,13 +3185,13 @@ static void test_EM_GETLINE(void)
         ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff);
 
         memset(buffW, 0, sizeof(buffW));
-        *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]);
+        *(WORD *)buffW = ARRAY_SIZE(buffW);
         r = SendMessageW(hwnd[i], EM_GETLINE, 0, (LPARAM)buffW);
         ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r);
         ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW));
 
         memset(buffW, 0, sizeof(buffW));
-        *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]);
+        *(WORD *)buffW = ARRAY_SIZE(buffW);
         r = SendMessageW(hwnd[i], EM_GETLINE, 1, (LPARAM)buffW);
         ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r);
         ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW));
index 37fe0ce..d856e69 100644 (file)
@@ -79,13 +79,17 @@ static struct {
     LONG last_hook_up;
     LONG last_hook_syskey_down;
     LONG last_hook_syskey_up;
+    WORD vk;
     BOOL expect_alt;
     BOOL sendinput_broken;
 } key_status;
 
-static UINT (WINAPI *pSendInput) (UINT, INPUT*, size_t);
+static BOOL (WINAPI *pGetCurrentInputMessageSource)( INPUT_MESSAGE_SOURCE *source );
+static BOOL (WINAPI *pGetPointerType)(UINT32, POINTER_INPUT_TYPE*);
 static int (WINAPI *pGetMouseMovePointsEx) (UINT, LPMOUSEMOVEPOINT, LPMOUSEMOVEPOINT, int, DWORD);
 static UINT (WINAPI *pGetRawInputDeviceList) (PRAWINPUTDEVICELIST, PUINT, UINT);
+static UINT (WINAPI *pGetRawInputDeviceInfoW) (HANDLE, UINT, void *, UINT *);
+static UINT (WINAPI *pGetRawInputDeviceInfoA) (HANDLE, UINT, void *, UINT *);
 static int  (WINAPI *pGetWindowRgnBox)(HWND, LPRECT);
 
 #define MAXKEYEVENTS 12
@@ -120,15 +124,6 @@ typedef struct
     } 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 = GETFLAGS[ 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;
@@ -164,15 +159,16 @@ static void init_function_pointers(void)
     HMODULE hdll = GetModuleHandleA("user32");
 
 #define GET_PROC(func) \
-    p ## func = (void*)GetProcAddress(hdll, #func); \
-    if(!p ## func) \
-      trace("GetProcAddress(%s) failed\n", #func);
-
-    GET_PROC(SendInput)
-    GET_PROC(GetMouseMovePointsEx)
-    GET_PROC(GetRawInputDeviceList)
-    GET_PROC(GetWindowRgnBox)
-
+    if (!(p ## func = (void*)GetProcAddress(hdll, #func))) \
+      trace("GetProcAddress(%s) failed\n", #func)
+
+    GET_PROC(GetCurrentInputMessageSource);
+    GET_PROC(GetMouseMovePointsEx);
+    GET_PROC(GetPointerType);
+    GET_PROC(GetRawInputDeviceList);
+    GET_PROC(GetRawInputDeviceInfoW);
+    GET_PROC(GetRawInputDeviceInfoA);
+    GET_PROC(GetWindowRgnBox);
 #undef GET_PROC
 }
 
@@ -232,17 +228,25 @@ static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam )
  */
 static BOOL do_test( HWND hwnd, int seqnr, const KEV td[] )
 {
-    INPUT inputs[MAXKEYEVENTS];
+    TEST_INPUT inputs[MAXKEYEVENTS];
     KMSG expmsg[MAXKEYEVENTS];
     MSG msg;
     char buf[100];
-    UINT evtctr=0;
+    UINT evtctr=0, ret;
     int kmctr, i;
 
     buf[0]='\0';
     TrackSysKey=0; /* see input.c */
-    for( i = 0; i < MAXKEYEVENTS; i++) {
-        ADDTOINPUTS(td[i])
+    for (i = 0; i < MAXKEYEVENTS; i++)
+    {
+        inputs[evtctr].type = INPUT_KEYBOARD;
+        inputs[evtctr].u.ki.wVk = GETVKEY[td[i]];
+        inputs[evtctr].u.ki.wScan = GETSCAN[td[i]];
+        inputs[evtctr].u.ki.dwFlags = GETFLAGS[td[i]];
+        inputs[evtctr].u.ki.dwExtraInfo = 0;
+        inputs[evtctr].u.ki.time = ++timetag;
+        if (td[i]) evtctr++;
+
         strcat(buf, getdesc[td[i]]);
         if(td[i])
             expmsg[i].message = KbdMessage(td[i], &(expmsg[i].wParam), &(expmsg[i].lParam));
@@ -252,8 +256,8 @@ static BOOL do_test( HWND hwnd, int seqnr, const KEV td[] )
     for( kmctr = 0; kmctr < MAXKEYEVENTS && expmsg[kmctr].message; kmctr++)
         ;
     ok( evtctr <= MAXKEYEVENTS, "evtctr is above MAXKEYEVENTS\n" );
-    if( evtctr != pSendInput(evtctr, &inputs[0], sizeof(INPUT)))
-       ok (FALSE, "SendInput failed to send some events\n");
+    ret = SendInput(evtctr, (INPUT *)inputs, sizeof(INPUT));
+    ok(ret == evtctr, "SendInput failed to send some events\n");
     i = 0;
     if (winetest_debug > 1)
         trace("======== key stroke sequence #%d: %s =============\n",
@@ -700,14 +704,17 @@ static struct message sent_messages[MAXKEYMESSAGES];
 static UINT sent_messages_cnt;
 
 /* Verify that only specified key state transitions occur */
-static void compare_and_check(int id, BYTE *ks1, BYTE *ks2, const struct sendinput_test_s *test)
+static void compare_and_check(int id, BYTE *ks1, BYTE *ks2,
+    const struct sendinput_test_s *test, BOOL foreground)
 {
     int i, failcount = 0;
     const struct transition_s *t = test->expected_transitions;
     UINT actual_cnt = 0;
     const struct message *expected = test->expected_messages;
 
-    while (t->wVk) {
+    while (t->wVk && foreground) {
+        /* We won't receive any information from GetKeyboardState() if we're
+         * not the foreground window. */
         BOOL matched = ((ks1[t->wVk]&0x80) == (t->before_state&0x80)
                        && (ks2[t->wVk]&0x80) == (~t->before_state&0x80));
 
@@ -790,6 +797,13 @@ static void compare_and_check(int id, BYTE *ks1, BYTE *ks2, const struct sendinp
             expected++;
             continue;
         }
+        else if (!(expected->flags & hook) && !foreground)
+        {
+            /* If we weren't able to receive foreground status, we won't get
+             * any window messages. */
+            expected++;
+            continue;
+        }
         /* NT4 doesn't send SYSKEYDOWN/UP to hooks, only KEYDOWN/UP */
         else if ((expected->flags & hook) &&
                  (expected->message == WM_SYSKEYDOWN || expected->message == WM_SYSKEYUP) &&
@@ -826,7 +840,7 @@ static void compare_and_check(int id, BYTE *ks1, BYTE *ks2, const struct sendinp
         expected++;
     }
     /* skip all optional trailing messages */
-    while (expected->message && (expected->flags & optional))
+    while (expected->message && ((expected->flags & optional) || (!(expected->flags & hook) && !foreground)))
         expected++;
 
 
@@ -857,16 +871,7 @@ static LRESULT CALLBACK WndProc2(HWND hWnd, UINT Msg, WPARAM wParam,
 {
     if (winetest_debug > 1) trace("MSG:  %8x W:%8lx L:%8lx\n", Msg, wParam, lParam);
 
-    if (Msg != WM_PAINT &&
-        Msg != WM_NCPAINT &&
-        Msg != WM_SYNCPAINT &&
-        Msg != WM_ERASEBKGND &&
-        Msg != WM_NCHITTEST &&
-        Msg != WM_GETTEXT &&
-        Msg != WM_GETICON &&
-        Msg != WM_IME_SELECT &&
-        Msg != WM_DEVICECHANGE &&
-        Msg != WM_TIMECHANGE)
+    if ((Msg >= WM_KEYFIRST && Msg <= WM_KEYLAST) || Msg == WM_SYSCOMMAND)
     {
         ok(sent_messages_cnt < MAXKEYMESSAGES, "Too many messages\n");
         if (sent_messages_cnt < MAXKEYMESSAGES)
@@ -915,6 +920,7 @@ static void test_Input_blackbox(void)
     int ii;
     BYTE ks1[256], ks2[256];
     LONG_PTR prevWndProc;
+    BOOL foreground;
     HWND window;
     HHOOK hook;
 
@@ -928,7 +934,9 @@ static void test_Input_blackbox(void)
         NULL, NULL);
     ok(window != NULL, "error: %d\n", (int) GetLastError());
     SetWindowPos( window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE );
-    SetForegroundWindow( window );
+    foreground = SetForegroundWindow( window );
+    if (!foreground)
+        skip("Failed to set foreground window; some tests will be skipped.\n");
 
     if (!(hook = SetWindowsHookExA(WH_KEYBOARD_LL, hook_proc, GetModuleHandleA( NULL ), 0)))
     {
@@ -948,24 +956,15 @@ static void test_Input_blackbox(void)
     i.u.ki.time = 0;
     i.u.ki.dwExtraInfo = 0;
 
-    for (ii = 0; ii < sizeof(sendinput_test)/sizeof(struct sendinput_test_s)-1;
-         ii++) {
+    for (ii = 0; ii < ARRAY_SIZE(sendinput_test)-1; ii++) {
         GetKeyboardState(ks1);
         i.u.ki.wScan = ii+1 /* useful for debugging */;
         i.u.ki.dwFlags = sendinput_test[ii].dwFlags;
         i.u.ki.wVk = sendinput_test[ii].wVk;
-        pSendInput(1, (INPUT*)&i, sizeof(TEST_INPUT));
+        SendInput(1, (INPUT*)&i, sizeof(TEST_INPUT));
         empty_message_queue();
         GetKeyboardState(ks2);
-        if (!ii && sent_messages_cnt <= 1 && !memcmp( ks1, ks2, sizeof(ks1) ))
-        {
-            win_skip( "window doesn't receive the queued input\n" );
-            /* release the key */
-            i.u.ki.dwFlags |= KEYEVENTF_KEYUP;
-            pSendInput(1, (INPUT*)&i, sizeof(TEST_INPUT));
-            break;
-        }
-        compare_and_check(ii, ks1, ks2, &sendinput_test[ii]);
+        compare_and_check(ii, ks1, ks2, &sendinput_test[ii], foreground);
     }
 
     empty_message_queue();
@@ -973,7 +972,7 @@ static void test_Input_blackbox(void)
     UnhookWindowsHookEx(hook);
 }
 
-static void reset_key_status(void)
+static void reset_key_status(WORD vk)
 {
     key_status.last_key_down = -1;
     key_status.last_key_up = -1;
@@ -985,6 +984,7 @@ static void reset_key_status(void)
     key_status.last_hook_up = -1;
     key_status.last_hook_syskey_down = -1;
     key_status.last_hook_syskey_up = -1;
+    key_status.vk = vk;
     key_status.expect_alt = FALSE;
     key_status.sendinput_broken = FALSE;
 }
@@ -1004,8 +1004,8 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
     inputs[0].u.ki.wScan = 0x3c0;
     inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
 
-    reset_key_status();
-    pSendInput(1, (INPUT*)inputs, sizeof(INPUT));
+    reset_key_status(VK_PACKET);
+    SendInput(1, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_KEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
@@ -1026,8 +1026,8 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
     inputs[1].u.ki.wScan = 0x3c0;
     inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
 
-    reset_key_status();
-    pSendInput(1, (INPUT*)(inputs+1), sizeof(INPUT));
+    reset_key_status(VK_PACKET);
+    SendInput(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);
@@ -1051,9 +1051,9 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
     inputs[1].u.ki.wScan = 0x3041;
     inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE;
 
-    reset_key_status();
+    reset_key_status(VK_PACKET);
     key_status.expect_alt = TRUE;
-    pSendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
@@ -1078,9 +1078,9 @@ static void test_unicode_keys(HWND hwnd, HHOOK hook)
     inputs[0].u.ki.wScan = 0;
     inputs[0].u.ki.dwFlags = KEYEVENTF_KEYUP;
 
-    reset_key_status();
+    reset_key_status(VK_PACKET);
     key_status.expect_alt = TRUE;
-    pSendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
     while(PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)){
         if(msg.message == WM_SYSKEYDOWN && msg.wParam == VK_PACKET){
             TranslateMessage(&msg);
@@ -1094,6 +1094,32 @@ static void test_unicode_keys(HWND hwnd, HHOOK 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);
     }
+
+    /* Press and release, non-zero key code. */
+    inputs[0].u.ki.wVk = 0x51;
+    inputs[0].u.ki.wScan = 0x123;
+    inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
+
+    inputs[1].u.ki.wVk = 0x51;
+    inputs[1].u.ki.wScan = 0x123;
+    inputs[1].u.ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
+
+    reset_key_status(inputs[0].u.ki.wVk);
+    SendInput(2, (INPUT*)inputs, sizeof(INPUT));
+    while (PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE))
+    {
+        TranslateMessage(&msg);
+        DispatchMessageW(&msg);
+    }
+
+    if (!key_status.sendinput_broken)
+    {
+        ok(key_status.last_key_down == 0x51, "Unexpected key down %#x.\n", key_status.last_key_down);
+        ok(key_status.last_key_up == 0x51, "Unexpected key up %#x.\n", key_status.last_key_up);
+        if (hook)
+            todo_wine
+            ok(key_status.last_hook_up == 0x23, "Unexpected hook message %#x.\n", key_status.last_hook_up);
+    }
 }
 
 static LRESULT CALLBACK unicode_wnd_proc( HWND hWnd, UINT msg, WPARAM wParam,
@@ -1134,7 +1160,8 @@ static LRESULT CALLBACK llkbd_unicode_hook(int nCode, WPARAM wParam, LPARAM lPar
                 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);
+            todo_wine_if(key_status.vk != VK_PACKET)
+                ok(info->vkCode == key_status.vk, "Unexpected vkCode %#x, expected %#x.\n", info->vkCode, key_status.vk);
         }
         switch(wParam){
         case WM_KEYDOWN:
@@ -1569,7 +1596,7 @@ static void test_GetMouseMovePointsEx(void)
 static void test_GetRawInputDeviceList(void)
 {
     RAWINPUTDEVICELIST devices[32];
-    UINT ret, oret, devcount, odevcount;
+    UINT ret, oret, devcount, odevcount, i;
     DWORD err;
 
     SetLastError(0xdeadbeef);
@@ -1601,18 +1628,84 @@ static void test_GetRawInputDeviceList(void)
     ret = pGetRawInputDeviceList(devices, &devcount, sizeof(devices[0]));
     ok(ret > 0, "expected non-zero\n");
 
+    for(i = 0; i < devcount; ++i)
+    {
+        WCHAR name[128];
+        char nameA[128];
+        UINT sz, len;
+        RID_DEVICE_INFO info;
+        HANDLE file;
+
+        /* get required buffer size */
+        name[0] = '\0';
+        sz = 5;
+        ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz);
+        ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %d\n", err);
+        ok(sz > 5 && sz < ARRAY_SIZE(name), "Size should have been set and not too large (got: %u)\n", sz);
+
+        /* buffer size for RIDI_DEVICENAME is in CHARs, not BYTEs */
+        ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICENAME, name, &sz);
+        ok(ret == sz, "GetRawInputDeviceInfo gave wrong return: %d\n", err);
+        len = lstrlenW(name);
+        ok(len + 1 == ret, "GetRawInputDeviceInfo returned wrong length (name: %u, ret: %u)\n", len + 1, ret);
+
+        /* test A variant with same size */
+        ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICENAME, nameA, &sz);
+        ok(ret == sz, "GetRawInputDeviceInfoA gave wrong return: %d\n", err);
+        len = strlen(nameA);
+        ok(len + 1 == ret, "GetRawInputDeviceInfoA returned wrong length (name: %u, ret: %u)\n", len + 1, ret);
+
+        /* buffer size for RIDI_DEVICEINFO is in BYTEs */
+        memset(&info, 0, sizeof(info));
+        info.cbSize = sizeof(info);
+        sz = sizeof(info) - 1;
+        ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
+        ok(ret == -1, "GetRawInputDeviceInfo gave wrong failure: %d\n", err);
+        ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
+
+        ret = pGetRawInputDeviceInfoW(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
+        ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %d\n", err);
+        ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
+        ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%x\n", info.dwType);
+
+        memset(&info, 0, sizeof(info));
+        info.cbSize = sizeof(info);
+        ret = pGetRawInputDeviceInfoA(devices[i].hDevice, RIDI_DEVICEINFO, &info, &sz);
+        ok(ret == sizeof(info), "GetRawInputDeviceInfo gave wrong return: %d\n", err);
+        ok(sz == sizeof(info), "GetRawInputDeviceInfo set wrong size\n");
+        ok(info.dwType == devices[i].dwType, "GetRawInputDeviceInfo set wrong type: 0x%x\n", info.dwType);
+
+        /* setupapi returns an NT device path, but CreateFile() < Vista can't
+         * understand that; so use the \\?\ prefix instead */
+        name[1] = '\\';
+        file = CreateFileW(name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+        todo_wine_if(info.dwType != RIM_TYPEHID)
+            ok(file != INVALID_HANDLE_VALUE, "Failed to open %s, error %u\n", wine_dbgstr_w(name), GetLastError());
+        CloseHandle(file);
+    }
+
     /* check if variable changes from larger to smaller value */
-    devcount = odevcount = sizeof(devices) / sizeof(devices[0]);
+    devcount = odevcount = ARRAY_SIZE(devices);
     oret = ret = pGetRawInputDeviceList(devices, &odevcount, sizeof(devices[0]));
     ok(ret > 0, "expected non-zero\n");
     ok(devcount == odevcount, "expected %d, got %d\n", devcount, odevcount);
     devcount = odevcount;
-    odevcount = sizeof(devices) / sizeof(devices[0]);
+    odevcount = ARRAY_SIZE(devices);
     ret = pGetRawInputDeviceList(NULL, &odevcount, sizeof(devices[0]));
     ok(ret == 0, "expected 0, got %d\n", ret);
     ok(odevcount == oret, "expected %d, got %d\n", oret, odevcount);
 }
 
+static void test_GetRawInputData(void)
+{
+    UINT size;
+    UINT ret;
+
+    /* Null raw input handle */
+    ret = GetRawInputData(NULL, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
+    ok(ret == ~0U, "Expect ret %u, got %u\n", ~0U, ret);
+}
+
 static void test_key_map(void)
 {
     HKL kl = GetKeyboardLayout(0);
@@ -1649,7 +1742,7 @@ static void test_key_map(void)
        "Scan code -> vKey = %x (not VK_RSHIFT)\n", kR);
 
     /* test that MAPVK_VSC_TO_VK prefers the non-numpad vkey if there's ambiguity */
-    for (i = 0; i < sizeof(numpad_collisions)/sizeof(numpad_collisions[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(numpad_collisions); i++)
     {
         UINT numpad_scan = MapVirtualKeyExA(numpad_collisions[i][0],  MAPVK_VK_TO_VSC, kl);
         UINT other_scan  = MapVirtualKeyExA(numpad_collisions[i][1],  MAPVK_VK_TO_VSC, kl);
@@ -1747,7 +1840,7 @@ static void test_ToUnicode(void)
            "ToUnicode didn't null-terminate the buffer when there was room.\n");
     }
 
-    for (i = 0; i < sizeof(utests) / sizeof(utests[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(utests); i++)
     {
         UINT vk = utests[i].vk, mod = utests[i].modifiers, scan;
 
@@ -1890,7 +1983,7 @@ static void test_key_names(void)
     ok( buffer[0] == 0, "wrong string '%s'\n", buffer );
 
     memset( bufferW, 0xcc, sizeof(bufferW) );
-    ret = GetKeyNameTextW( lparam, bufferW, sizeof(bufferW)/sizeof(WCHAR) );
+    ret = GetKeyNameTextW( lparam, bufferW, ARRAY_SIZE(bufferW));
     ok( ret > 0, "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
     ok( ret == lstrlenW(bufferW), "wrong len %u for %s\n", ret, wine_dbgstr_w(bufferW) );
 
@@ -2274,6 +2367,25 @@ static void test_Input_mouse(void)
         ok(region_type == ERROR, "expected ERROR, got %d\n", region_type);
     }
 
+    get_dc_region(&region, hwnd, DCX_PARENTCLIP);
+    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
+       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
+    get_dc_region(&region, hwnd, DCX_WINDOW | DCX_USESTYLE);
+    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
+       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
+    get_dc_region(&region, hwnd, DCX_USESTYLE);
+    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
+       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
+    get_dc_region(&region, static_win, DCX_PARENTCLIP);
+    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
+       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
+    get_dc_region(&region, static_win, DCX_WINDOW | DCX_USESTYLE);
+    ok(region.left == 110 && region.top == 110 && region.right == 130 && region.bottom == 130,
+       "expected region (110,110)-(130,130), got %s\n", wine_dbgstr_rect(&region));
+    get_dc_region(&region, static_win, DCX_USESTYLE);
+    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
+       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
+
     got_button_down = got_button_up = FALSE;
     simulate_click(TRUE, 150, 150);
     while (wait_for_message(&msg))
@@ -2314,13 +2426,11 @@ static void test_Input_mouse(void)
 
         if (msg.message == WM_LBUTTONDOWN)
         {
-            todo_wine
             ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
             got_button_down = TRUE;
         }
         else if (msg.message == WM_LBUTTONUP)
         {
-            todo_wine
             ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
             got_button_up = TRUE;
             break;
@@ -2372,25 +2482,6 @@ static void test_Input_mouse(void)
         ok(region_type == ERROR, "expected ERROR, got %d\n", region_type);
     }
 
-    get_dc_region(&region, hwnd, DCX_PARENTCLIP);
-    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
-       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
-    get_dc_region(&region, hwnd, DCX_WINDOW | DCX_USESTYLE);
-    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
-       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
-    get_dc_region(&region, hwnd, DCX_USESTYLE);
-    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
-       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
-    get_dc_region(&region, static_win, DCX_PARENTCLIP);
-    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
-       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
-    get_dc_region(&region, static_win, DCX_WINDOW | DCX_USESTYLE);
-    ok(region.left == 110 && region.top == 110 && region.right == 130 && region.bottom == 130,
-       "expected region (110,110)-(130,130), got %s\n", wine_dbgstr_rect(&region));
-    get_dc_region(&region, static_win, DCX_USESTYLE);
-    ok(region.left == 100 && region.top == 100 && region.right == 200 && region.bottom == 200,
-       "expected region (100,100)-(200,200), got %s\n", wine_dbgstr_rect(&region));
-
     got_button_down = got_button_up = FALSE;
     simulate_click(TRUE, 150, 150);
     while (wait_for_message(&msg))
@@ -2399,11 +2490,13 @@ static void test_Input_mouse(void)
 
         if (msg.message == WM_LBUTTONDOWN)
         {
+            todo_wine
             ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
             got_button_down = TRUE;
         }
         else if (msg.message == WM_LBUTTONUP)
         {
+            todo_wine
             ok(msg.hwnd == button_win, "msg.hwnd = %p\n", msg.hwnd);
             got_button_up = TRUE;
             break;
@@ -2832,6 +2925,7 @@ static void test_GetKeyState(void)
     result = WaitForSingleObject(semaphores[0], 1000);
     ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
 
+    SetForegroundWindow(hwnd);
     SetFocus(hwnd);
     keybd_event('X', 0, 0, 0);
 
@@ -2885,19 +2979,205 @@ static void test_OemKeyScan(void)
     }
 }
 
-START_TEST(input)
+static INPUT_MESSAGE_SOURCE expect_src;
+
+static LRESULT WINAPI msg_source_proc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
 {
-    init_function_pointers();
+    INPUT_MESSAGE_SOURCE source;
+    MSG msg;
 
-    if (pSendInput)
+    ok( pGetCurrentInputMessageSource( &source ), "GetCurrentInputMessageSource failed\n" );
+    switch (message)
     {
-        test_Input_blackbox();
-        test_Input_whitebox();
-        test_Input_unicode();
-        test_Input_mouse();
+    case WM_KEYDOWN:
+    case WM_KEYUP:
+    case WM_SYSKEYDOWN:
+    case WM_SYSKEYUP:
+    case WM_MOUSEMOVE:
+    case WM_LBUTTONDOWN:
+    case WM_LBUTTONUP:
+    case WM_RBUTTONDOWN:
+    case WM_RBUTTONUP:
+        ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
+            (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
+            "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
+        ok( source.originId == expect_src.originId ||
+            (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
+            "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
+        SendMessageA( hwnd, WM_USER, 0, 0 );
+        PostMessageA( hwnd, WM_USER, 0, 0 );
+        if (PeekMessageW( &msg, hwnd, WM_USER, WM_USER, PM_REMOVE )) DispatchMessageW( &msg );
+        ok( source.deviceType == expect_src.deviceType || /* also accept system-generated WM_MOUSEMOVE */
+            (message == WM_MOUSEMOVE && source.deviceType == IMDT_UNAVAILABLE),
+            "%x: wrong deviceType %x/%x\n", message, source.deviceType, expect_src.deviceType );
+        ok( source.originId == expect_src.originId ||
+            (message == WM_MOUSEMOVE && source.originId == IMO_SYSTEM),
+            "%x: wrong originId %x/%x\n", message, source.originId, expect_src.originId );
+        break;
+    default:
+        ok( source.deviceType == IMDT_UNAVAILABLE, "%x: wrong deviceType %x\n",
+            message, source.deviceType );
+        ok( source.originId == 0, "%x: wrong originId %x\n", message, source.originId );
+        break;
     }
-    else win_skip("SendInput is not available\n");
 
+    return DefWindowProcA( hwnd, message, wp, lp );
+}
+
+static void test_input_message_source(void)
+{
+    WNDCLASSA cls;
+    TEST_INPUT inputs[2];
+    HWND hwnd;
+    RECT rc;
+    MSG msg;
+
+    cls.style = 0;
+    cls.lpfnWndProc = msg_source_proc;
+    cls.cbClsExtra = 0;
+    cls.cbWndExtra = 0;
+    cls.hInstance = GetModuleHandleA(0);
+    cls.hIcon = 0;
+    cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+    cls.hbrBackground = 0;
+    cls.lpszMenuName = NULL;
+    cls.lpszClassName = "message source class";
+    RegisterClassA(&cls);
+    hwnd = CreateWindowA( cls.lpszClassName, "test", WS_OVERLAPPED, 0, 0, 100, 100,
+                          0, 0, 0, 0 );
+    ShowWindow( hwnd, SW_SHOWNORMAL );
+    UpdateWindow( hwnd );
+    SetForegroundWindow( hwnd );
+    SetFocus( hwnd );
+
+    inputs[0].type = INPUT_KEYBOARD;
+    inputs[0].u.ki.dwExtraInfo = 0;
+    inputs[0].u.ki.time = 0;
+    inputs[0].u.ki.wVk = 0;
+    inputs[0].u.ki.wScan = 0x3c0;
+    inputs[0].u.ki.dwFlags = KEYEVENTF_UNICODE;
+    inputs[1] = inputs[0];
+    inputs[1].u.ki.dwFlags |= KEYEVENTF_KEYUP;
+
+    expect_src.deviceType = IMDT_UNAVAILABLE;
+    expect_src.originId = IMO_UNAVAILABLE;
+    SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
+    SendMessageA( hwnd, WM_MOUSEMOVE, 0, 0 );
+
+    SendInput( 2, (INPUT *)inputs, sizeof(INPUT) );
+    while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
+    {
+        expect_src.deviceType = IMDT_KEYBOARD;
+        expect_src.originId = IMO_INJECTED;
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+    GetWindowRect( hwnd, &rc );
+    simulate_click( TRUE, (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 );
+    simulate_click( FALSE, (rc.left + rc.right) / 2 + 1, (rc.top + rc.bottom) / 2 + 1 );
+    while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
+    {
+        expect_src.deviceType = IMDT_MOUSE;
+        expect_src.originId = IMO_INJECTED;
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+
+    expect_src.deviceType = IMDT_UNAVAILABLE;
+    expect_src.originId = IMO_UNAVAILABLE;
+    SendMessageA( hwnd, WM_KEYDOWN, 0, 0 );
+    SendMessageA( hwnd, WM_LBUTTONDOWN, 0, 0 );
+    PostMessageA( hwnd, WM_KEYUP, 0, 0 );
+    PostMessageA( hwnd, WM_LBUTTONUP, 0, 0 );
+    while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
+    {
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+
+    expect_src.deviceType = IMDT_UNAVAILABLE;
+    expect_src.originId = IMO_SYSTEM;
+    SetCursorPos( (rc.left + rc.right) / 2 - 1, (rc.top + rc.bottom) / 2 - 1 );
+    while (PeekMessageW( &msg, hwnd, 0, 0, PM_REMOVE ))
+    {
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+
+    DestroyWindow( hwnd );
+    UnregisterClassA( cls.lpszClassName, GetModuleHandleA(0) );
+}
+
+static void test_GetPointerType(void)
+{
+    BOOL ret;
+    POINTER_INPUT_TYPE type = -1;
+    UINT id = 0;
+
+    SetLastError(0xdeadbeef);
+    ret = pGetPointerType(id, NULL);
+    ok(!ret, "GetPointerType should have failed.\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected error ERROR_INVALID_PARAMETER, got %u.\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetPointerType(id, &type);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "expected error ERROR_INVALID_PARAMETER, got %u.\n", GetLastError());
+    ok(!ret, "GetPointerType failed, got type %d for %u.\n", type, id );
+    ok(type == -1, " type %d\n", type );
+
+    id = 1;
+    ret = pGetPointerType(id, &type);
+    ok(ret, "GetPointerType failed, got type %d for %u.\n", type, id );
+    ok(type == PT_MOUSE, " type %d\n", type );
+}
+
+static void test_GetKeyboardLayoutList(void)
+{
+    int cnt, cnt2;
+    HKL *layouts;
+    ULONG_PTR baselayout;
+    LANGID langid;
+
+    baselayout = GetUserDefaultLCID();
+    langid = PRIMARYLANGID(LANGIDFROMLCID(baselayout));
+    if (langid == LANG_CHINESE || langid == LANG_JAPANESE || langid == LANG_KOREAN)
+        baselayout = MAKELONG( baselayout, 0xe001 ); /* IME */
+    else
+        baselayout |= baselayout << 16;
+
+    cnt = GetKeyboardLayoutList(0, NULL);
+    /* Most users will not have more than a few keyboard layouts installed at a time. */
+    ok(cnt > 0 && cnt < 10, "Layout count %d\n", cnt);
+    if (cnt > 0)
+    {
+        layouts = HeapAlloc(GetProcessHeap(), 0, sizeof(*layouts) * cnt );
+
+        cnt2 = GetKeyboardLayoutList(cnt, layouts);
+        ok(cnt == cnt2, "wrong value %d!=%d\n", cnt, cnt2);
+        for(cnt = 0; cnt < cnt2; cnt++)
+        {
+            if(layouts[cnt] == (HKL)baselayout)
+                break;
+        }
+        ok(cnt < cnt2, "Didnt find current keyboard\n");
+
+        HeapFree(GetProcessHeap(), 0, layouts);
+    }
+}
+
+START_TEST(input)
+{
+    POINT pos;
+
+    init_function_pointers();
+    GetCursorPos( &pos );
+
+    test_Input_blackbox();
+    test_Input_whitebox();
+    test_Input_unicode();
+    test_Input_mouse();
     test_keynames();
     test_mouse_ll_hook();
     test_key_map();
@@ -2909,6 +3189,8 @@ START_TEST(input)
     test_attach_input();
     test_GetKeyState();
     test_OemKeyScan();
+    test_GetRawInputData();
+    test_GetKeyboardLayoutList();
 
     if(pGetMouseMovePointsEx)
         test_GetMouseMovePointsEx();
@@ -2919,4 +3201,16 @@ START_TEST(input)
         test_GetRawInputDeviceList();
     else
         win_skip("GetRawInputDeviceList is not available\n");
+
+    if (pGetCurrentInputMessageSource)
+        test_input_message_source();
+    else
+        win_skip("GetCurrentInputMessageSource is not available\n");
+
+    SetCursorPos( pos.x, pos.y );
+
+    if(pGetPointerType)
+        test_GetPointerType();
+    else
+        win_skip("GetPointerType is not available\n");
 }
index 88d3adb..8d81151 100644 (file)
@@ -51,7 +51,7 @@ static int strcmp_aw(LPCWSTR strw, const char *stra)
     WCHAR buf[1024];
 
     if (!stra) return 1;
-    MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, ARRAY_SIZE(buf));
     return lstrcmpW(strw, buf);
 }
 
@@ -90,7 +90,6 @@ struct listbox_stat {
 };
 
 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;
@@ -130,8 +129,7 @@ keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
 
 #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)
+      ": expected %d, got %d\n", style, t.s.f, got.f)
 
 #define listbox_todo_field_ok(t, s, f, got) \
   todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); }
@@ -143,13 +141,15 @@ keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
   listbox_todo_field_ok(t, s, selcount, got)
 
 static void
-check (const struct listbox_test test)
+check (DWORD style, const struct listbox_test test)
 {
   struct listbox_stat answer;
-  HWND hLB=create_listbox (test.prop.add_style, 0);
   RECT second_item;
   int i;
   int res;
+  HWND hLB;
+
+  hLB = create_listbox (style, 0);
 
   listbox_query (hLB, &answer);
   listbox_ok (test, init, answer);
@@ -166,13 +166,13 @@ check (const struct listbox_test test)
   listbox_ok (test, step, answer);
 
   DestroyWindow (hLB);
-  hLB=create_listbox (test.prop.add_style, 0);
+  hLB = create_listbox(style, 0);
 
   SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
   listbox_query (hLB, &answer);
   listbox_ok (test, sel, answer);
 
-  for (i=0;i<4;i++) {
+  for (i = 0; i < 4 && !(style & LBS_NODATA); i++) {
        DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0);
        CHAR *txt;
        WCHAR *txtw;
@@ -184,13 +184,9 @@ check (const struct listbox_test test)
 
        txtw = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, 2*size+2);
        resW=SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
-       if (resA != resW) {
-            trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n",
-                resA, resW);
-       } else {
-           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]);
-       }
+       ok(resA == resW, "Unexpected text length.\n");
+       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);
@@ -342,35 +338,73 @@ static HWND create_parent( void )
 
 static void test_ownerdraw(void)
 {
+    static const DWORD styles[] =
+    {
+        0,
+        LBS_NODATA
+    };
     HWND parent, hLB;
     INT ret;
     RECT rc;
+    UINT i;
 
     parent = create_parent();
     assert(parent);
 
-    hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent);
-    assert(hLB);
+    for (i = 0; i < ARRAY_SIZE(styles); i++)
+    {
+        hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | styles[i], parent);
+        assert(hLB);
 
-    SetForegroundWindow(hLB);
-    UpdateWindow(hLB);
+        SetForegroundWindow(hLB);
+        UpdateWindow(hLB);
 
-    /* make height short enough */
-    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
-    SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1,
-                 SWP_NOZORDER | SWP_NOMOVE);
+        /* make height short enough */
+        SendMessageA(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 */
-    SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
-    ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
-    ok(ret == 1, "wrong top index %d\n", ret);
+        /* make 0 item invisible */
+        SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
+        ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
+        ok(ret == 1, "wrong top index %d\n", ret);
 
-    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
-    trace("item 0 rect %s\n", wine_dbgstr_rect(&rc));
-    ok(!IsRectEmpty(&rc), "empty item rect\n");
-    ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
+        SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+        trace("item 0 rect %s\n", wine_dbgstr_rect(&rc));
+        ok(!IsRectEmpty(&rc), "empty item rect\n");
+        ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
 
-    DestroyWindow(hLB);
+        DestroyWindow(hLB);
+
+        /* Both FIXED and VARIABLE, FIXED should override VARIABLE. */
+        hLB = CreateWindowA("listbox", "TestList", LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE | styles[i],
+            0, 0, 100, 100, NULL, NULL, NULL, 0);
+        ok(hLB != NULL, "last error 0x%08x\n", GetLastError());
+
+        ok(GetWindowLongA(hLB, GWL_STYLE) & LBS_OWNERDRAWVARIABLE, "Unexpected window style.\n");
+
+        ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
+        ok(ret == 0, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
+        ok(ret == 1, "Unexpected return value %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 13);
+        ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+        ok(ret == 13, "Unexpected item height %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 1, 42);
+        ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+        ok(ret == 42, "Unexpected item height %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 1, 0);
+        ok(ret == 42, "Unexpected item height %d.\n", ret);
+
+        DestroyWindow (hLB);
+    }
     DestroyWindow(parent);
 }
 
@@ -475,19 +509,123 @@ static void test_LB_SETCURSEL(void)
 
     SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
     ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret);
     ret = GetScrollPos(hLB, SB_VERT);
     ok(ret == 0, "expected vscroll 0, got %d\n", ret);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0);
     ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret);
     ret = GetScrollPos(hLB, SB_VERT);
     ok(ret == 1, "expected vscroll 1, got %d\n", ret);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    hLB = create_listbox(0, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 1, 0);
+    ok(ret == 1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    /* LBS_EXTENDEDSEL */
+    hLB = create_listbox(LBS_EXTENDEDSEL, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
+    ok(ret == -1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    /* LBS_MULTIPLESEL */
+    hLB = create_listbox(LBS_MULTIPLESEL, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
+    ok(ret == -1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     DestroyWindow(hLB);
 }
 
+static void test_LB_SETSEL(void)
+{
+    HWND list;
+    int ret;
+
+    /* LBS_EXTENDEDSEL */
+    list = create_listbox(LBS_EXTENDEDSEL, 0);
+    ok(list != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(list);
+
+    /* LBS_MULTIPLESEL */
+    list = create_listbox(LBS_MULTIPLESEL, 0);
+    ok(list != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(list);
+}
+
 static void test_listbox_height(void)
 {
     HWND hList;
@@ -527,6 +665,135 @@ static void test_listbox_height(void)
     DestroyWindow( hList );
 }
 
+static void test_changing_selection_styles(void)
+{
+    static const DWORD styles[] =
+    {
+        0,
+        LBS_NODATA | LBS_OWNERDRAWFIXED
+    };
+    static const DWORD selstyles[] =
+    {
+        0,
+        LBS_MULTIPLESEL,
+        LBS_EXTENDEDSEL,
+        LBS_MULTIPLESEL | LBS_EXTENDEDSEL
+    };
+    static const LONG selexpect_single[]  = { 0, 0, 1 };
+    static const LONG selexpect_single2[] = { 1, 0, 0 };
+    static const LONG selexpect_multi[]   = { 1, 0, 1 };
+    static const LONG selexpect_multi2[]  = { 1, 1, 0 };
+
+    HWND parent, listbox;
+    DWORD style;
+    LONG ret;
+    UINT i, j, k;
+
+    parent = create_parent();
+    ok(parent != NULL, "Failed to create parent window.\n");
+    for (i = 0; i < ARRAY_SIZE(styles); i++)
+    {
+        /* Test if changing selection styles affects selection storage */
+        for (j = 0; j < ARRAY_SIZE(selstyles); j++)
+        {
+            LONG setcursel_expect, selitemrange_expect, getselcount_expect;
+            const LONG *selexpect;
+
+            listbox = CreateWindowA("listbox", "TestList", styles[i] | selstyles[j] | WS_CHILD | WS_VISIBLE,
+                                    0, 0, 100, 100, parent, (HMENU)1, NULL, 0);
+            ok(listbox != NULL, "%u: Failed to create ListBox window.\n", j);
+
+            if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
+            {
+                setcursel_expect = LB_ERR;
+                selitemrange_expect = LB_OKAY;
+                getselcount_expect = 2;
+                selexpect = selexpect_multi;
+            }
+            else
+            {
+                setcursel_expect = 2;
+                selitemrange_expect = LB_ERR;
+                getselcount_expect = LB_ERR;
+                selexpect = selexpect_single;
+            }
+
+            for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
+            {
+                ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"x");
+                ok(ret == k, "%u: Unexpected return value %d, expected %d.\n", j, ret, k);
+            }
+            ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0);
+            ok(ret == ARRAY_SIZE(selexpect_multi), "%u: Unexpected count %d.\n", j, ret);
+
+            /* Select items with different methods */
+            ret = SendMessageA(listbox, LB_SETCURSEL, 2, 0);
+            ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret);
+            ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 0));
+            ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
+            ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 2));
+            ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
+
+            /* Verify that the proper items are selected */
+            for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
+            {
+                ret = SendMessageA(listbox, LB_GETSEL, k, 0);
+                ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
+                    j, ret, selexpect[k]);
+            }
+
+            /* Now change the selection style */
+            style = GetWindowLongA(listbox, GWL_STYLE);
+            ok((style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == selstyles[j],
+                "%u: unexpected window styles %#x.\n", j, style);
+            if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
+                style &= ~selstyles[j];
+            else
+                style |= LBS_MULTIPLESEL | LBS_EXTENDEDSEL;
+            SetWindowLongA(listbox, GWL_STYLE, style);
+            style = GetWindowLongA(listbox, GWL_STYLE);
+            ok(!(style & selstyles[j]), "%u: unexpected window styles %#x.\n", j, style);
+
+            /* Verify that the same items are selected */
+            ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0);
+            ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n",
+                j, getselcount_expect, ret);
+
+            for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
+            {
+                ret = SendMessageA(listbox, LB_GETSEL, k, 0);
+                ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
+                    j, ret, selexpect[k]);
+            }
+
+            /* Lastly see if we can still change the selection as before with old style */
+            if (setcursel_expect != LB_ERR) setcursel_expect = 0;
+            ret = SendMessageA(listbox, LB_SETCURSEL, 0, 0);
+            ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret);
+            ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 1));
+            ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
+            ret = SendMessageA(listbox, LB_SELITEMRANGE, FALSE, MAKELPARAM(2, 2));
+            ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret);
+
+            /* And verify the selections */
+            selexpect = (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) ? selexpect_multi2 : selexpect_single2;
+            ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0);
+            ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n",
+                j, getselcount_expect, ret);
+
+            for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++)
+            {
+                ret = SendMessageA(listbox, LB_GETSEL, k, 0);
+                ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n",
+                    j, ret, selexpect[k]);
+            }
+
+            DestroyWindow(listbox);
+        }
+    }
+    DestroyWindow(parent);
+}
+
 static void test_itemfrompoint(void)
 {
     /* WS_POPUP is required in order to have a more accurate size calculation (
@@ -637,6 +904,7 @@ static void test_listbox_item_data(void)
 
 static void test_listbox_LB_DIR(void)
 {
+    char path[MAX_PATH], curdir[MAX_PATH];
     HWND hList;
     int res, itemCount;
     int itemCount_justFiles;
@@ -649,6 +917,16 @@ static void test_listbox_LB_DIR(void)
     char driveletter;
     const char *wildcard = "*";
     HANDLE file;
+    BOOL ret;
+
+    GetCurrentDirectoryA(ARRAY_SIZE(curdir), curdir);
+
+    GetTempPathA(ARRAY_SIZE(path), path);
+    ret = SetCurrentDirectoryA(path);
+    ok(ret, "Failed to set current directory.\n");
+
+    ret = CreateDirectoryA("lb_dir_test", NULL);
+    ok(ret, "Failed to create test directory.\n");
 
     file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
     ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
@@ -980,11 +1258,11 @@ static void test_listbox_LB_DIR(void)
         itemCount, itemCount_allDirs);
     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n");
 
-    if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3)  /* there's no [..] in drive root */
+    if (itemCount)
     {
         memset(pathBuffer, 0, MAX_PATH);
         SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer);
-        ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n");
+        ok( !strcmp(pathBuffer, "[..]"), "First element is %s, not [..]\n", pathBuffer);
     }
 
     /* This tests behavior when no files match the wildcard */
@@ -1068,6 +1346,9 @@ static void test_listbox_LB_DIR(void)
     DestroyWindow(hList);
 
     DeleteFileA( "wtest1.tmp.c" );
+    RemoveDirectoryA("lb_dir_test");
+
+    SetCurrentDirectoryA(curdir);
 }
 
 static HWND g_listBox;
@@ -1555,7 +1836,7 @@ static void test_listbox_dlgdir(void)
     strcpy(pathBuffer, "C:\\");
     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
     ok(res || broken(!res) /* NT4/W2K */, "DlgDirList failed to list C:\\ folders\n");
-    todo_wine ok(!strcmp(pathBuffer, "*") || broken(!res) /* NT4/W2K */,
+    ok(!strcmp(pathBuffer, "*") || broken(!res) /* NT4/W2K */,
        "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
 
     strcpy(pathBuffer, "C:\\*");
@@ -1568,8 +1849,8 @@ static void test_listbox_dlgdir(void)
     SetLastError(0xdeadbeef);
     strcpy(pathBuffer, "C:\\INVALID$$DIR");
     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
-    todo_wine ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res);
-    todo_wine ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS,
+    ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res);
+    ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS,
        "GetLastError should return 0x589, got 0x%X\n",GetLastError());
 
     DestroyWindow(hWnd);
@@ -1577,7 +1858,13 @@ static void test_listbox_dlgdir(void)
 
 static void test_set_count( void )
 {
+    static const DWORD styles[] =
+    {
+        LBS_OWNERDRAWFIXED,
+        LBS_HASSTRINGS,
+    };
     HWND parent, listbox;
+    unsigned int i;
     LONG ret;
     RECT r;
 
@@ -1606,7 +1893,25 @@ static void test_set_count( void )
     GetUpdateRect( listbox, &r, TRUE );
     ok( !IsRectEmpty( &r ), "got empty rect\n");
 
+    ret = SendMessageA( listbox, LB_SETCOUNT, -5, 0 );
+    ok( ret == 0, "got %d\n", ret );
+    ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 );
+    ok( ret == -5, "got %d\n", ret );
+
     DestroyWindow( listbox );
+
+    for (i = 0; i < ARRAY_SIZE(styles); ++i)
+    {
+        listbox = create_listbox( styles[i] | WS_CHILD | WS_VISIBLE, parent );
+
+        SetLastError( 0xdeadbeef );
+        ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 );
+        ok( ret == LB_ERR, "expected %d, got %d\n", LB_ERR, ret );
+        ok( GetLastError() == ERROR_SETCOUNT_ON_BAD_LB, "Unexpected error %d.\n", GetLastError() );
+
+        DestroyWindow( listbox );
+    }
+
     DestroyWindow( parent );
 }
 
@@ -1653,6 +1958,69 @@ todo_wine
     DestroyWindow(parent);
 }
 
+static void test_init_storage( void )
+{
+    static const DWORD styles[] =
+    {
+        LBS_HASSTRINGS,
+        LBS_NODATA | LBS_OWNERDRAWFIXED,
+    };
+    HWND parent, listbox;
+    LONG ret, items_size;
+    int i, j;
+
+    parent = create_parent();
+    for (i = 0; i < ARRAY_SIZE(styles); i++)
+    {
+        listbox = CreateWindowA("listbox", "TestList", styles[i] | WS_CHILD,
+                                0, 0, 100, 100, parent, (HMENU)1, NULL, 0);
+
+        items_size = SendMessageA(listbox, LB_INITSTORAGE, 100, 0);
+        ok(items_size >= 100, "expected at least 100, got %d\n", items_size);
+
+        ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0);
+        ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
+
+        /* it doesn't grow since the space was already reserved */
+        ret = SendMessageA(listbox, LB_INITSTORAGE, items_size, 0);
+        ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
+
+        /* it doesn't shrink the reserved space */
+        ret = SendMessageA(listbox, LB_INITSTORAGE, 42, 0);
+        ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
+
+        /* now populate almost all of it so it's not reserved anymore */
+        if (styles[i] & LBS_NODATA)
+        {
+            ret = SendMessageA(listbox, LB_SETCOUNT, items_size - 1, 0);
+            ok(ret == 0, "unexpected return value %d\n", ret);
+        }
+        else
+        {
+            for (j = 0; j < items_size - 1; j++)
+            {
+                ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"");
+                ok(ret == j, "expected %d, got %d\n", j, ret);
+            }
+        }
+
+        /* we still have one more reserved slot, so it doesn't grow yet */
+        ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0);
+        ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
+
+        /* fill the slot and check again, it should grow this time */
+        ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"");
+        ok(ret == items_size - 1, "expected %d, got %d\n", items_size - 1, ret);
+        ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0);
+        ok(ret == items_size, "expected %d, got %d\n", items_size, ret);
+        ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0);
+        ok(ret > items_size, "expected it to grow past %d, got %d\n", items_size, ret);
+
+        DestroyWindow(listbox);
+    }
+    DestroyWindow(parent);
+}
+
 static void test_missing_lbuttonup( void )
 {
     HWND listbox, parent, capture;
@@ -1876,88 +2244,216 @@ static void test_WM_MEASUREITEM(void)
     DestroyWindow(parent);
 }
 
+static void test_LBS_NODATA(void)
+{
+    static const DWORD invalid_styles[] =
+    {
+        0,
+        LBS_OWNERDRAWVARIABLE,
+        LBS_SORT,
+        LBS_HASSTRINGS,
+        LBS_OWNERDRAWFIXED | LBS_SORT,
+        LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
+    };
+    static const UINT invalid_idx[] = { -2, 2 };
+    static const UINT valid_idx[] = { 0, 1 };
+    static const ULONG_PTR zero_data;
+    HWND listbox, parent;
+    unsigned int i;
+    ULONG_PTR data;
+    INT ret;
+
+    listbox = CreateWindowA("listbox", "TestList", LBS_NODATA | LBS_OWNERDRAWFIXED | WS_VISIBLE,
+        0, 0, 100, 100, NULL, NULL, NULL, 0);
+    ok(listbox != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
+    ok(ret == 1, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0);
+    ok(ret == 2, "Unexpected return value %d.\n", ret);
+
+    /* Invalid indices. */
+    for (i = 0; i < ARRAY_SIZE(invalid_idx); ++i)
+    {
+        ret = SendMessageA(listbox, LB_SETITEMDATA, invalid_idx[i], 42);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(listbox, LB_GETTEXTLEN, invalid_idx[i], 0);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        if (ret == LB_ERR)
+        {
+            ret = SendMessageA(listbox, LB_GETTEXT, invalid_idx[i], (LPARAM)&data);
+            ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        }
+        ret = SendMessageA(listbox, LB_GETITEMDATA, invalid_idx[i], 0);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    }
+
+    /* Valid indices. */
+    for (i = 0; i < ARRAY_SIZE(valid_idx); ++i)
+    {
+        ret = SendMessageA(listbox, LB_SETITEMDATA, valid_idx[i], 42);
+        ok(ret == TRUE, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(listbox, LB_GETTEXTLEN, valid_idx[i], 0);
+        ok(ret == sizeof(data), "Unexpected return value %d.\n", ret);
+
+        memset(&data, 0xee, sizeof(data));
+        ret = SendMessageA(listbox, LB_GETTEXT, valid_idx[i], (LPARAM)&data);
+        ok(ret == sizeof(data), "Unexpected return value %d.\n", ret);
+        ok(!memcmp(&data, &zero_data, sizeof(data)), "Unexpected item data.\n");
+
+        ret = SendMessageA(listbox, LB_GETITEMDATA, valid_idx[i], 0);
+        ok(ret == 0, "Unexpected return value %d.\n", ret);
+    }
+
+    /* More messages that don't work with LBS_NODATA. */
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRING, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRING, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+
+    DestroyWindow(listbox);
+
+    /* Invalid window style combinations. */
+    parent = create_parent();
+    ok(parent != NULL, "Failed to create parent window.\n");
+
+    for (i = 0; i < ARRAY_SIZE(invalid_styles); ++i)
+    {
+        DWORD style;
+
+        listbox = CreateWindowA("listbox", "TestList", LBS_NODATA | WS_CHILD | invalid_styles[i],
+                0, 0, 100, 100, parent, (HMENU)1, NULL, 0);
+        ok(listbox != NULL, "Failed to create a listbox.\n");
+
+        style = GetWindowLongA(listbox, GWL_STYLE);
+        ok((style & invalid_styles[i]) == invalid_styles[i], "%u: unexpected window styles %#x.\n", i, style);
+        ret = SendMessageA(listbox, LB_SETCOUNT, 100, 0);
+        ok(ret == LB_ERR, "%u: unexpected return value %d.\n", i, ret);
+        DestroyWindow(listbox);
+    }
+
+    DestroyWindow(parent);
+}
+
 START_TEST(listbox)
 {
   const struct listbox_test SS =
 /*   {add_style} */
-    {{0},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,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},
+    {{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},
+    {{     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},
+    {{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},
+    {{     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},
+    {{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},
+    {{     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},
+    {{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);
+  check (0, SS);
+  trace (" ... with NOSEL\n");
+  check (LBS_NOSEL, SS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED, SS);
   trace (" ... with NOSEL\n");
-  check (SS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOSEL, SS_NS);
+
   trace (" Testing multiple selection...\n");
-  check (MS);
+  check (LBS_MULTIPLESEL, MS);
+  trace (" ... with NOSEL\n");
+  check (LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL, MS);
   trace (" ... with NOSEL\n");
-  check (MS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
+
   trace (" Testing extended selection...\n");
-  check (ES);
+  check (LBS_EXTENDEDSEL, ES);
+  trace (" ... with NOSEL\n");
+  check (LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL, ES);
   trace (" ... with NOSEL\n");
-  check (ES_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
+
   trace (" Testing extended and multiple selection...\n");
-  check (EMS);
+  check (LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
+  trace (" ... with NOSEL\n");
+  check (LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
   trace (" ... with NOSEL\n");
-  check (EMS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
 
   check_item_height();
   test_ownerdraw();
   test_LB_SELITEMRANGE();
   test_LB_SETCURSEL();
   test_listbox_height();
+  test_changing_selection_styles();
   test_itemfrompoint();
   test_listbox_item_data();
   test_listbox_LB_DIR();
   test_listbox_dlgdir();
   test_set_count();
+  test_init_storage();
   test_GetListBoxInfo();
   test_missing_lbuttonup();
   test_extents();
   test_WM_MEASUREITEM();
+  test_LB_SETSEL();
+  test_LBS_NODATA();
 }
index 3430ed6..a7a8b8a 100644 (file)
 
 static ATOM atomMenuCheckClass;
 
-static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
-static BOOL (WINAPI *pGetMenuBarInfo)(HWND,LONG,LONG,PMENUBARINFO);
-static UINT (WINAPI *pSendInput)(UINT, INPUT*, size_t);
-static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
-
-static void init_function_pointers(void)
-{
-    HMODULE hdll = GetModuleHandleA("user32");
-
-#define GET_PROC(func) \
-    p ## func = (void*)GetProcAddress(hdll, #func); \
-    if(!p ## func) \
-      trace("GetProcAddress(%s) failed\n", #func);
-
-    GET_PROC(GetMenuInfo)
-    GET_PROC(GetMenuBarInfo)
-    GET_PROC(SendInput)
-    GET_PROC(SetMenuInfo)
-
-#undef GET_PROC
-}
-
-static BOOL correct_behavior(void)
-{
-    HMENU hmenu;
-    MENUITEMINFOA info;
-    BOOL rc;
-
-    hmenu = CreateMenu();
-
-    memset(&info, 0, sizeof(MENUITEMINFOA));
-    info.cbSize= sizeof(MENUITEMINFOA);
-    SetLastError(0xdeadbeef);
-    rc = GetMenuItemInfoA(hmenu, 0, TRUE, &info);
-    /* Win9x  : 0xdeadbeef
-     * NT4    : ERROR_INVALID_PARAMETER
-     * >= W2K : ERROR_MENU_ITEM_NOT_FOUND
-     */
-    if (!rc && GetLastError() != ERROR_MENU_ITEM_NOT_FOUND)
-    {
-        win_skip("NT4 and below can't handle a bigger MENUITEMINFO struct\n");
-        DestroyMenu(hmenu);
-        return FALSE;
-    }
-
-    DestroyMenu(hmenu);
-    return TRUE;
-}
-
 static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 {
     switch (msg)
@@ -284,11 +235,6 @@ static void test_getmenubarinfo(void)
     HWND hwnd;
     INT err;
 
-    if (!pGetMenuBarInfo) {
-        win_skip("GetMenuBarInfo is not available\n");
-        return;
-    }
-
     mbi.cbSize = sizeof(MENUBARINFO);
 
     hwnd = CreateWindowExA(0, (LPCSTR)MAKEINTATOM(atomMenuCheckClass), NULL,
@@ -298,7 +244,7 @@ static void test_getmenubarinfo(void)
 
     /* no menu: getmenubarinfo should fail */
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
     err = GetLastError();
     ok(ret == FALSE, "GetMenuBarInfo should not have been successful\n");
     ok(err == 0xdeadbeef, "err = %d\n", err);
@@ -311,38 +257,38 @@ static void test_getmenubarinfo(void)
     ok(ret, "SetMenu failed with error %d\n", GetLastError());
 
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(NULL, OBJID_CLIENT, 0, &mbi);
+    ret = GetMenuBarInfo(NULL, OBJID_CLIENT, 0, &mbi);
     err = GetLastError();
     ok(!ret, "GetMenuBarInfo succeeded\n");
     ok(err == ERROR_INVALID_WINDOW_HANDLE, "err = %d\n", err);
 
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, OBJID_CLIENT, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_CLIENT, 0, &mbi);
     err = GetLastError();
     ok(!ret, "GetMenuBarInfo succeeded\n");
     ok(err==ERROR_INVALID_MENU_HANDLE || broken(err==0xdeadbeef) /* NT, W2K, XP */, "err = %d\n", err);
 
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, -1, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, -1, &mbi);
     err = GetLastError();
     ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
     ok(err == 0xdeadbeef, "err = %d\n", err);
 
     mbi.cbSize = 1000;
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
     err = GetLastError();
     ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
     ok(err == ERROR_INVALID_PARAMETER, "err = %d\n", err);
     mbi.cbSize = sizeof(MENUBARINFO);
 
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, 123, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, 123, 0, &mbi);
     err = GetLastError();
     ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
     ok(err == 0xdeadbeef, "err = %d\n", err);
 
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
     ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
 
     ok(mbi.rcBar.left == 0 && mbi.rcBar.top == 0 && mbi.rcBar.bottom == 0 && mbi.rcBar.right == 0,
@@ -361,13 +307,13 @@ static void test_getmenubarinfo(void)
     ok(ret, "SetMenu failed with error %d\n", GetLastError());
 
     SetLastError(0xdeadbeef);
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 200, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 200, &mbi);
     err = GetLastError();
     ok(ret == FALSE, "GetMenuBarInfo should have failed\n");
     ok(err == 0xdeadbeef, "err = %d\n", err);
 
     /* get info for the whole menu */
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 0, &mbi);
     ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
 
     /* calculate menu rectangle, from window rectangle and the position of the first item  */
@@ -384,7 +330,7 @@ static void test_getmenubarinfo(void)
     ok(mbi.fFocused == 0, "fFocused: got %d instead of 0\n", mbi.fFocused);
 
     /* get info for item nr.2 */
-    ret = pGetMenuBarInfo(hwnd, OBJID_MENU, 2, &mbi);
+    ret = GetMenuBarInfo(hwnd, OBJID_MENU, 2, &mbi);
     ok(ret, "GetMenuBarInfo failed with error %d\n", GetLastError());
     ret = GetMenuItemRect(hwnd, hmenu, 1, &rci);
     ok(ret, "GetMenuItemRect failed.\n");
@@ -397,6 +343,62 @@ static void test_getmenubarinfo(void)
     DestroyWindow(hwnd);
 }
 
+static void test_GetMenuItemRect(void)
+{
+    HWND hwnd;
+    HMENU hmenu;
+    HMENU popup_hmenu;
+    RECT window_rect;
+    RECT item_rect;
+    POINT client_top_left;
+    INT caption_height;
+    BOOL ret;
+
+    hwnd = CreateWindowW((LPCWSTR)MAKEINTATOM(atomMenuCheckClass), NULL, WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL,
+                         NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindow failed with error %d\n", GetLastError());
+    hmenu = CreateMenu();
+    ok(hmenu != NULL, "CreateMenu failed with error %d\n", GetLastError());
+    popup_hmenu = CreatePopupMenu();
+    ok(popup_hmenu != NULL, "CreatePopupMenu failed with error %d\n", GetLastError());
+    ret = AppendMenuA(popup_hmenu, MF_STRING, 0, "Popup");
+    ok(ret, "AppendMenu failed with error %d\n", GetLastError());
+    ret = AppendMenuA(hmenu, MF_STRING | MF_POPUP, (UINT_PTR)popup_hmenu, "Menu");
+    ok(ret, "AppendMenu failed with error %d\n", GetLastError());
+    ret = SetMenu(hwnd, hmenu);
+    ok(ret, "SetMenu failed with error %d\n", GetLastError());
+
+    /* Get the menu item rectangle of the displayed sysmenu item */
+    ret = GetMenuItemRect(hwnd, hmenu, 0, &item_rect);
+    ok(ret, "GetMenuItemRect failed with error %d\n", GetLastError());
+    GetWindowRect(hwnd, &window_rect);
+    /* Get the screen coordinate of the left top corner of the client rectangle */
+    client_top_left.x = 0;
+    client_top_left.y = 0;
+    MapWindowPoints(hwnd, 0, &client_top_left, 1);
+    caption_height = GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
+
+    ok(item_rect.left == client_top_left.x, "Expect item_rect.left %d == %d\n", item_rect.left, client_top_left.x);
+    ok(item_rect.right <= window_rect.right, "Expect item_rect.right %d <= %d\n", item_rect.right, window_rect.right);
+    /* A gap of 1 pixel is added deliberately in commit 75f9e64, so using equal operator would fail on Wine.
+     * Check that top and bottom are correct with 1 pixel margin tolerance */
+    ok(item_rect.top - (window_rect.top + caption_height) <= 1, "Expect item_rect.top %d - %d <= 1\n", item_rect.top,
+       window_rect.top + caption_height);
+    ok(item_rect.bottom - (client_top_left.y - 1) <= 1, "Expect item_rect.bottom %d - %d <= 1\n", item_rect.bottom,
+       client_top_left.y - 1);
+
+    /* Get the item rectangle of the not yet displayed popup menu item. */
+    ret = GetMenuItemRect(hwnd, popup_hmenu, 0, &item_rect);
+    ok(ret, "GetMenuItemRect failed with error %d\n", GetLastError());
+    ok(item_rect.left == client_top_left.x, "Expect item_rect.left %d == %d\n", item_rect.left, client_top_left.x);
+    ok(item_rect.right == client_top_left.x, "Expect item_rect.right %d == %d\n", item_rect.right, client_top_left.x);
+    ok(item_rect.top == client_top_left.y, "Expect item_rect.top %d == %d\n", item_rect.top, client_top_left.y);
+    ok(item_rect.bottom == client_top_left.y, "Expect item_rect.bottom %d == %d\n", item_rect.bottom,
+       client_top_left.y);
+
+    DestroyWindow(hwnd);
+}
+
 static void test_system_menu(void)
 {
     WCHAR testW[] = {'t','e','s','t',0};
@@ -636,18 +638,13 @@ static LRESULT WINAPI subpopuplocked_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam
 
 static void test_subpopup_locked_by_menu(void)
 {
-    DWORD gle;
     BOOL ret;
     HMENU hmenu, hsubmenu;
     MENUINFO mi = { sizeof( MENUINFO)};
     MENUITEMINFOA mii = { sizeof( MENUITEMINFOA)};
     HWND hwnd;
     const int itemid = 0x1234567;
-    if( !pGetMenuInfo)
-    {
-        win_skip("GetMenuInfo is not available\n");
-        return;
-    }
+
     /* create window, popupmenu with one subpopup */
     hwnd = CreateWindowExA(0, (LPCSTR)MAKEINTATOM(atomMenuCheckClass), NULL,
             WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
@@ -669,48 +666,36 @@ static void test_subpopup_locked_by_menu(void)
     ok( ret, "GetMenuItemInfo failed error %d\n", GetLastError());
     ok( mii.hSubMenu == hsubmenu, "submenu is %p\n", mii.hSubMenu);
     mi.fMask |= MIM_STYLE;
-    ret = pGetMenuInfo( hsubmenu, &mi);
+    ret = GetMenuInfo( hsubmenu, &mi);
     ok( ret , "GetMenuInfo returned 0 with error %d\n", GetLastError());
     ret = IsMenu( hsubmenu);
     ok( ret , "Menu handle is not valid\n");
-    SetLastError( 0xdeadbeef);
+
     ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
-    if( ret == (itemid & 0xffff)) {
-        win_skip("not on 16 bit menu subsystem\n");
-        DestroyMenu( hsubmenu);
-    } else {
-        gle = GetLastError();
-        ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, gle);
-        ok( gle == 0 ||
-                broken( gle == 0xdeadbeef), /* win2k0 */
-                "Last error is %d\n", gle);
-        /* then destroy the sub-popup */
-        ret = DestroyMenu( hsubmenu);
-        ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
-        /* and repeat the tests */
-        mii.fMask = MIIM_SUBMENU;
-        ret = GetMenuItemInfoA( hmenu, 0, TRUE, &mii);
-        ok( ret, "GetMenuItemInfo failed error %d\n", GetLastError());
-        /* GetMenuInfo fails now */
-        ok( mii.hSubMenu == hsubmenu, "submenu is %p\n", mii.hSubMenu);
-        mi.fMask |= MIM_STYLE;
-        ret = pGetMenuInfo( hsubmenu, &mi);
-        ok( !ret , "GetMenuInfo should have failed\n");
-        /* IsMenu says it is not */
-        ret = IsMenu( hsubmenu);
-        ok( !ret , "Menu handle should be invalid\n");
-        /* but TrackPopupMenu still works! */
-        SetLastError( 0xdeadbeef);
-        ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
-        gle = GetLastError();
-        todo_wine {
-            ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, gle);
-        }
-        ok( gle == 0 ||
-            broken(gle == 0xdeadbeef) || /* wow64 */
-            broken(gle == ERROR_INVALID_PARAMETER), /* win2k0 */
-            "Last error is %d\n", gle);
+    ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, GetLastError());
+
+    /* then destroy the sub-popup */
+    ret = DestroyMenu( hsubmenu);
+    ok(ret, "DestroyMenu failed with error %d\n", GetLastError());
+    /* and repeat the tests */
+    mii.fMask = MIIM_SUBMENU;
+    ret = GetMenuItemInfoA( hmenu, 0, TRUE, &mii);
+    ok( ret, "GetMenuItemInfo failed error %d\n", GetLastError());
+    /* GetMenuInfo fails now */
+    ok( mii.hSubMenu == hsubmenu, "submenu is %p\n", mii.hSubMenu);
+    mi.fMask |= MIM_STYLE;
+    ret = GetMenuInfo( hsubmenu, &mi);
+    ok( !ret , "GetMenuInfo should have failed\n");
+    /* IsMenu says it is not */
+    ret = IsMenu( hsubmenu);
+    ok( !ret , "Menu handle should be invalid\n");
+
+    /* but TrackPopupMenu still works! */
+    ret = TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
+    todo_wine {
+        ok( ret == itemid , "TrackPopupMenu returned %d error is %d\n", ret, GetLastError());
     }
+
     /* clean up */
     DestroyMenu( hmenu);
     DestroyWindow(hwnd);
@@ -741,7 +726,7 @@ static void test_menu_ownerdraw(void)
             ok( ret, "AppendMenu failed for %d\n", k-1);
         }
     MOD_maxid = k-1;
-    assert( k <= sizeof(MOD_rc)/sizeof(RECT));
+    assert( k <= ARRAY_SIZE(MOD_rc));
     /* display the menu */
     TrackPopupMenu( hmenu, TPM_RETURNCMD, 100,100, 0, hwnd, NULL);
 
@@ -873,9 +858,9 @@ static void test_mbs_help( int ispop, int hassub, int mnuopt,
     if( mnuopt) {
         mi.cbSize = sizeof(mi);
         mi.fMask = MIM_STYLE;
-        pGetMenuInfo( hmenu, &mi);
+        GetMenuInfo( hmenu, &mi);
         if( mnuopt) mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
-        ret = pSetMenuInfo( hmenu, &mi);
+        ret = SetMenuInfo( hmenu, &mi);
         ok( ret, "SetMenuInfo failed with error %d\n", GetLastError());
     }
     ret = InsertMenuItemA( hmenu, 0, FALSE, &mii);
@@ -992,12 +977,6 @@ static void test_menu_bmp_and_string(void)
     int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
     BOOL got;
 
-    if( !pGetMenuInfo)
-    {
-        win_skip("GetMenuInfo is not available\n");
-        return;
-    }
-
     memset( bmfill, 0xcc, sizeof( bmfill));
     hwnd = CreateWindowExA(0, (LPCSTR)MAKEINTATOM(atomMenuCheckClass), NULL, WS_SYSMENU |
                           WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
@@ -1012,7 +991,7 @@ static void test_menu_bmp_and_string(void)
     ok( hsysmenu != NULL, "GetSystemMenu failed with error %d\n", GetLastError());
     mi.fMask = MIM_STYLE;
     mi.dwStyle = 0;
-    got = pGetMenuInfo( hsysmenu, &mi);
+    got = GetMenuInfo( hsysmenu, &mi);
     ok( got, "GetMenuInfo failed gle=%d\n", GetLastError());
     ok( MNS_CHECKORBMP == mi.dwStyle, "System Menu Style is %08x, without the bit %08x\n",
         mi.dwStyle, MNS_CHECKORBMP);
@@ -1065,14 +1044,14 @@ static void test_menu_bmp_and_string(void)
     for( ispop=1; ispop >= 0; ispop--){
         static SIZE bmsizes[]= {
             {10,10},{38,38},{1,30},{55,5}};
-        for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
+        for( szidx=0; szidx < ARRAY_SIZE(bmsizes); szidx++) {
             HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
             HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, HBMMENU_POPUP_CLOSE, NULL  };
             ok( hbm != 0, "CreateBitmap failed err %d\n", GetLastError());
-            for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
+            for( txtidx = 0; txtidx < ARRAY_SIZE(MOD_txtsizes); txtidx++) {
                 for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
                     for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
-                        for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
+                        for( bmpidx = 0; bmpidx <ARRAY_SIZE(bitmaps); bmpidx++) {
                             /* no need to test NULL bitmaps of several sizes */
                             if( !bitmaps[bmpidx] && szidx > 0) continue;
                             /* the HBMMENU_POPUP not to test for menu bars */
@@ -1149,15 +1128,9 @@ static void test_menu_add_string( void )
     ok (GetMenuStringA( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
     ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
 
-    SetLastError(0xdeadbeef);
     ret = GetMenuStringW( hmenu, 0, strbackW, 99, MF_BYPOSITION );
-    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-        win_skip("GetMenuStringW is not implemented\n");
-    else
-    {
-        ok (ret, "GetMenuStringW on ownerdraw entry failed\n");
-        ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
-    }
+    ok (ret, "GetMenuStringW on ownerdraw entry failed\n");
+    ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
 
     /* Just try some invalid parameter tests */
     SetLastError(0xdeadbeef);
@@ -1225,12 +1198,8 @@ static void test_menu_add_string( void )
     ok (rc, "InsertMenuItem failed\n");
     ok (!GetMenuStringA( hmenu, 0, NULL, 0, MF_BYPOSITION),
             "GetMenuString on ownerdraw entry succeeded.\n");
-    SetLastError(0xdeadbeef);
     ret = GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION);
-    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-        win_skip("GetMenuStringW is not implemented\n");
-    else
-        ok (!ret, "GetMenuStringW on ownerdraw entry succeeded.\n");
+    ok (!ret, "GetMenuStringW on ownerdraw entry succeeded.\n");
 
     DestroyMenu( hmenu );
 }
@@ -1823,7 +1792,7 @@ static void test_menu_iteminfo( void )
        Only the low word of the dwTypeData is used.
        Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
-                (HMENU)MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
+                (HMENU)MAKELPARAM(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
     TMII_GMII ( MIIM_TYPE, 80,
         MFT_BITMAP | MFT_RIGHTJUSTIFY, 0, 0, 0, 0, 0, 0, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE,
         NULL, OK, OK );
@@ -1869,7 +1838,7 @@ static void test_menu_iteminfo( void )
        Only the low word of the dwTypeData is used.
        Use a magic bitmap here (Word 95 uses this to create its MDI menu buttons) */
     TMII_INSMI( MIIM_TYPE, MFT_BITMAP | MFT_RIGHTJUSTIFY, -1, -1, 0, 0, 0, -1,
-                (HMENU)MAKELONG(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
+                (HMENU)MAKELPARAM(HBMMENU_MBAR_CLOSE, 0x1234), -1, 0, OK );
     TMII_GMII ( MIIM_TYPE, 80,
         MFT_BITMAP | MFT_RIGHTJUSTIFY, 0, 0, 0, 0, 0, 0, HBMMENU_MBAR_CLOSE, 0, HBMMENU_MBAR_CLOSE,
         NULL, OK, OK );
@@ -2277,16 +2246,16 @@ static struct menu_mouse_tests_s {
     { INPUT_KEYBOARD, {{0}}, {VK_F10, 0}, TRUE, FALSE },
     { INPUT_KEYBOARD, {{0}}, {VK_F10, 0}, FALSE, FALSE },
 
-    { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, TRUE }, /* test 20 */
+    { INPUT_MOUSE, {{1, 2}, {0}}, {0}, TRUE, FALSE }, /* test 20 */
     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
-    { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, TRUE },
+    { INPUT_MOUSE, {{1, 0}, {0}}, {0}, TRUE, FALSE },
     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
-    { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, TRUE },
+    { INPUT_MOUSE, {{1, 0}, {2, 2}, {0}}, {0}, TRUE, FALSE },
     { INPUT_MOUSE, {{2, 1}, {0}}, {0}, FALSE, FALSE },
-    { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
+    { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, FALSE },
     { INPUT_MOUSE, {{3, 0}, {0}}, {0}, FALSE, FALSE },
-    { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, TRUE },
-    { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, TRUE },
+    { INPUT_MOUSE, {{1, 0}, {2, 0}, {0}}, {0}, TRUE, FALSE },
+    { INPUT_MOUSE, {{3, 1}, {0}}, {0}, TRUE, FALSE },
     { INPUT_MOUSE, {{1, 1}, {0}}, {0}, FALSE, FALSE },
     { -1 }
 };
@@ -2298,7 +2267,7 @@ static void send_key(WORD wVk)
     i[0].type = i[1].type = INPUT_KEYBOARD;
     i[0].u.ki.wVk = i[1].u.ki.wVk = wVk;
     i[1].u.ki.dwFlags = KEYEVENTF_KEYUP;
-    pSendInput(2, (INPUT *) i, sizeof(INPUT));
+    SendInput(2, (INPUT *) i, sizeof(INPUT));
 }
 
 static BOOL click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
@@ -2323,7 +2292,7 @@ static BOOL click_menu(HANDLE hWnd, struct menu_item_pair_s *mi)
     i[0].u.mi.dwFlags |= MOUSEEVENTF_MOVE;
     i[1].u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
     i[2].u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
-    ret = pSendInput(3, (INPUT *) i, sizeof(INPUT));
+    ret = SendInput(3, (INPUT *) i, sizeof(INPUT));
 
     /* hack to prevent mouse message buildup in Wine */
     while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
@@ -2340,7 +2309,7 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
     for (i = 0; menu_tests[i].type != -1; i++)
     {
         BOOL ret = TRUE;
-        int elapsed = 0;
+        int elapsed;
 
         got_input = i && menu_tests[i-1].bMenuVisible;
 
@@ -2349,7 +2318,16 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
                 send_key(menu_tests[i].wVk[j]);
         else
             for (j = 0; menu_tests[i].menu_item_pairs[j].uMenu != 0; j++)
-                if (!(ret = click_menu(hWnd, &menu_tests[i].menu_item_pairs[j]))) break;
+            {
+                /* Maybe clicking too fast before menu is initialized. Sleep 100 ms and retry */
+                elapsed = 0;
+                while (!(ret = click_menu(hWnd, &menu_tests[i].menu_item_pairs[j])))
+                {
+                    if (elapsed > 1000) break;
+                    elapsed += 100;
+                    Sleep(100);
+                }
+            }
 
         if (!ret)
         {
@@ -2357,6 +2335,8 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
             PostMessageA( hWnd, WM_CANCELMODE, 0, 0 );
             return 0;
         }
+
+        elapsed = 0;
         while (menu_tests[i].bMenuVisible != bMenuVisible)
         {
             if (elapsed > 200)
@@ -2381,6 +2361,11 @@ static DWORD WINAPI test_menu_input_thread(LPVOID lpParameter)
 static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
         LPARAM lParam)
 {
+    MENUBARINFO mbi;
+    HMENU hmenu;
+    UINT state;
+    BOOL br;
+
     switch (msg) {
         case WM_ENTERMENULOOP:
             bMenuVisible = TRUE;
@@ -2405,47 +2390,36 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam,
             return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
     }
 
-    if(pGetMenuBarInfo)
-    {
-        MENUBARINFO mbi;
-        HMENU hmenu;
-        UINT state;
-        BOOL br;
-
-        mbi.cbSize = sizeof(MENUBARINFO);
-
-        /* get info for the menu */
-        br = pGetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
-        ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
-        hmenu = GetMenu(hWnd);
-        ok(!mbi.hwndMenu, "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected NULL\n",
-                msg, mbi.hwndMenu);
-        ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
-                msg, mbi.hMenu, hmenu);
-        ok(!bMenuVisible == !mbi.fBarFocused, "msg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
-                msg, mbi.fBarFocused != 0);
-        ok(!bMenuVisible == !mbi.fFocused, "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n",
-                msg, mbi.fFocused != 0);
-
-        /* get info for the menu's first item */
-        br = pGetMenuBarInfo(hWnd, OBJID_MENU, 1, &mbi);
-        ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
-        state = GetMenuState(hmenu, 0, MF_BYPOSITION);
-        if (pGetMenuInfo) /* Skip on NT */
-        {
-            /* Native returns handle to destroyed window */
-            todo_wine_if (msg==WM_UNINITMENUPOPUP && popmenu==1)
-                ok(!mbi.hwndMenu == !popmenu,
-                        "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected %sNULL\n",
-                        msg, mbi.hwndMenu, popmenu ? "not " : "");
-        }
-        ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
-                msg, mbi.hMenu, hmenu);
-        ok(!bMenuVisible == !mbi.fBarFocused, "nsg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
-                msg, mbi.fBarFocused != 0);
-        ok(!(bMenuVisible && (state & MF_HILITE)) == !mbi.fFocused,
-                "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n", msg, mbi.fFocused != 0);
-    }
+    mbi.cbSize = sizeof(MENUBARINFO);
+
+    /* get info for the menu */
+    br = GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
+    ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
+    hmenu = GetMenu(hWnd);
+    ok(!mbi.hwndMenu, "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected NULL\n",
+            msg, mbi.hwndMenu);
+    ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
+            msg, mbi.hMenu, hmenu);
+    ok(!bMenuVisible == !mbi.fBarFocused, "msg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
+            msg, mbi.fBarFocused != 0);
+    ok(!bMenuVisible == !mbi.fFocused, "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n",
+            msg, mbi.fFocused != 0);
+
+    /* get info for the menu's first item */
+    br = GetMenuBarInfo(hWnd, OBJID_MENU, 1, &mbi);
+    ok(br, "msg %x: GetMenuBarInfo failed\n", msg);
+    state = GetMenuState(hmenu, 0, MF_BYPOSITION);
+    /* Native returns handle to destroyed window */
+    todo_wine_if (msg==WM_UNINITMENUPOPUP && popmenu==1)
+        ok(!mbi.hwndMenu == !popmenu,
+            "msg %x: GetMenuBarInfo.hwndMenu wrong: %p expected %sNULL\n",
+            msg, mbi.hwndMenu, popmenu ? "not " : "");
+    ok(mbi.hMenu == hmenu, "msg %x: GetMenuBarInfo got wrong menu: %p expected %p\n",
+            msg, mbi.hMenu, hmenu);
+    ok(!bMenuVisible == !mbi.fBarFocused, "nsg %x: GetMenuBarInfo.fBarFocused (%d) is wrong\n",
+            msg, mbi.fBarFocused != 0);
+    ok(!(bMenuVisible && (state & MF_HILITE)) == !mbi.fFocused,
+            "msg %x: GetMenuBarInfo.fFocused (%d) is wrong\n", msg, mbi.fFocused != 0);
 
     if (msg == WM_EXITMENULOOP)
         bMenuVisible = FALSE;
@@ -2465,12 +2439,6 @@ static void test_menu_input(void) {
     ATOM aclass;
     POINT orig_pos;
 
-    if (!pSendInput)
-    {
-        win_skip("SendInput is not available\n");
-        return;
-    }
-
     wclass.lpszClassName = "MenuTestClass";
     wclass.style         = CS_HREDRAW | CS_VREDRAW;
     wclass.lpfnWndProc   = WndProc;
@@ -2858,9 +2826,8 @@ static void test_menu_resource_layout(void)
     ok(ret, "AppendMenu failed\n");
 
     count = GetMenuItemCount(hmenu);
-    ok(count == sizeof(menu_data)/sizeof(menu_data[0]),
-       "expected %u menu items, got %u\n",
-       (UINT)(sizeof(menu_data)/sizeof(menu_data[0])), count);
+    ok(count == ARRAY_SIZE(menu_data), "expected %u menu items, got %u\n",
+       (UINT) ARRAY_SIZE(menu_data), count);
 
     for (i = 0; i < count; i++)
     {
@@ -3079,9 +3046,9 @@ static void test_InsertMenu(void)
     };
     HMENU hmenu;
 
-#define create_menu(a) create_menu_from_data((a), sizeof(a)/sizeof((a)[0]))
-#define create_menuitem(a) create_menuitem_from_data((a), sizeof(a)/sizeof((a)[0]))
-#define compare_menu(h, a) compare_menu_data((h), (a), sizeof(a)/sizeof((a)[0]))
+#define create_menu(a) create_menu_from_data((a), ARRAY_SIZE(a))
+#define create_menuitem(a) create_menuitem_from_data((a), ARRAY_SIZE(a))
+#define compare_menu(h, a) compare_menu_data((h), (a), ARRAY_SIZE(a))
 
     hmenu = create_menu(in1);
     compare_menu(hmenu, out1);
@@ -3128,18 +3095,12 @@ static void test_menu_getmenuinfo(void)
     BOOL ret;
     DWORD gle;
 
-    if (!pGetMenuInfo)
-    {
-        win_skip("GetMenuInfo is not available\n");
-        return;
-    }
-
     /* create a menu */
     hmenu = CreateMenu();
     assert( hmenu);
     /* test some parameter errors */
     SetLastError(0xdeadbeef);
-    ret = pGetMenuInfo( hmenu, NULL);
+    ret = GetMenuInfo( hmenu, NULL);
     gle= GetLastError();
     ok( !ret, "GetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3147,7 +3108,7 @@ static void test_menu_getmenuinfo(void)
         "GetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
     SetLastError(0xdeadbeef);
     mi.cbSize = 0;
-    ret = pGetMenuInfo( hmenu, &mi);
+    ret = GetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( !ret, "GetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3155,13 +3116,13 @@ static void test_menu_getmenuinfo(void)
         "GetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
-    ret = pGetMenuInfo( hmenu, &mi);
+    ret = GetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
     SetLastError(0xdeadbeef);
     mi.cbSize = 0;
-    ret = pGetMenuInfo( NULL, &mi);
+    ret = GetMenuInfo( NULL, &mi);
     gle= GetLastError();
     ok( !ret, "GetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3181,12 +3142,6 @@ static void test_menu_setmenuinfo(void)
     DWORD gle;
     HBRUSH brush;
 
-    if (!pGetMenuInfo || !pSetMenuInfo)
-    {
-        win_skip("Get/SetMenuInfo are not available\n");
-        return;
-    }
-
     /* create a menu with a submenu */
     hmenu = CreateMenu();
     hsubmenu = CreateMenu();
@@ -3197,7 +3152,7 @@ static void test_menu_setmenuinfo(void)
     ok( ret, "InsertMenuItem failed with error %d\n", GetLastError());
     /* test some parameter errors */
     SetLastError(0xdeadbeef);
-    ret = pSetMenuInfo( hmenu, NULL);
+    ret = SetMenuInfo( hmenu, NULL);
     gle= GetLastError();
     ok( !ret, "SetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3205,7 +3160,7 @@ static void test_menu_setmenuinfo(void)
         "SetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
     SetLastError(0xdeadbeef);
     mi.cbSize = 0;
-    ret = pSetMenuInfo( hmenu, &mi);
+    ret = SetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( !ret, "SetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3213,13 +3168,13 @@ static void test_menu_setmenuinfo(void)
         "SetMenuInfo() error got %u expected %u\n", gle, ERROR_INVALID_PARAMETER);
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
-    ret = pSetMenuInfo( hmenu, &mi);
+    ret = SetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "SetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "SetMenuInfo() error got %u\n", gle);
     SetLastError(0xdeadbeef);
     mi.cbSize = 0;
-    ret = pSetMenuInfo( NULL, &mi);
+    ret = SetMenuInfo( NULL, &mi);
     gle= GetLastError();
     ok( !ret, "SetMenuInfo() should have failed\n");
     ok( gle == ERROR_INVALID_PARAMETER ||
@@ -3230,7 +3185,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hmenu, &mi);
+    ret = GetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3238,7 +3193,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hsubmenu, &mi);
+    ret = GetMenuInfo( hsubmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3248,7 +3203,7 @@ static void test_menu_setmenuinfo(void)
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE | MIM_APPLYTOSUBMENUS;
     mi.dwStyle = MNS_CHECKORBMP;
-    ret = pSetMenuInfo( hmenu, &mi);
+    ret = SetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "SetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "SetMenuInfo() error got %u\n", gle);
@@ -3256,7 +3211,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hmenu, &mi);
+    ret = GetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3264,7 +3219,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hsubmenu, &mi);
+    ret = GetMenuInfo( hsubmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3274,7 +3229,7 @@ static void test_menu_setmenuinfo(void)
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE ;
     mi.dwStyle = MNS_NOCHECK;
-    ret = pSetMenuInfo( hmenu, &mi);
+    ret = SetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "SetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "SetMenuInfo() error got %u\n", gle);
@@ -3282,7 +3237,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hmenu, &mi);
+    ret = GetMenuInfo( hmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3290,7 +3245,7 @@ static void test_menu_setmenuinfo(void)
     SetLastError(0xdeadbeef);
     mi.cbSize = sizeof( MENUINFO);
     mi.fMask = MIM_STYLE;
-    ret = pGetMenuInfo( hsubmenu, &mi);
+    ret = GetMenuInfo( hsubmenu, &mi);
     gle= GetLastError();
     ok( ret, "GetMenuInfo() should have succeeded\n");
     ok( gle == 0xdeadbeef, "GetMenuInfo() error got %u\n", gle);
@@ -3299,23 +3254,23 @@ static void test_menu_setmenuinfo(void)
     /* test background brush */
     mi.cbSize = sizeof(mi);
     mi.fMask = MIM_BACKGROUND;
-    ret = pGetMenuInfo( hmenu, &mi );
+    ret = GetMenuInfo( hmenu, &mi );
     ok( ret, "GetMenuInfo() should have succeeded\n" );
     ok( mi.hbrBack == NULL, "got %p\n", mi.hbrBack );
 
     brush = CreateSolidBrush( RGB(0xff, 0, 0) );
     mi.hbrBack = brush;
-    ret = pSetMenuInfo( hmenu, &mi );
+    ret = SetMenuInfo( hmenu, &mi );
     ok( ret, "SetMenuInfo() should have succeeded\n" );
     mi.hbrBack = NULL;
-    ret = pGetMenuInfo( hmenu, &mi );
+    ret = GetMenuInfo( hmenu, &mi );
     ok( ret, "GetMenuInfo() should have succeeded\n" );
     ok( mi.hbrBack == brush, "got %p original %p\n", mi.hbrBack, brush );
 
     mi.hbrBack = NULL;
-    ret = pSetMenuInfo( hmenu, &mi );
+    ret = SetMenuInfo( hmenu, &mi );
     ok( ret, "SetMenuInfo() should have succeeded\n" );
-    ret = pGetMenuInfo( hmenu, &mi );
+    ret = GetMenuInfo( hmenu, &mi );
     ok( ret, "GetMenuInfo() should have succeeded\n" );
     ok( mi.hbrBack == NULL, "got %p\n", mi.hbrBack );
     DeleteObject( brush );
@@ -3682,13 +3637,13 @@ static void test_menualign(void)
     ok( hbm1 && hbm2 && hbm3, "Creating bitmaps failed\n");
     menu = CreatePopupMenu();
     ok( menu != NULL, "CreatePopupMenu() failed\n");
-    if( pGetMenuInfo) {
-        mi.fMask = MIM_STYLE;
-        ret = pGetMenuInfo( menu, &mi);
-        ok( ret, "GetMenuInfo failed: %d\n", GetLastError());
-        ok( menu != NULL, "GetMenuInfo() failed\n");
-        ok( 0 == mi.dwStyle, "menuinfo style is %x\n", mi.dwStyle);
-    }
+
+    mi.fMask = MIM_STYLE;
+    ret = GetMenuInfo( menu, &mi);
+    ok( ret, "GetMenuInfo failed: %d\n", GetLastError());
+    ok( menu != NULL, "GetMenuInfo() failed\n");
+    ok( 0 == mi.dwStyle, "menuinfo style is %x\n", mi.dwStyle);
+
     /* test 1 */
     mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_ID;
     mii.wID = 1;
@@ -4298,28 +4253,22 @@ if (0) /* FIXME: uncomment once Wine is fixed */ {
 
 START_TEST(menu)
 {
-    init_function_pointers();
     register_menu_check_class();
 
-    /* Wine defines MENUITEMINFO for W2K and above. NT4 and below can't
-     * handle that.
-     */
-    if (correct_behavior())
-    {
-        test_menu_add_string();
-        test_menu_iteminfo();
-        test_menu_search_bycommand();
-        test_CheckMenuRadioItem();
-        test_menu_resource_layout();
-        test_InsertMenu();
-        test_menualign();
-        test_system_menu();
-    }
+    test_menu_add_string();
+    test_menu_iteminfo();
+    test_menu_search_bycommand();
+    test_CheckMenuRadioItem();
+    test_menu_resource_layout();
+    test_InsertMenu();
+    test_menualign();
+    test_system_menu();
 
     test_menu_locked_by_window();
     test_subpopup_locked_by_menu();
     test_menu_ownerdraw();
     test_getmenubarinfo();
+    test_GetMenuItemRect();
     test_menu_bmp_and_string();
     test_menu_getmenuinfo();
     test_menu_setmenuinfo();
index 886d63c..26ee28a 100644 (file)
@@ -23,6 +23,8 @@
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winreg.h"
+#include <stdio.h>
 
 static HMODULE hdll;
 static LONG (WINAPI *pChangeDisplaySettingsExA)(LPCSTR, LPDEVMODEA, HWND, DWORD, LPVOID);
@@ -74,12 +76,154 @@ static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc,
     return TRUE;
 }
 
+static int adapter_count = 0;
+static int monitor_count = 0;
+
+static void test_enumdisplaydevices_adapter(int index, const DISPLAY_DEVICEA *device, DWORD flags)
+{
+    char video_name[32];
+    char video_value[128];
+    char buffer[128];
+    int number;
+    int vendor_id;
+    int device_id;
+    int subsys_id;
+    int revision_id;
+    size_t length;
+    HKEY hkey;
+    HDC hdc;
+    DWORD size;
+    LSTATUS ls;
+
+    adapter_count++;
+
+    /* DeviceName */
+    ok(sscanf(device->DeviceName, "\\\\.\\DISPLAY%d", &number) == 1, "#%d: wrong DeviceName %s\n", index,
+       device->DeviceName);
+
+    /* DeviceKey */
+    /* win7 is the only OS version where \Device\Video? value in HLKM\HARDWARE\DEVICEMAP\VIDEO are not in order with adapter index. */
+    if (GetVersion() != 0x1db10106 || !strcmp(winetest_platform, "wine"))
+    {
+        sprintf(video_name, "\\Device\\Video%d", index);
+        ls = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkey);
+        ok(!ls, "#%d: failed to open registry, error: %#x\n", index, ls);
+        if (!ls)
+        {
+            memset(video_value, 0, sizeof(video_value));
+            size = sizeof(video_value);
+            ls = RegQueryValueExA(hkey, video_name, NULL, NULL, (unsigned char *)video_value, &size);
+            ok(!ls, "#%d: failed to get registry value, error: %#x\n", index, ls);
+            RegCloseKey(hkey);
+            ok(!strcmp(video_value, device->DeviceKey), "#%d: wrong DeviceKey: %s\n", index, device->DeviceKey);
+        }
+    }
+    else
+        ok(sscanf(device->DeviceKey, "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Video\\%[^\\]\\%04d", buffer, &number) == 2,
+           "#%d: wrong DeviceKey %s\n", index, device->DeviceKey);
+
+    /* DeviceString */
+    length = strlen(device->DeviceString);
+    ok(broken(length == 0) || /* XP on Testbot will return an empty string, whereas XP on real machine doesn't. Probably a bug in virtual adapter driver */
+       length > 0, "#%d: expect DeviceString not empty\n", index);
+
+    /* StateFlags */
+    if (index == 0)
+        ok(device->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE, "#%d: adapter should be primary\n", index);
+    else
+        ok(!(device->StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE), "#%d: adapter should not be primary\n", index);
+
+    if (device->StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
+    {
+        /* Test creating DC */
+        hdc = CreateDCA(device->DeviceName, NULL, NULL, NULL);
+        ok(hdc != NULL, "#%d: failed to CreateDC(\"%s\") err=%d\n", index, device->DeviceName, GetLastError());
+        DeleteDC(hdc);
+    }
+
+    /* DeviceID */
+    /* DeviceID should equal to the first string of HardwareID value data in PCI GPU instance. You can verify this
+     * by changing the data and rerun EnumDisplayDevices. But it's difficult to find corresponding PCI device on
+     * userland. So here we check the expected format instead. */
+    if (flags & EDD_GET_DEVICE_INTERFACE_NAME)
+        ok(strlen(device->DeviceID) == 0 || /* vista+ */
+           sscanf(device->DeviceID, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",
+                  &vendor_id, &device_id, &subsys_id, &revision_id) == 4, /* XP/2003 ignores EDD_GET_DEVICE_INTERFACE_NAME */
+           "#%d: got %s\n", index, device->DeviceID);
+    else
+    {
+        ok(broken(strlen(device->DeviceID) == 0) || /* XP on Testbot returns an empty string, whereas real machine doesn't */
+           sscanf(device->DeviceID, "PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X", &vendor_id, &device_id, &subsys_id,
+                  &revision_id) == 4, "#%d: wrong DeviceID %s\n", index, device->DeviceID);
+    }
+}
+
+static void test_enumdisplaydevices_monitor(int adapter_index, int monitor_index, const char *adapter_name,
+                                            const DISPLAY_DEVICEA *device, DWORD flags)
+{
+    static const char device_id_prefix[] = "MONITOR\\Default_Monitor\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\";
+    static const char device_key_prefix[] = "\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class"
+                                            "\\{4d36e96e-e325-11ce-bfc1-08002be10318}\\";
+    char monitor_name[32];
+    char buffer[128];
+    int number;
+
+    monitor_count++;
+
+    /* DeviceName */
+    lstrcpyA(monitor_name, adapter_name);
+    sprintf(monitor_name + strlen(monitor_name), "\\Monitor%d", monitor_index);
+    ok(!strcmp(monitor_name, device->DeviceName), "#%d: expect %s, got %s\n", monitor_index, monitor_name, device->DeviceName);
+
+    /* DeviceString */
+    ok(strlen(device->DeviceString) > 0, "#%d: expect DeviceString not empty\n", monitor_index);
+
+    /* StateFlags */
+    if (adapter_index == 0 && monitor_index == 0)
+        ok(device->StateFlags & DISPLAY_DEVICE_ATTACHED, "#%d expect to have a primary monitor attached\n", monitor_index);
+    else
+        ok(device->StateFlags <= (DISPLAY_DEVICE_ATTACHED | DISPLAY_DEVICE_ACTIVE), "#%d wrong state %#x\n", monitor_index,
+           device->StateFlags);
+
+    /* DeviceID */
+    lstrcpynA(buffer, device->DeviceID, sizeof(device_id_prefix));
+    if (flags & EDD_GET_DEVICE_INTERFACE_NAME)
+    {   /* HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\Default_Monitor\4&2abfaa30&0&UID0 GUID_DEVINTERFACE_MONITOR
+         *                                                   ^                ^                     ^
+         * Expect format                  \\?\DISPLAY#Default_Monitor#4&2abfaa30&0&UID0#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7} */
+        ok(strlen(device->DeviceID) == 0 || /* vista ~ win7 */
+            sscanf(device->DeviceID, "\\\\?\\DISPLAY#Default_Monitor#%[^#]#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}", buffer) == 1 || /* win8+ */
+            (!lstrcmpiA(buffer, device_id_prefix) &&
+             sscanf(device->DeviceID + sizeof(device_id_prefix) - 1, "%04d", &number) == 1), /* XP/2003 ignores EDD_GET_DEVICE_INTERFACE_NAME */
+            "#%d: wrong DeviceID : %s\n", monitor_index, device->DeviceID);
+    }
+    else
+    {
+        /* Expect HarewareID value data + Driver value data in HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\Default_Monitor\{Instance} */
+        /* But we don't know which monitor instance this belongs to, so check format instead */
+        ok(!lstrcmpiA(buffer, device_id_prefix), "#%d wrong DeviceID : %s\n", monitor_index, device->DeviceID);
+        ok(sscanf(device->DeviceID + sizeof(device_id_prefix) - 1, "%04d", &number) == 1,
+           "#%d wrong DeviceID : %s\n", monitor_index, device->DeviceID);
+    }
+
+    /* DeviceKey */
+    lstrcpynA(buffer, device->DeviceKey, sizeof(device_key_prefix));
+    ok(!lstrcmpiA(buffer, device_key_prefix), "#%d: wrong DeviceKey : %s\n", monitor_index, device->DeviceKey);
+    ok(sscanf(device->DeviceKey + sizeof(device_key_prefix) - 1, "%04d", &number) == 1,
+       "#%d wrong DeviceKey : %s\n", monitor_index, device->DeviceKey);
+}
+
 static void test_enumdisplaydevices(void)
 {
+    static const DWORD flags[] = {0, EDD_GET_DEVICE_INTERFACE_NAME};
     DISPLAY_DEVICEA dd;
     char primary_device_name[32];
     char primary_monitor_device_name[32];
-    DWORD primary_num = -1, num = 0;
+    char adapter_name[32];
+    int number;
+    int flag_index;
+    int adapter_index;
+    int monitor_index;
     BOOL ret;
 
     if (!pEnumDisplayDevicesA)
@@ -88,50 +232,50 @@ static void test_enumdisplaydevices(void)
         return;
     }
 
+    /* Doesn't accept \\.\DISPLAY */
     dd.cb = sizeof(dd);
-    for (num = 0;; num++)
-    {
-        HDC dc;
-        ret = pEnumDisplayDevicesA(NULL, num, &dd, 0);
-        if(!ret) break;
+    ret = pEnumDisplayDevicesA("\\\\.\\DISPLAY", 0, &dd, 0);
+    ok(!ret, "Expect failure\n");
 
-        if(dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
-        {
-            strcpy(primary_device_name, dd.DeviceName);
-            primary_num = num;
-        }
-        if(dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
+    /* Enumeration */
+    for (flag_index = 0; flag_index < ARRAY_SIZE(flags); flag_index++)
+        for (adapter_index = 0; pEnumDisplayDevicesA(NULL, adapter_index, &dd, flags[flag_index]); adapter_index++)
         {
-            /* test creating DC */
-            dc = CreateDCA(dd.DeviceName, NULL, NULL, NULL);
-            ok(dc != NULL, "Failed to CreateDC(\"%s\") err=%d\n", dd.DeviceName, GetLastError());
-            DeleteDC(dc);
+            lstrcpyA(adapter_name, dd.DeviceName);
+
+            if (sscanf(adapter_name, "\\\\.\\DISPLAYV%d", &number) == 1)
+            {
+                skip("Skipping software devices %s:%s\n", adapter_name, dd.DeviceString);
+                continue;
+            }
+
+            test_enumdisplaydevices_adapter(adapter_index, &dd, flags[flag_index]);
+
+            for (monitor_index = 0; pEnumDisplayDevicesA(adapter_name, monitor_index, &dd, flags[flag_index]);
+                 monitor_index++)
+                test_enumdisplaydevices_monitor(adapter_index, monitor_index, adapter_name, &dd, flags[flag_index]);
         }
-    }
 
-    if (primary_num == -1 || !pEnumDisplayMonitors || !pGetMonitorInfoA)
+    ok(adapter_count > 0, "Expect at least one adapter found\n");
+    /* XP on Testbot doesn't report a monitor, whereas XP on real machine does */
+    ok(broken(monitor_count == 0) || monitor_count > 0, "Expect at least one monitor found\n");
+
+    if (!pEnumDisplayMonitors || !pGetMonitorInfoA)
     {
         win_skip("EnumDisplayMonitors or GetMonitorInfoA are not available\n");
         return;
     }
 
+    ret = pEnumDisplayDevicesA(NULL, 0, &dd, 0);
+    ok(ret, "Expect success\n");
+    lstrcpyA(primary_device_name, dd.DeviceName);
+
     primary_monitor_device_name[0] = 0;
     ret = pEnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM)primary_monitor_device_name);
     ok(ret, "EnumDisplayMonitors failed\n");
     ok(!strcmp(primary_monitor_device_name, primary_device_name),
        "monitor device name %s, device name %s\n", primary_monitor_device_name,
        primary_device_name);
-
-    dd.cb = sizeof(dd);
-    for (num = 0;; num++)
-    {
-        ret = pEnumDisplayDevicesA(primary_device_name, num, &dd, 0);
-        if (!ret) break;
-
-        dd.DeviceID[63] = 0;
-        ok(!strcasecmp(dd.DeviceID, "Monitor\\Default_Monitor\\{4D36E96E-E325-11CE-BFC1-08002BE10318}\\"),
-           "DeviceID \"%s\" does not start with \"Monitor\\Default_Monitor\\...\" prefix\n", dd.DeviceID);
-    }
 }
 
 struct vid_mode
@@ -160,7 +304,6 @@ static const struct vid_mode vid_modes_test[] = {
     {0, 0, 0, 0, DM_DISPLAYFREQUENCY, 0}
     */
 };
-#define vid_modes_cnt (sizeof(vid_modes_test) / sizeof(vid_modes_test[0]))
 
 static void test_ChangeDisplaySettingsEx(void)
 {
@@ -247,7 +390,7 @@ static void test_ChangeDisplaySettingsEx(void)
     memset(&dm, 0, sizeof(dm));
     dm.dmSize = sizeof(dm);
 
-    for (i = 0; i < vid_modes_cnt; i++)
+    for (i = 0; i < ARRAY_SIZE(vid_modes_test); i++)
     {
         dm.dmPelsWidth        = vid_modes_test[i].w;
         dm.dmPelsHeight       = vid_modes_test[i].h;
@@ -409,7 +552,7 @@ static void test_monitors(void)
 
     /* tests for cbSize in MONITORINFO */
     monitor = pMonitorFromWindow( 0, MONITOR_DEFAULTTOPRIMARY );
-    for (i = 0; i < (sizeof(testdatami) / sizeof(testdatami[0])); i++)
+    for (i = 0; i < ARRAY_SIZE(testdatami); i++)
     {
         memset( &mi, 0, sizeof(mi) );
         mi.cbSize = testdatami[i].cbSize;
@@ -431,7 +574,7 @@ static void test_monitors(void)
     }
 
     /* tests for cbSize in MONITORINFOEXA */
-    for (i = 0; i < (sizeof(testdatamiexa) / sizeof(testdatamiexa[0])); i++)
+    for (i = 0; i < ARRAY_SIZE(testdatamiexa); i++)
     {
         memset( &miexa, 0, sizeof(miexa) );
         miexa.cbSize = testdatamiexa[i].cbSize;
@@ -444,7 +587,7 @@ static void test_monitors(void)
     }
 
     /* tests for cbSize in MONITORINFOEXW */
-    for (i = 0; i < (sizeof(testdatamiexw) / sizeof(testdatamiexw[0])); i++)
+    for (i = 0; i < ARRAY_SIZE(testdatamiexw); i++)
     {
         memset( &miexw, 0, sizeof(miexw) );
         miexw.cbSize = testdatamiexw[i].cbSize;
@@ -596,7 +739,7 @@ static void test_display_config(void)
     paths = modes = 100;
     ret = pGetDisplayConfigBufferSizes(0, &paths, &modes);
     ok(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED, "got %d\n", ret);
-    ok(modes == 0 && paths == 0, "got %u, %u\n", modes, paths);
+    ok((modes == 0 || modes == 100) && paths == 0, "got %u, %u\n", modes, paths);
 }
 
 START_TEST(monitor)
index 39ce152..e66d0dc 100644 (file)
@@ -569,6 +569,7 @@ static const struct message WmShowRestoreMaxOverlappedSeq[] = {
     { WM_GETTITLEBARINFOEX, sent|optional },
     { WM_NCPAINT, sent|beginpaint|optional },
     { WM_ERASEBKGND, sent|beginpaint|optional },
+    { WM_SYNCPAINT, sent|optional },
     { 0 }
 };
 /* ShowWindow(SW_RESTORE) for a not visible minimized overlapped window */
@@ -778,37 +779,6 @@ static const struct message WmCreateInvisibleMaxPopupSeq[] = {
     { 0 }
 };
 /* ShowWindow(SW_SHOWMAXIMIZED) for a resized not visible popup window */
-static const struct message WmShowMaxPopupResizedSeq_todo[] = {
-    { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
-    { WM_GETMINMAXINFO, sent },
-    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED },
-    { WM_NCCALCSIZE, sent|wparam, TRUE },
-    { 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|optional, SWP_NOSIZE|SWP_NOMOVE },
-    { WM_NCPAINT, sent|wparam|optional, 1 },
-    { WM_ERASEBKGND, sent|optional },
-    { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
-    { WM_ACTIVATEAPP, sent|wparam, 1 },
-    { WM_NCACTIVATE, sent },
-    { WM_ACTIVATE, sent|wparam, 1 },
-    { HCBT_SETFOCUS, hook },
-    { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
-    { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
-    { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
-    { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
-    { WM_GETTEXT, sent|optional },
-    { WM_NCPAINT, sent|wparam|optional, 1 },
-    { WM_ERASEBKGND, sent|optional },
-    { WM_WINDOWPOSCHANGED, sent },
-    /* WinNT4.0 sends WM_MOVE */
-    { WM_MOVE, sent|defwinproc|optional },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_MAXIMIZED },
-    { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
-    { 0 }
-};
 static const struct message WmShowMaxPopupResizedSeq[] = {
     { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
     { WM_GETMINMAXINFO, sent },
@@ -831,7 +801,7 @@ static const struct message WmShowMaxPopupResizedSeq[] = {
     { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
     { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
     { WM_GETTEXT, sent|optional },
-    { WM_NCPAINT, sent|optional }, /* We'll check WM_NCPAINT behaviour in another test */
+    { WM_NCPAINT, sent|wparam|optional, 1 },
     { WM_ERASEBKGND, sent|optional },
     { WM_WINDOWPOSCHANGED, sent },
     /* WinNT4.0 sends WM_MOVE */
@@ -870,6 +840,7 @@ static const struct message WmShowMaxPopupSeq[] = {
     { WM_ERASEBKGND, sent|defwinproc|optional },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+    { WM_SIZE, sent|defwinproc|optional },
     { 0 }
 };
 /* CreateWindow(WS_VISIBLE) for popup window */
@@ -3510,6 +3481,7 @@ static const struct message WmMaximizeMDIchildInvisibleSeq2[] = {
     { WM_MDIACTIVATE, sent|defwinproc|optional },
     { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+    { WM_SIZE, sent|defwinproc|optional },
     { 0 }
 };
 /* WM_MDIMAXIMIZE for an MDI child window with invisible parent */
@@ -4017,7 +3989,7 @@ static void test_mdi_messages(void)
     ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
 
     ShowWindow(mdi_child2, SW_MINIMIZE);
-    ok_sequence(WmMinimizeMDIchildVisibleSeq, "ShowWindow(SW_MINIMIZE):MDI child", TRUE);
+    ok_sequence(WmMinimizeMDIchildVisibleSeq, "ShowWindow(SW_MINIMIZE):MDI child", FALSE);
 
     ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
     ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
@@ -4756,18 +4728,6 @@ static void test_showwindow(void)
     DestroyWindow(hwnd);
     flush_sequence();
 
-    /* Test again, this time the NC_PAINT message */
-    hwnd = CreateWindowExA(0, "TestWindowClass", "Test popup", WS_POPUP | WS_MAXIMIZE,
-                           100, 100, 200, 200, 0, 0, 0, NULL);
-    ok (hwnd != 0, "Failed to create popup window\n");
-    SetWindowPos(hwnd, 0, 10, 10, 200, 200, SWP_NOZORDER | SWP_NOACTIVATE);
-    flush_sequence();
-    ShowWindow(hwnd, SW_SHOWMAXIMIZED);
-    ok_sequence(WmShowMaxPopupResizedSeq_todo,
-            "ShowWindow(SW_SHOWMAXIMIZED):invisible maximized and resized popup TODO", TRUE);
-    DestroyWindow(hwnd);
-    flush_sequence();
-
     /* Test 2:
      * 1. Create invisible maximized popup window.
      * 2. Show it maximized.
@@ -4817,6 +4777,39 @@ static void test_showwindow(void)
     flush_sequence();
 }
 
+static void test_recursive_activation(void)
+{
+    static const struct message seq[] =
+    {
+        { HCBT_ACTIVATE, hook },
+        { WM_NCACTIVATE, sent|wparam, TRUE },
+        { WM_ACTIVATE, sent|wparam, WA_ACTIVE },
+        { HCBT_ACTIVATE, hook },
+        { WM_NCACTIVATE, sent|wparam, FALSE },
+        { WM_ACTIVATE, sent|wparam, WA_INACTIVE },
+        { WM_SETFOCUS, sent|optional },
+        { 0 }
+    };
+    HWND hwnd, recursive;
+
+    hwnd = CreateWindowExA(0, "SimpleWindowClass", NULL, WS_OVERLAPPED|WS_VISIBLE,
+                              100, 100, 200, 200, 0, 0, 0, NULL);
+    ok(hwnd != 0, "Failed to create simple window\n");
+
+    recursive = CreateWindowExA(0, "RecursiveActivationClass", NULL, WS_OVERLAPPED|WS_VISIBLE,
+                                10, 10, 50, 50, hwnd, 0, 0, NULL);
+    ok(recursive != 0, "Failed to create recursive activation window\n");
+    SetActiveWindow(hwnd);
+
+    flush_sequence();
+    SetActiveWindow(recursive);
+    ok_sequence(seq, "Recursive Activation", FALSE);
+
+    DestroyWindow(recursive);
+    DestroyWindow(hwnd);
+    flush_sequence();
+}
+
 static void test_sys_menu(void)
 {
     HWND hwnd;
@@ -5066,7 +5059,7 @@ static void test_WM_DEVICECHANGE(HWND hwnd)
                                      DBT_DEVICETYPESPECIFIC,
                                      DBT_CUSTOMEVENT};
 
-    for (i = 0; i < sizeof(wparams)/sizeof(wparams[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(wparams); i++)
     {
         SetLastError(0xdeadbeef);
         ret = PostMessageA(hwnd, WM_DEVICECHANGE, wparams[i], 0);
@@ -5187,7 +5180,7 @@ static void test_messages(void)
 
     ShowWindow(hwnd, SW_MINIMIZE);
     flush_events();
-    ok_sequence(WmShowMinOverlappedSeq, "ShowWindow(SW_SHOWMINIMIZED):overlapped", TRUE);
+    ok_sequence(WmShowMinOverlappedSeq, "ShowWindow(SW_SHOWMINIMIZED):overlapped", FALSE);
     flush_sequence();
 
     if (GetWindowLongW( hwnd, GWL_STYLE ) & WS_MINIMIZE)
@@ -5343,7 +5336,7 @@ static void test_messages(void)
     hwnd = CreateWindowExA(0, "TestWindowClass", "Test Popup", WS_POPUP | WS_VISIBLE,
                              -10, -10, 10000, 10000, NULL, 0, 0, NULL );
     ok (hwnd != 0, "Failed to create popup window\n");
-    ok_sequence(WmShowPopupExtremeLocationSeq, "RedrawWindow:show_popup_extreme_location", TRUE);
+    ok_sequence(WmShowPopupExtremeLocationSeq, "RedrawWindow:show_popup_extreme_location", FALSE);
     DestroyWindow(hwnd);
 
 
@@ -6390,7 +6383,7 @@ static void test_button_messages(void)
     hfont2 = CreateFontIndirectA(&logfont);
     ok(hfont2 != NULL, "Failed to create Tahoma font\n");
 
-    for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(button); i++)
     {
         MSG msg;
         DWORD style, state;
@@ -6619,6 +6612,121 @@ static void test_button_messages(void)
     DestroyWindow(parent);
 }
 
+static void test_button_bm_get_set_image(void)
+{
+    HWND hwnd;
+    HDC hdc;
+    HBITMAP hbmp1x1;
+    HBITMAP hbmp2x2;
+    HBITMAP hmask2x2;
+    ICONINFO icon_info2x2;
+    HICON hicon2x2;
+    HBITMAP hbmp;
+    HICON hicon;
+    ICONINFO icon_info;
+    BITMAP bm;
+    DWORD default_style = BS_PUSHBUTTON | WS_TABSTOP | WS_POPUP | WS_VISIBLE;
+    LRESULT ret;
+
+    hdc = GetDC(0);
+    hbmp1x1 = CreateCompatibleBitmap(hdc, 1, 1);
+    hbmp2x2 = CreateCompatibleBitmap(hdc, 2, 2);
+    ZeroMemory(&bm, sizeof(bm));
+    ok(GetObjectW(hbmp1x1, sizeof(bm), &bm), "Expect GetObjectW() success\n");
+    ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
+       bm.bmWidth, bm.bmHeight);
+    ZeroMemory(&bm, sizeof(bm));
+    ok(GetObjectW(hbmp2x2, sizeof(bm), &bm), "Expect GetObjectW() success\n");
+    ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
+       bm.bmWidth, bm.bmHeight);
+
+    hmask2x2 = CreateCompatibleBitmap(hdc, 2, 2);
+    ZeroMemory(&icon_info2x2, sizeof(icon_info2x2));
+    icon_info2x2.fIcon = TRUE;
+    icon_info2x2.hbmMask = hmask2x2;
+    icon_info2x2.hbmColor = hbmp2x2;
+    hicon2x2 = CreateIconIndirect(&icon_info2x2);
+
+    ZeroMemory(&icon_info, sizeof(icon_info));
+    ok(GetIconInfo(hicon2x2, &icon_info), "Expect GetIconInfo() success\n");
+    ZeroMemory(&bm, sizeof(bm));
+    ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
+    ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
+       bm.bmWidth, bm.bmHeight);
+    DeleteObject(icon_info.hbmColor);
+    DeleteObject(icon_info.hbmMask);
+
+    /* Set bitmap with BS_BITMAP */
+    hwnd = CreateWindowA("Button", "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd not NULL\n");
+    SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp1x1);
+    hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_BITMAP, 0);
+    ok(hbmp != 0, "Expect hbmp not 0\n");
+    ZeroMemory(&bm, sizeof(bm));
+    ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
+    ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
+       bm.bmWidth, bm.bmHeight);
+    DestroyWindow(hwnd);
+
+    /* Set bitmap without BS_BITMAP */
+    hwnd = CreateWindowA("Button", "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd not NULL\n");
+    ret = SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp1x1);
+    ok(ret == 0, "Expect ret to be 0\n");
+    hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_BITMAP, 0);
+    ok(hbmp == NULL, "Expect hbmp to be NULL\n");
+    DestroyWindow(hwnd);
+
+    /* Set icon with BS_ICON */
+    hwnd = CreateWindowA("Button", "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd not NULL\n");
+    SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon2x2);
+    hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0);
+    ok(hicon != NULL, "Expect hicon not NULL\n");
+    ZeroMemory(&icon_info, sizeof(icon_info));
+    ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
+    ZeroMemory(&bm, sizeof(bm));
+    ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
+    ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
+       bm.bmWidth, bm.bmHeight);
+    DeleteObject(icon_info.hbmColor);
+    DeleteObject(icon_info.hbmMask);
+    DestroyWindow(hwnd);
+
+    /* Set icon without BS_ICON */
+    hwnd = CreateWindowA("Button", "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd not NULL\n");
+    ret = SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon2x2);
+    ok(ret == 0, "Expect ret to be 0\n");
+    hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0);
+    ok(hicon == NULL, "Expect hicon to be NULL\n");
+    DestroyWindow(hwnd);
+
+    /* Set icon with BS_BITMAP */
+    hwnd = CreateWindowA("Button", "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
+    ret = SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon2x2);
+    ok(ret == 0, "Expect ret to be 0\n");
+    hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_ICON, 0);
+    ok(hicon == NULL, "Expect hicon to be NULL\n");
+    DestroyWindow(hwnd);
+
+    /* Set bitmap with BS_ICON */
+    hwnd = CreateWindowA("Button", "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0);
+    ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
+    ret = SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp1x1);
+    ok(ret == 0, "Expect ret to be 0\n");
+    hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, (WPARAM)IMAGE_BITMAP, 0);
+    ok(hbmp == NULL, "Expect hbmp to be NULL\n");
+    DestroyWindow(hwnd);
+
+    DestroyIcon(hicon2x2);
+    DeleteObject(hmask2x2);
+    DeleteObject(hbmp2x2);
+    DeleteObject(hbmp1x1);
+    ReleaseDC(0, hdc);
+}
+
 #define ID_RADIO1 501
 #define ID_RADIO2 502
 #define ID_RADIO3 503
@@ -7119,7 +7227,7 @@ static void test_static_messages(void)
 
     subclass_static();
 
-    for (i = 0; i < sizeof(static_ctrl)/sizeof(static_ctrl[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(static_ctrl); i++)
     {
        hwnd = CreateWindowExA(0, "my_static_class", "test", static_ctrl[i].style | WS_POPUP,
                               0, 0, 50, 14, 0, 0, 0, NULL);
@@ -7609,7 +7717,8 @@ void dump_region(HRGN hrgn)
     HeapFree( GetProcessHeap(), 0, data );
 }
 
-static void check_update_rgn( HWND hwnd, HRGN hrgn )
+#define check_update_rgn( hwnd, hrgn ) check_update_rgn_( __LINE__, hwnd, hrgn )
+static void check_update_rgn_( int line, HWND hwnd, HRGN hrgn )
 {
     INT ret;
     RECT r1, r2;
@@ -7620,13 +7729,13 @@ static void check_update_rgn( HWND hwnd, HRGN hrgn )
     ok( ret != ERROR, "GetUpdateRgn failed\n" );
     if (ret == NULLREGION)
     {
-        ok( !hrgn, "Update region shouldn't be empty\n" );
+        ok_(__FILE__,line)( !hrgn, "Update region shouldn't be empty\n" );
     }
     else
     {
         if (CombineRgn( tmp, hrgn, update, RGN_XOR ) != NULLREGION)
         {
-            ok( 0, "Regions are different\n" );
+            ok_(__FILE__,line)( 0, "Regions are different\n" );
             if (winetest_debug > 0)
             {
                 printf( "Update region: " );
@@ -7638,8 +7747,8 @@ static void check_update_rgn( HWND hwnd, HRGN hrgn )
     }
     GetRgnBox( update, &r1 );
     GetUpdateRect( hwnd, &r2, FALSE );
-    ok( EqualRect( &r1, &r2 ), "Rectangles are different: %s / %s\n", wine_dbgstr_rect( &r1 ),
-        wine_dbgstr_rect( &r2 ));
+    ok_(__FILE__,line)( EqualRect( &r1, &r2 ), "Rectangles are different: %s / %s\n",
+                        wine_dbgstr_rect( &r1 ), wine_dbgstr_rect( &r2 ));
 
     DeleteObject( tmp );
     DeleteObject( update );
@@ -7835,9 +7944,76 @@ static void test_paint_messages(void)
     /* MSDN: if hwnd parameter is NULL, InvalidateRect invalidates and redraws
      * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
      */
-    trace("testing InvalidateRect(0, NULL, FALSE)\n");
     SetRectEmpty( &rect );
-    ok(InvalidateRect(0, &rect, FALSE), "InvalidateRect(0, &rc, FALSE) should fail\n");
+    ok(InvalidateRect(0, &rect, FALSE), "InvalidateRect(0, &rc, FALSE) failed\n");
+    check_update_rgn( hwnd, hrgn );
+    ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+    flush_events();
+    ok_sequence( WmPaint, "Paint", FALSE );
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    check_update_rgn( hwnd, 0 );
+
+    SetRectEmpty( &rect );
+    ok(RedrawWindow(0, &rect, 0, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, 0 );
+
+    SetRectEmpty( &rect );
+    ok(RedrawWindow(0, &rect, 0, RDW_ALLCHILDREN | RDW_VALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, 0 );
+
+    GetWindowRect( hwnd, &rect );
+    ok(RedrawWindow(0, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, 0 );
+
+    flush_events();
+    ok(RedrawWindow(0, &rect, 0, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, hrgn );
+    ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+    flush_events();
+    ok_sequence( WmPaint, "Paint", FALSE );
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    check_update_rgn( hwnd, 0 );
+
+    ok(RedrawWindow(GetDesktopWindow(), &rect, 0,
+                    RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    ret = GetUpdateRgn( hwnd, hrgn2, FALSE );
+    ok( ret == NULLREGION || broken(ret == SIMPLEREGION), /* <= win7 */
+        "region should be null (%d)\n", ret );
+    if (ret == SIMPLEREGION) ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    flush_events();
+
+    ok(RedrawWindow(GetDesktopWindow(), NULL, 0,
+                    RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    ret = GetUpdateRgn( hwnd, hrgn2, FALSE );
+    ok( ret == NULLREGION || broken(ret == SIMPLEREGION), /* <= win7 */
+        "region should be null (%d)\n", ret );
+    if (ret == SIMPLEREGION) ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    flush_events();
+
+    SetRectRgn( hrgn2, rect.left, rect.top, rect.right, rect.bottom );
+    ok(RedrawWindow(0, NULL, hrgn2, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, hrgn );
+    ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+    flush_events();
+    ok_sequence( WmPaint, "Paint", FALSE );
+    RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+    check_update_rgn( hwnd, 0 );
+
+    ok(RedrawWindow(0, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
+    check_update_rgn( hwnd, 0 );
+
+    ok(RedrawWindow(0, NULL, 0, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_ERASE | RDW_ERASENOW ),
+       "RedrawWindow failed\n");
     check_update_rgn( hwnd, hrgn );
     ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
     flush_events();
@@ -7848,7 +8024,6 @@ static void test_paint_messages(void)
     /* MSDN: if hwnd parameter is NULL, ValidateRect invalidates and redraws
      * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
      */
-    trace("testing ValidateRect(0, NULL)\n");
     SetRectEmpty( &rect );
     if (ValidateRect(0, &rect) && /* not supported on Win9x */
         GetUpdateRect(hwnd, NULL, FALSE))  /* or >= Win 8 */
@@ -7861,7 +8036,6 @@ static void test_paint_messages(void)
         check_update_rgn( hwnd, 0 );
     }
 
-    trace("testing InvalidateRgn(0, NULL, FALSE)\n");
     SetLastError(0xdeadbeef);
     ok(!InvalidateRgn(0, NULL, FALSE), "InvalidateRgn(0, NULL, FALSE) should fail\n");
     ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE || GetLastError() == 0xdeadbeef,
@@ -7870,7 +8044,6 @@ static void test_paint_messages(void)
     flush_events();
     ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
 
-    trace("testing ValidateRgn(0, NULL)\n");
     SetLastError(0xdeadbeef);
     ok(!ValidateRgn(0, NULL), "ValidateRgn(0, NULL) should fail\n");
     ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
@@ -7880,7 +8053,6 @@ static void test_paint_messages(void)
     flush_events();
     ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
 
-    trace("testing UpdateWindow(NULL)\n");
     SetLastError(0xdeadbeef);
     ok(!UpdateWindow(NULL), "UpdateWindow(NULL) should fail\n");
     ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE ||
@@ -9565,6 +9737,48 @@ static LRESULT WINAPI ShowWindowProcA(HWND hwnd, UINT message, WPARAM wParam, LP
     return ret;
 }
 
+static LRESULT WINAPI recursive_activation_wndprocA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    static LONG defwndproc_counter = 0;
+    struct recvd_message msg;
+    LRESULT ret;
+
+    switch (message)
+    {
+    /* log only specific messages we are interested in */
+    case WM_NCACTIVATE:
+    case WM_ACTIVATE:
+    case WM_SETFOCUS:
+    case WM_KILLFOCUS:
+        break;
+    default:
+        return DefWindowProcA(hwnd, message, wParam, lParam);
+    }
+
+    msg.hwnd = hwnd;
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    msg.descr = "recursive_activation";
+    add_message(&msg);
+
+    /* recursively activate ourselves by first losing activation and changing it back */
+    if (message == WM_ACTIVATE && LOWORD(wParam) != WA_INACTIVE)
+    {
+        SetActiveWindow((HWND)lParam);
+        SetActiveWindow(hwnd);
+        return 0;
+    }
+
+    defwndproc_counter++;
+    ret = DefWindowProcA(hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+
+    return ret;
+}
+
 static LRESULT WINAPI PaintLoopProcA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     switch (msg)
@@ -9662,6 +9876,10 @@ static BOOL RegisterWindowClasses(void)
     cls.lpszClassName = "ShowWindowClass";
     if(!RegisterClassA(&cls)) return FALSE;
 
+    cls.lpfnWndProc = recursive_activation_wndprocA;
+    cls.lpszClassName = "RecursiveActivationClass";
+    if(!RegisterClassA(&cls)) return FALSE;
+
     cls.lpfnWndProc = PopupMsgCheckProcA;
     cls.lpszClassName = "TestPopupClass";
     if(!RegisterClassA(&cls)) return FALSE;
@@ -9717,6 +9935,7 @@ static BOOL is_our_logged_class(HWND hwnd)
     {
        if (!lstrcmpiA(buf, "TestWindowClass") ||
            !lstrcmpiA(buf, "ShowWindowClass") ||
+           !lstrcmpiA(buf, "RecursiveActivationClass") ||
            !lstrcmpiA(buf, "TestParentClass") ||
            !lstrcmpiA(buf, "TestPopupClass") ||
            !lstrcmpiA(buf, "SimpleWindowClass") ||
@@ -10164,13 +10383,13 @@ todo_wine
         win_skip("SetCoalescableTimer not available.\n");
 
     /* Check what happens when we're running out of timers */
-    for (i=0; i<sizeof(ids)/sizeof(ids[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(ids); i++)
     {
         SetLastError(0xdeadbeef);
         ids[i] = SetTimer(NULL, 0, USER_TIMER_MAXIMUM, tfunc);
         if (!ids[i]) break;
     }
-    ok(i != sizeof(ids)/sizeof(ids[0]), "all timers were created successfully\n");
+    ok(i != ARRAY_SIZE(ids), "all timers were created successfully\n");
     ok(GetLastError()==ERROR_NO_MORE_USER_HANDLES || broken(GetLastError()==0xdeadbeef),
             "GetLastError() = %d\n", GetLastError());
     while (i > 0) KillTimer(NULL, ids[--i]);
@@ -10548,7 +10767,7 @@ static void test_winevents(void)
     ok_sequence(WmEmptySeq, "empty notify winevents", FALSE);
     }
 
-    for (i = 0; i < sizeof(WmWinEventsSeq)/sizeof(WmWinEventsSeq[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(WmWinEventsSeq); i++)
        pNotifyWinEvent(events[i].message, hwnd, events[i].wParam, events[i].lParam);
 
     ok_sequence(WmWinEventsSeq, "notify winevents", FALSE);
@@ -12038,6 +12257,31 @@ todo_wine {
         qstatus = GetQueueStatus(qs_all_input);
         ok(qstatus == 0, "wrong qstatus %08x\n", qstatus);
     }
+
+    PostThreadMessageA(GetCurrentThreadId(), WM_USER, 0, 0);
+    ret = PeekMessageA(&msg, (HWND)-1, 0, 0, PM_NOREMOVE);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+    ret = GetMessageA(&msg, (HWND)-1, 0, 0);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+
+    PostThreadMessageA(GetCurrentThreadId(), WM_USER, 0, 0);
+    ret = PeekMessageA(&msg, (HWND)1, 0, 0, PM_NOREMOVE);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+    ret = GetMessageA(&msg, (HWND)1, 0, 0);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+
+    PostThreadMessageA(GetCurrentThreadId(), WM_USER, 0, 0);
+    ret = PeekMessageA(&msg, (HWND)0xffff, 0, 0, PM_NOREMOVE);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+    ret = GetMessageA(&msg, (HWND)0xffff, 0, 0);
+    ok(ret == TRUE, "wrong ret %d\n", ret);
+    ok(msg.message == WM_USER, "wrong message %u\n", msg.message);
+
 done:
     trace("signalling to exit\n");
     SetEvent(info.hevent[EV_STOP]);
@@ -13243,7 +13487,7 @@ static void test_ShowWindow(void)
     ok(EqualRect(&win_rc, &wp.rcNormalPosition), "expected %s got %s\n", wine_dbgstr_rect(&win_rc),
        wine_dbgstr_rect(&wp.rcNormalPosition));
 
-    for (i = 0; i < sizeof(sw)/sizeof(sw[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(sw); i++)
     {
         static const char * const sw_cmd_name[13] =
         {
@@ -13492,6 +13736,8 @@ static const struct message WmCreateDialogParamSeq_3[] = {
     { WM_QUERYNEWPALETTE, sent|parent|optional }, /* TODO: this message should not be sent */
     { WM_WINDOWPOSCHANGING, sent|parent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
     { WM_WINDOWPOSCHANGING, sent|parent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_WINDOWPOSCHANGED, sent|parent|wparam|optional, SWP_NOREDRAW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_WINDOWPOSCHANGED, sent|parent|wparam|optional, SWP_NOREDRAW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
     { WM_ACTIVATEAPP, sent|parent|wparam, 1 },
     { WM_NCACTIVATE, sent|parent },
     { WM_ACTIVATE, sent|parent|wparam, 1 },
@@ -15224,6 +15470,46 @@ static const struct message NCXBUTTONUPSeq2[] =
     { 0 }
 };
 
+/* DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0) to minimized visible window */
+static const struct message WmRestoreMinimizedOverlappedSeq[] =
+{
+    { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_RESTORE, 0 },
+    { HCBT_MINMAX, hook },
+    { WM_QUERYOPEN, sent },
+    { WM_GETTEXT, sent|optional },
+    { WM_NCACTIVATE, sent|optional },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+    { WM_WINDOWPOSCHANGED, sent|optional },
+    { WM_WINDOWPOSCHANGING, sent|optional },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|optional },
+    { WM_NCPAINT, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|optional },
+    { HCBT_ACTIVATE, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_ACTIVATEAPP, sent|wparam, TRUE },
+    { WM_NCACTIVATE, sent|wparam, TRUE },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ACTIVATE, sent|wparam, TRUE },
+    { HCBT_SETFOCUS, hook },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_NCPAINT, sent },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_FRAMECHANGED|SWP_STATECHANGED },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|optional },
+    { WM_NCPAINT, sent|optional },
+    { WM_ERASEBKGND, sent|optional },
+    { WM_ACTIVATE, sent|wparam, TRUE },
+    { WM_SYNCPAINT, sent|optional },
+    { WM_PAINT, sent },
+    { 0 }
+};
+
 struct rbuttonup_thread_data
 {
     HWND hwnd;
@@ -15262,22 +15548,31 @@ static void test_defwinproc(void)
     flush_events();
 
     buffA[0] = 0;
-    GetWindowTextA(hwnd, buffA, sizeof(buffA)/sizeof(*buffA));
+    GetWindowTextA(hwnd, buffA, ARRAY_SIZE(buffA));
     ok(!strcmp(buffA, "test_defwndproc"), "unexpected window text, %s\n", buffA);
 
     /* Zero high word of the lParam */
     res = DefWindowProcA(hwnd, WM_SETTEXT, 0, 0x1234);
     ok(res == 0, "WM_SETTEXT was expected to fail, %ld\n", res);
 
-    GetWindowTextA(hwnd, buffA, sizeof(buffA)/sizeof(*buffA));
+    GetWindowTextA(hwnd, buffA, ARRAY_SIZE(buffA));
     ok(!strcmp(buffA, "test_defwndproc"), "unexpected window text, %s\n", buffA);
 
     res = DefWindowProcW(hwnd, WM_SETTEXT, 0, 0x1234);
     ok(res == 0, "WM_SETTEXT was expected to fail, %ld\n", res);
 
-    GetWindowTextA(hwnd, buffA, sizeof(buffA)/sizeof(*buffA));
+    GetWindowTextA(hwnd, buffA, ARRAY_SIZE(buffA));
     ok(!strcmp(buffA, "test_defwndproc"), "unexpected window text, %s\n", buffA);
 
+    ShowWindow(hwnd, SW_MINIMIZE);
+    flush_events();
+    flush_sequence();
+
+    DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
+    flush_events();
+    ok_sequence(WmRestoreMinimizedOverlappedSeq, "DefWindowProcA(SC_RESTORE):overlapped", TRUE);
+    flush_sequence();
+
     GetCursorPos(&pos);
     GetWindowRect(hwnd, &rect);
     x = (rect.left+rect.right) / 2;
@@ -15333,6 +15628,67 @@ static void test_defwinproc(void)
     DestroyWindow( hwnd);
 }
 
+static void test_desktop_winproc(void)
+{
+    HINSTANCE instance = GetModuleHandleA(NULL);
+    RECT rect, default_rect;
+    WNDPROC desktop_proc;
+    char buffer[256];
+    WNDCLASSA cls;
+    LRESULT res;
+    HWND hwnd;
+    BOOL ret;
+
+    ret = GetClassInfoA(instance, (const CHAR *)MAKEINTATOM(32769), &cls);
+    ok(ret, "Failed to get desktop class.\n");
+    desktop_proc = cls.lpfnWndProc;
+
+    memset(&cls, 0, sizeof(cls));
+    cls.lpfnWndProc = desktop_proc;
+    cls.hInstance = instance;
+    cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
+    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+    cls.lpszClassName = "TestDesktopClass";
+    ret = !!RegisterClassA(&cls);
+    ok(ret, "Failed to register class.\n");
+
+    hwnd = CreateWindowExA(0, cls.lpszClassName, "test_desktop_wndproc",
+            WS_VISIBLE | WS_CAPTION | WS_OVERLAPPEDWINDOW, 0, 0, 500, 100, 0, 0, 0, NULL);
+    if (!hwnd) /* win2003 */
+    {
+        skip("Failed to create window with desktop window procedure.\n");
+        goto out_unregister;
+    }
+
+    memset(&cls, 0, sizeof(cls));
+    ret = GetClassInfoA(instance, "TestDesktopClass", &cls);
+    ok(ret, "Failed to get class info.\n");
+    ok(cls.lpfnWndProc == desktop_proc, "Got %p, expected %p.\n", cls.lpfnWndProc, desktop_proc);
+
+    GetWindowTextA(hwnd, buffer, ARRAY_SIZE(buffer));
+    todo_wine ok(!strcmp(buffer, "test_desktop_wndproc"), "Got unexpected window text: %s.\n", buffer);
+
+    res = CallWindowProcA(desktop_proc, hwnd, WM_SETTEXT, 0, (LPARAM)"test");
+    ok(res == TRUE, "Failed to set text, %ld.\n", res);
+    GetWindowTextA(hwnd, buffer, ARRAY_SIZE(buffer));
+    ok(!strcmp(buffer, "test"), "Got unexpected window text: %s.\n", buffer);
+
+    SetRect(&default_rect, 0, 0, 100, 100);
+    res = DefWindowProcW(hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&default_rect);
+    ok(!res, "Got unexpected result %ld.\n", res);
+
+    SetRect(&rect, 0, 0, 100, 100);
+    res = CallWindowProcA(desktop_proc, hwnd, WM_NCCALCSIZE, FALSE, (LPARAM)&rect);
+    ok(!res, "Got unexpected result %ld.\n", res);
+    todo_wine ok(EqualRect(&rect, &default_rect), "rect Got %s, expected %s.\n",
+            wine_dbgstr_rect(&rect), wine_dbgstr_rect(&default_rect));
+
+    DestroyWindow(hwnd);
+
+out_unregister:
+    UnregisterClassA("TestDesktopClass", instance);
+}
+
 #define clear_clipboard(hwnd)  clear_clipboard_(__LINE__, (hwnd))
 static void clear_clipboard_(int line, HWND hWnd)
 {
@@ -15535,7 +15891,7 @@ static void test_PostMessage(void)
     PostMessageA(hwnd, WM_USER+1, 0x1234, 0x5678);
     PostMessageA(0, WM_USER+2, 0x5678, 0x1234);
 
-    for (i = 0; i < sizeof(data)/sizeof(data[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(data); i++)
     {
         memset(&msg, 0xab, sizeof(msg));
         ret = PeekMessageA(&msg, data[i].hwnd, 0, 0, PM_NOREMOVE);
@@ -15593,7 +15949,7 @@ static void test_broadcast(void)
     oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)broadcast_test_proc);
     SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
 
-    for (i = 0; i < sizeof(messages)/sizeof(messages[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(messages); i++)
     {
         BOOL ret;
         MSG msg;
@@ -15923,14 +16279,14 @@ static void test_WaitForInputIdle( char *argv0 )
 
     thread = CreateThread( NULL, 0, wait_idle_thread, NULL, 0, &id );
 
-    for (i = 0; i < sizeof(wait_idle_expect)/sizeof(wait_idle_expect[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(wait_idle_expect); i++)
     {
         ResetEvent( start_event );
         ResetEvent( end_event );
-#ifndef __REACTOS__
-        sprintf( path, "%s msg %u", argv0, i );
-#else
+#ifdef __REACTOS__
         sprintf( path, "%s msg_queue %u", argv0, i );
+#else
+        sprintf( path, "%s msg %u", argv0, i );
 #endif
         ret = CreateProcessA( NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi );
         ok( ret, "CreateProcess '%s' failed err %u.\n", path, GetLastError() );
@@ -16039,7 +16395,7 @@ static void test_SetParent(void)
 
     SetParent(child, parent2);
     flush_events();
-    ok_sequence(WmSetParentSeq_1, "SetParent() visible WS_CHILD", TRUE);
+    ok_sequence(WmSetParentSeq_1, "SetParent() visible WS_CHILD", FALSE);
 
     ok(GetWindowLongA(child, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
     ok(!IsWindowVisible(child), "IsWindowVisible() should return FALSE\n");
@@ -16314,16 +16670,16 @@ static void test_hotkey(void)
 
     SetLastError(0xdeadbeef);
     ret = UnregisterHotKey(NULL, 0);
-    ok(ret == FALSE, "expected FALSE, got %i\n", ret);
-    ok(GetLastError() == ERROR_HOTKEY_NOT_REGISTERED || broken(GetLastError() == 0xdeadbeef),
-       "unexpected error %d\n", GetLastError());
-
     if (ret == TRUE)
     {
         skip("hotkeys not supported\n");
         return;
     }
 
+    ok(ret == FALSE, "expected FALSE, got %i\n", ret);
+    ok(GetLastError() == ERROR_HOTKEY_NOT_REGISTERED || broken(GetLastError() == 0xdeadbeef),
+       "unexpected error %d\n", GetLastError());
+
     test_window = CreateWindowExA(0, "HotkeyWindowClass", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            100, 100, 200, 200, 0, 0, 0, NULL);
 
@@ -17344,6 +17700,112 @@ static void test_DoubleSetCapture(void)
     DestroyWindow(hwnd);
 }
 
+static const struct message WmRestoreMinimizedSeq[] =
+{
+    { HCBT_ACTIVATE, hook },
+    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+    { WM_ACTIVATEAPP, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent|wparam, 0x200001 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ACTIVATE, sent|wparam, 0x200001 }, /* Note that activate messages are after WM_WINDOWPOSCHANGED and before WM_SYSCOMMAND */
+    { HCBT_KEYSKIPPED, hook|optional },
+    { WM_SYSKEYUP, sent|optional },
+    { WM_SYSCOMMAND, sent|wparam, SC_RESTORE },
+    { HCBT_SYSCOMMAND, hook|wparam, SC_RESTORE },
+    { HCBT_SYSCOMMAND, hook|wparam|optional, SC_RESTORE },
+    { HCBT_MINMAX, hook },
+    { HCBT_MINMAX, hook|optional },
+    { WM_QUERYOPEN, sent|defwinproc },
+    { WM_QUERYOPEN, sent|optional },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+    { WM_GETMINMAXINFO, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+    { WM_NCPAINT, sent|wparam|defwinproc|optional, 1 },
+    { WM_GETTEXT, sent|defwinproc|optional },
+    { WM_ERASEBKGND, sent|defwinproc },
+    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOCOPYBITS|SWP_STATECHANGED },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    { WM_NCCALCSIZE, sent|wparam|defwinproc|optional, 1 },
+    { WM_NCPAINT, sent|wparam|defwinproc|optional, 1 },
+    { WM_ERASEBKGND, sent|defwinproc|optional },
+    { HCBT_SETFOCUS, hook },
+    { WM_SETFOCUS, sent|defwinproc },
+    { WM_ACTIVATE, sent|wparam|defwinproc, 1 },
+    { WM_PAINT, sent| optional },
+    { WM_SETFOCUS, sent|defwinproc|optional },
+    { HCBT_KEYSKIPPED, hook|optional },
+    { WM_KEYUP, sent|optional },
+    { HCBT_KEYSKIPPED, hook|optional },
+    { WM_SYSKEYUP, sent|optional },
+    { HCBT_KEYSKIPPED, hook|optional },
+    { WM_KEYUP, sent|optional },
+    { WM_PAINT, sent| optional },
+    { 0 }
+};
+
+static void test_restore_messages(void)
+{
+    INPUT ip = {0};
+    HWND hwnd;
+    INT i;
+
+    hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100,
+                           100, 200, 200, 0, 0, 0, NULL);
+    ok (hwnd != 0, "Failed to create overlapped window\n");
+    SetForegroundWindow(hwnd);
+    ShowWindow(hwnd, SW_MINIMIZE);
+    flush_events();
+    flush_sequence();
+
+    for (i = 0; i < 5; i++)
+    {
+        /* Send Alt+Tab to restore test window from minimized state */
+        ip.type = INPUT_KEYBOARD;
+        ip.ki.wVk = VK_MENU;
+        SendInput(1, &ip, sizeof(INPUT));
+        ip.ki.wVk = VK_TAB;
+        SendInput(1, &ip, sizeof(INPUT));
+        ip.ki.wVk = VK_MENU;
+        ip.ki.dwFlags = KEYEVENTF_KEYUP;
+        SendInput(1, &ip, sizeof(INPUT));
+        ip.ki.wVk = VK_TAB;
+        ip.ki.dwFlags = KEYEVENTF_KEYUP;
+        SendInput(1, &ip, sizeof(INPUT));
+        flush_events();
+        if (!IsIconic(hwnd))
+            break;
+    }
+
+    if (IsIconic(hwnd))
+    {
+        skip("Alt+Tab failed to bring up test window.\n");
+        goto done;
+    }
+    ok_sequence(WmRestoreMinimizedSeq, "Restore minimized window", TRUE);
+
+done:
+    DestroyWindow(hwnd);
+}
+
+static void test_invalid_window(void)
+{
+    MSG msg;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ret = GetMessageA(&msg, (HWND)0xdeadbeef, 0, 0);
+    ok(ret == -1, "wrong ret %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE, "wrong error %u\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = PeekMessageA(&msg, (HWND)0xdeadbeef, 0, 0, PM_REMOVE);
+    ok(!ret, "wrong ret %d\n", ret);
+    ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE, "wrong error %u\n", GetLastError());
+}
+
 static void init_funcs(void)
 {
     HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
@@ -17440,9 +17902,11 @@ START_TEST(msg)
     test_messages();
     test_setwindowpos();
     test_showwindow();
+    test_recursive_activation();
     invisible_parent_tests();
     test_mdi_messages();
     test_button_messages();
+    test_button_bm_get_set_image();
     test_autoradio_BM_CLICK();
     test_autoradio_kbd_move();
     test_static_messages();
@@ -17468,6 +17932,8 @@ START_TEST(msg)
     test_quit_message();
     test_notify_message();
     test_SetActiveWindow();
+    test_restore_messages();
+    test_invalid_window();
 
     if (!pTrackMouseEvent)
         win_skip("TrackMouseEvent is not available\n");
@@ -17484,6 +17950,7 @@ START_TEST(msg)
     test_menu_messages();
     test_paintingloop();
     test_defwinproc();
+    test_desktop_winproc();
     test_clipboard_viewers();
     test_keyflags();
     test_hotkey();
@@ -17598,6 +18065,7 @@ START_TEST(msg_messages)
     test_dbcs_wm_char();
     test_unicode_wm_char();
     test_defwinproc();
+    test_desktop_winproc();
     cleanup_tests();
 }
 
@@ -17619,6 +18087,9 @@ START_TEST(msg_focus)
     keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
     flush_events();
 
+    test_restore_messages();
+    test_invalid_window();
+
     test_DoubleSetCapture();
 
     /* keep it the last test, under Windows it tends to break the tests
@@ -17635,6 +18106,7 @@ START_TEST(msg_winpos)
     test_ShowWindow();
     test_setwindowpos();
     test_showwindow();
+    test_recursive_activation();
     test_SetWindowRgn();
     invisible_parent_tests();
     cleanup_tests();
@@ -17745,6 +18217,7 @@ START_TEST(msg_controls)
 {
     init_tests();
     test_button_messages();
+    test_button_bm_get_set_image();
     test_autoradio_BM_CLICK();
     test_autoradio_kbd_move();
     test_static_messages();
diff --git a/modules/rostests/winetests/user32/rawinput.c b/modules/rostests/winetests/user32/rawinput.c
new file mode 100644 (file)
index 0000000..f4c8eb6
--- /dev/null
@@ -0,0 +1,101 @@
+/* Unit test suite for rawinput.
+ *
+ * Copyright 2019 Remi Bernon for CodeWeavers
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define STRICT
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include "wine/test.h"
+
+static void test_RegisterRawInputDevices(void)
+{
+    HWND hwnd;
+    RAWINPUTDEVICE raw_devices[1];
+    BOOL res;
+
+    raw_devices[0].usUsagePage = 0x01;
+    raw_devices[0].usUsage = 0x05;
+
+    hwnd = CreateWindowExA(WS_EX_TOPMOST, "static", "dinput", WS_POPUP | WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "CreateWindowExA failed\n");
+
+
+    res = RegisterRawInputDevices(NULL, 0, 0);
+    ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
+
+
+    raw_devices[0].dwFlags = 0;
+    raw_devices[0].hwndTarget = 0;
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), 0);
+    ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
+    ok(res == TRUE, "RegisterRawInputDevices failed\n");
+    ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+
+    /* RIDEV_REMOVE requires hwndTarget == NULL */
+    raw_devices[0].dwFlags = RIDEV_REMOVE;
+    raw_devices[0].hwndTarget = hwnd;
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
+    ok(res == FALSE, "RegisterRawInputDevices succeeded\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+    raw_devices[0].hwndTarget = 0;
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
+    ok(res == TRUE, "RegisterRawInputDevices failed\n");
+    ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+
+    /* RIDEV_INPUTSINK requires hwndTarget != NULL */
+    raw_devices[0].dwFlags = RIDEV_INPUTSINK;
+    raw_devices[0].hwndTarget = 0;
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
+    todo_wine
+    ok(res == FALSE, "RegisterRawInputDevices failed\n");
+    todo_wine
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+    raw_devices[0].hwndTarget = hwnd;
+
+    SetLastError(0xdeadbeef);
+    res = RegisterRawInputDevices(raw_devices, ARRAY_SIZE(raw_devices), sizeof(RAWINPUTDEVICE));
+    ok(res == TRUE, "RegisterRawInputDevices succeeded\n");
+    ok(GetLastError() == 0xdeadbeef, "RegisterRawInputDevices returned %08x\n", GetLastError());
+
+    DestroyWindow(hwnd);
+}
+
+START_TEST(rawinput)
+{
+    test_RegisterRawInputDevices();
+}
index 2388adb..5d4c4e2 100644 (file)
@@ -50,7 +50,7 @@ static void test_LoadStringW(void)
             win_skip( "LoadStringW does not return a pointer to the resource\n" );
         return;
     }
-    length2 = LoadStringW(hInst, 2, returnedstringw, sizeof(returnedstringw) /sizeof(WCHAR)); /* get resource string */
+    length2 = LoadStringW(hInst, 2, returnedstringw, ARRAY_SIZE(returnedstringw)); /* get resource string */
     ok(length2 > 0, "LoadStringW failed to load resource 2, ret %d, err %d\n", length2, GetLastError());
     ok(length1 == length2, "LoadStringW returned different values dependent on buflen. ret1 %d, ret2 %d\n",
         length1, length2);
@@ -93,7 +93,7 @@ static void test_LoadStringA (void)
     int ret, ret2;
 
     assert (sizeof str < sizeof buf);
-    for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
         const unsigned int bufsiz = tests[i].bufsiz;
         const unsigned int expected = tests[i].expected;
         const int len = LoadStringA (hInst, 0, buf, bufsiz);
index 3823ed3..4301af6 100644 (file)
@@ -237,6 +237,15 @@ FONT 8, "MS Sans Serif"
     CONTROL "oddlengthtext", -1, "TESTCONTROL", WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP, 10,60,100,50 { 1,2,3,4,5 }
 }
 
+GETDLGITEM_TEST_DIALOG DIALOGEX 6, 15, 207, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Test Dialog"
+FONT 8, "MS Shell Dlg"
+{
+    LTEXT "Text1",-1,10,10,100,50
+    LTEXT "Text2",-2,10,10,100,50
+}
+
 /* @makedep: test_mono.bmp */
 100 BITMAP test_mono.bmp
 
index 0bda5dd..fbc94a6 100644 (file)
@@ -114,6 +114,14 @@ static void test_EnableScrollBar(void)
     ok( ret, "The scrollbar should be enabled.\n" );
     ok( IsWindowEnabled( hScroll ), "The scrollbar window should be enabled.\n" );
 
+    SetLastError( 0xdeadbeef );
+    ret = EnableScrollBar( mainwnd, SB_CTL, ESB_ENABLE_BOTH );
+    ok( !ret, "EnableScrollBar should fail.\n" );
+    todo_wine
+    ok( GetLastError() == ERROR_INVALID_PARAMETER
+        || broken(GetLastError() == 0xdeadbeef), /* winxp */
+        "GetLastError() = %u\n", GetLastError() );
+
     /* disable window, try to re-enable */
     ret = EnableWindow( hScroll, FALSE );
     ok( !ret, "got %d\n", ret );
@@ -170,6 +178,16 @@ static void test_SetScrollPos(void)
     ret = GetScrollPos( hScroll, SB_CTL);
     ok( ret == 30, "The position should not be equal to zero\n");
 
+    SetLastError( 0xdeadbeef );
+    ret = SetScrollPos( mainwnd, SB_CTL, 30, TRUE );
+    ok( !ret, "The position should not be set.\n" );
+    ok( GetLastError() == 0xdeadbeef, "GetLastError() = %u\n", GetLastError() );
+
+    SetLastError( 0xdeadbeef );
+    ret = GetScrollPos( mainwnd, SB_CTL );
+    ok( !ret, "The position should be equal to zero\n");
+    ok( GetLastError() == 0xdeadbeef, "GetLastError() = %u\n", GetLastError() );
+
     DestroyWindow(hScroll);
     DestroyWindow(mainwnd);
 }
@@ -192,6 +210,9 @@ static void test_ShowScrollBar(void)
     ret = ShowScrollBar( NULL, SB_CTL, TRUE );
     ok( !ret, "The ShowScrollBar() should failed.\n" );
 
+    ret = ShowScrollBar( mainwnd, SB_CTL, TRUE );
+    ok( ret, "The ShowScrollBar() should not fail.\n" );
+
     DestroyWindow(hScroll);
     DestroyWindow(mainwnd);
 }
@@ -514,7 +535,7 @@ static void scrollbar_test_init(void)
     wc.lpfnWndProc   = scroll_init_proc;
     RegisterClassExA(&wc);
 
-    for(i = 0; i < sizeof style / sizeof style[0]; i++)
+    for(i = 0; i < ARRAY_SIZE(style); i++)
     {
         /* need not to destroy these windows due creation abort */
         CreateWindowExA(0, cls_name, NULL, style[i],
@@ -581,6 +602,105 @@ static void test_SetScrollInfo(void)
     DestroyWindow(mainwnd);
 }
 
+static WNDPROC scrollbar_wndproc;
+
+static SCROLLINFO set_scrollinfo;
+
+static LRESULT CALLBACK subclass_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    if (msg == WM_CREATE && ((CREATESTRUCTA*)lparam)->lpCreateParams)
+        return DefWindowProcA(hwnd, msg, wparam, lparam);
+
+    if (msg == SBM_SETSCROLLINFO)
+        set_scrollinfo = *(SCROLLINFO*)lparam;
+
+    return CallWindowProcA(scrollbar_wndproc, hwnd, msg, wparam, lparam);
+}
+
+static void test_subclass(void)
+{
+    SCROLLBARINFO scroll_info;
+    WNDCLASSEXA class_info;
+    WNDCLASSA wc;
+    LRESULT res;
+    HWND hwnd;
+    BOOL r;
+
+    r = GetClassInfoExA(GetModuleHandleA(NULL), "SCROLLBAR", &class_info);
+    ok(r, "GetClassInfoEx failed: %u\n", GetLastError());
+    scrollbar_wndproc = class_info.lpfnWndProc;
+
+    memset(&wc, 0, sizeof(wc));
+    wc.cbWndExtra = class_info.cbWndExtra + 3; /* more space than needed works */
+    wc.hInstance = GetModuleHandleA(NULL);
+    wc.lpszClassName = "MyTestSubclass";
+    wc.lpfnWndProc = subclass_proc;
+    r = RegisterClassA(&wc);
+    ok(r, "RegisterClass failed: %u\n", GetLastError());
+
+    hwnd = CreateWindowExA( 0, "MyTestSubclass", "Scroll", WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, GetModuleHandleA(NULL), 0 );
+    ok(hwnd != NULL, "Failed to create window: %u\n", GetLastError());
+
+    r = SetScrollRange(hwnd, SB_CTL, 0, 100, TRUE);
+    ok(r, "SetScrollRange failed: %u\n", GetLastError());
+
+    res = SetScrollPos(hwnd, SB_CTL, 2, FALSE);
+    ok(!res, "SetScrollPos returned %lu\n", res);
+
+    memset(&set_scrollinfo, 0xcc, sizeof(set_scrollinfo));
+    res = SetScrollPos(hwnd, SB_CTL, 1, FALSE);
+    ok(res == 2, "SetScrollPos returned %lu\n", res);
+    ok(set_scrollinfo.cbSize == sizeof(SCROLLINFO), "cbSize = %u\n", set_scrollinfo.cbSize);
+    todo_wine
+    ok(set_scrollinfo.fMask == (0x1000 | SIF_POS), "fMask = %x\n", set_scrollinfo.fMask);
+    ok(set_scrollinfo.nPos == 1, "nPos = %x\n", set_scrollinfo.nPos);
+
+    memset(&scroll_info, 0xcc, sizeof(scroll_info));
+    scroll_info.cbSize = sizeof(scroll_info);
+    res = SendMessageA(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)&scroll_info);
+    ok(res == 1, "SBM_GETSCROLLBARINFO returned %lu\n", res);
+
+    DestroyWindow(hwnd);
+
+    /* if we skip calling wndproc for WM_CREATE, window is not considered a scrollbar */
+    hwnd = CreateWindowExA( 0, "MyTestSubclass", "Scroll", WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, GetModuleHandleA(NULL), (void *)1 );
+    ok(hwnd != NULL, "Failed to create window: %u\n", GetLastError());
+
+    memset(&scroll_info, 0xcc, sizeof(scroll_info));
+    scroll_info.cbSize = sizeof(scroll_info);
+    res = SendMessageA(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)&scroll_info);
+    ok(!res, "SBM_GETSCROLLBARINFO returned %lu\n", res);
+
+    DestroyWindow(hwnd);
+
+    /* not enough space in extra data */
+    wc.cbWndExtra = class_info.cbWndExtra - 1;
+    wc.lpszClassName = "MyTestSubclass2";
+    r = RegisterClassA(&wc);
+    ok(r, "RegisterClass failed: %u\n", GetLastError());
+
+    hwnd = CreateWindowExA( 0, "MyTestSubclass2", "Scroll", WS_OVERLAPPEDWINDOW,
+                            CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, GetModuleHandleA(NULL), 0 );
+    ok(hwnd != NULL, "Failed to create window: %u\n", GetLastError());
+
+    memset(&scroll_info, 0xcc, sizeof(scroll_info));
+    scroll_info.cbSize = sizeof(scroll_info);
+    res = SendMessageA(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)&scroll_info);
+    ok(!res, "SBM_GETSCROLLBARINFO returned %lu\n", res);
+
+    memset(&set_scrollinfo, 0xcc, sizeof(set_scrollinfo));
+    res = SetScrollPos(hwnd, SB_CTL, 1, FALSE);
+    ok(res == 0, "SetScrollPos returned %lu\n", res);
+    ok(set_scrollinfo.cbSize == sizeof(SCROLLINFO), "cbSize = %u\n", set_scrollinfo.cbSize);
+    todo_wine
+    ok(set_scrollinfo.fMask == (0x1000 | SIF_POS), "fMask = %x\n", set_scrollinfo.fMask);
+    ok(set_scrollinfo.nPos == 1, "nPos = %x\n", set_scrollinfo.nPos);
+
+    DestroyWindow(hwnd);
+}
+
 START_TEST ( scroll )
 {
     WNDCLASSA wc;
@@ -605,6 +725,7 @@ START_TEST ( scroll )
     test_GetScrollBarInfo();
     scrollbar_test_track();
     test_SetScrollInfo();
+    test_subclass();
 
     /* Some test results vary depending of theming being active or not */
     hUxtheme = LoadLibraryA("uxtheme.dll");
index 9ed85a2..bd10789 100644 (file)
 static LONG (WINAPI *pChangeDisplaySettingsExA)(LPCSTR, LPDEVMODEA, HWND, DWORD, LPVOID);
 static BOOL (WINAPI *pIsProcessDPIAware)(void);
 static BOOL (WINAPI *pSetProcessDPIAware)(void);
+static BOOL (WINAPI *pSetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
+static BOOL (WINAPI *pGetProcessDpiAwarenessInternal)(HANDLE,DPI_AWARENESS*);
+static BOOL (WINAPI *pSetProcessDpiAwarenessInternal)(DPI_AWARENESS);
+static UINT (WINAPI *pGetDpiForSystem)(void);
+static UINT (WINAPI *pGetDpiForWindow)(HWND);
+static BOOL (WINAPI *pGetDpiForMonitorInternal)(HMONITOR,UINT,UINT*,UINT*);
+static DPI_AWARENESS_CONTEXT (WINAPI *pGetThreadDpiAwarenessContext)(void);
+static DPI_AWARENESS_CONTEXT (WINAPI *pSetThreadDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
+static DPI_AWARENESS_CONTEXT (WINAPI *pGetWindowDpiAwarenessContext)(HWND);
+static DPI_AWARENESS (WINAPI *pGetAwarenessFromDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
+static BOOL (WINAPI *pIsValidDpiAwarenessContext)(DPI_AWARENESS_CONTEXT);
+static INT (WINAPI *pGetSystemMetricsForDpi)(INT,UINT);
+static BOOL (WINAPI *pSystemParametersInfoForDpi)(UINT,UINT,void*,UINT,UINT);
+static BOOL (WINAPI *pAdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT);
+static BOOL (WINAPI *pLogicalToPhysicalPointForPerMonitorDPI)(HWND,POINT*);
+static BOOL (WINAPI *pPhysicalToLogicalPointForPerMonitorDPI)(HWND,POINT*);
 static LONG (WINAPI *pGetAutoRotationState)(PAR_STATE);
 
 static BOOL strict;
 static int dpi, real_dpi;
-static BOOL iswin9x;
-static HDC hdc;
 
 #define eq(received, expected, label, type) \
         ok((received) == (expected), "%s: got " type " instead of " type "\n", (label),(received),(expected))
@@ -193,6 +207,13 @@ static DWORD get_real_dpi(void)
 {
     DWORD dpi;
 
+    if (pSetThreadDpiAwarenessContext)
+    {
+        DPI_AWARENESS_CONTEXT context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_SYSTEM_AWARE );
+        dpi = pGetDpiForSystem();
+        pSetThreadDpiAwarenessContext( context );
+        return dpi;
+    }
     if (get_reg_dword(HKEY_CURRENT_USER, "Control Panel\\Desktop", "LogPixels", &dpi))
         return dpi;
     if (get_reg_dword(HKEY_CURRENT_CONFIG, "Software\\Fonts", "LogPixels", &dpi))
@@ -622,30 +643,17 @@ static BOOL run_spi_setmouse_test( int curr_val[], POINT *req_change, POINT *pro
     BOOL rc;
     INT mi[3];
     static int aw_turn = 0;
-    static BOOL w_implemented = TRUE;
 
     char buf[20];
     int i;
 
     aw_turn++;
     rc = FALSE;
-    if ((aw_turn % 2!=0) && (w_implemented))
-    {
-        /* call unicode on odd (non even) calls */ 
-        SetLastError(0xdeadbeef);
+    SetLastError(0xdeadbeef);
+    if (aw_turn % 2)  /* call unicode on odd (non even) calls */
         rc=SystemParametersInfoW( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
-        if (rc == FALSE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
-        {
-            w_implemented = FALSE;
-            trace("SystemParametersInfoW not supported on this platform\n");
-        }
-    }
-
-    if ((aw_turn % 2==0) || (!w_implemented))
-    {
-        /* call ascii version on even calls or if unicode is not available */
+    else
         rc=SystemParametersInfoA( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
-    }
     if (!test_error_msg(rc,"SPI_SETMOUSE")) return FALSE;
 
     ok(rc, "SystemParametersInfo: rc=%d err=%d\n", rc, GetLastError());
@@ -664,15 +672,12 @@ static BOOL run_spi_setmouse_test( int curr_val[], POINT *req_change, POINT *pro
            "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]);
     }
 
-    if (w_implemented)
-    { 
-        rc=SystemParametersInfoW( SPI_GETMOUSE, 0, mi, 0 );
-        ok(rc, "SystemParametersInfoW: rc=%d err=%d\n", rc, GetLastError());
-        for (i = 0; i < 3; i++)
-        {
-            ok(mi[i] == curr_val[i],
-               "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]);
-        }
+    rc=SystemParametersInfoW( SPI_GETMOUSE, 0, mi, 0 );
+    ok(rc, "SystemParametersInfoW: rc=%d err=%d\n", rc, GetLastError());
+    for (i = 0; i < 3; i++)
+    {
+        ok(mi[i] == curr_val[i],
+           "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]);
     }
 
     if (0)
@@ -710,7 +715,7 @@ static void test_SPI_SETMOUSE( void )                  /*      4 */
     POINT proj_change7[] = { {6, 6}, {14, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} };
     POINT proj_change8[] = { {6, 6}, {28, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} };
 
-    int nchange = sizeof( req_change ) / sizeof( POINT );
+    int nchange = ARRAY_SIZE(req_change);
 
     trace("testing SPI_{GET,SET}MOUSE\n");
     SetLastError(0xdeadbeef);
@@ -848,15 +853,10 @@ static void test_SPI_SETBORDER( void )                 /*      6 */
     if ( old_border == 7 || old_border == 20 )
         old_border = 1;
 
-    /* The SPI_SETBORDER seems to be buggy on Win9x/ME (looks like you need to
-     * do it twice to make the intended change). So skip parts of the tests on
-     * those platforms */
-    if( !iswin9x) {
-        /* win2k3 fails if you set the same border twice, or if size is 0 */
-        if (!test_setborder(2,  1, dpi)) return;
-        test_setborder(1,  1, dpi);
-        test_setborder(3,  1, dpi);
-    }
+    /* win2k3 fails if you set the same border twice, or if size is 0 */
+    if (!test_setborder(2,  1, dpi)) return;
+    test_setborder(1,  1, dpi);
+    test_setborder(3,  1, dpi);
     if (!test_setborder(1, 0, dpi)) return;
     test_setborder(0, 0, dpi);
     test_setborder(3, 0, dpi);
@@ -880,7 +880,7 @@ static void test_SPI_SETKEYBOARDSPEED( void )          /*     10 */
     if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDSPEED"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -969,7 +969,7 @@ static void test_SPI_SETSCREENSAVETIMEOUT( void )      /*     14 */
     if (!test_error_msg(rc,"SPI_{GET,SET}SCREENSAVETIMEOUT"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -1006,7 +1006,7 @@ static void test_SPI_SETSCREENSAVEACTIVE( void )       /*     17 */
     if (!test_error_msg(rc,"SPI_{GET,SET}SCREENSAVEACTIVE"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -1047,7 +1047,7 @@ static void test_SPI_SETKEYBOARDDELAY( void )          /*     23 */
     if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDDELAY"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT delay;
         char buf[10];
@@ -1148,7 +1148,7 @@ static void test_SPI_SETICONTITLEWRAP( void )          /*     26 */
     if (!test_error_msg(rc,"SPI_{GET,SET}ICONTITLEWRAP"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         UINT regval;
@@ -1163,8 +1163,7 @@ static void test_SPI_SETICONTITLEWRAP( void )          /*     26 */
         if( regval != vals[i])
             regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY1,
                     SPI_SETICONTITLEWRAP_VALNAME, dpi);
-        ok( regval == vals[i] || broken(regval == -1), /* win9x */
-                "wrong value in registry %d, expected %d\n", regval, vals[i] );
+        ok( regval == vals[i], "wrong value in registry %d, expected %d\n", regval, vals[i] );
 
         rc=SystemParametersInfoA( SPI_GETICONTITLEWRAP, 0, &v, 0 );
         ok(rc, "%d: rc=%d err=%d\n", i, rc, GetLastError());
@@ -1193,7 +1192,7 @@ static void test_SPI_SETMENUDROPALIGNMENT( void )      /*     28 */
     if (!test_error_msg(rc,"SPI_{GET,SET}MENUDROPALIGNMENT"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -1229,7 +1228,7 @@ static void test_SPI_SETDOUBLECLKWIDTH( void )         /*     29 */
     trace("testing SPI_{GET,SET}DOUBLECLKWIDTH\n");
     old_width = GetSystemMetrics( SM_CXDOUBLECLK );
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         char buf[10];
 
@@ -1263,7 +1262,7 @@ static void test_SPI_SETDOUBLECLKHEIGHT( void )        /*     30 */
     trace("testing SPI_{GET,SET}DOUBLECLKHEIGHT\n");
     old_height = GetSystemMetrics( SM_CYDOUBLECLK );
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         char buf[10];
 
@@ -1353,7 +1352,7 @@ static void test_SPI_SETMOUSEBUTTONSWAP( void )        /*     33 */
     trace("testing SPI_{GET,SET}MOUSEBUTTONSWAP\n");
     old_b = GetSystemMetrics( SM_SWAPBUTTON );
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         SetLastError(0xdeadbeef);
         rc=SystemParametersInfoA( SPI_SETMOUSEBUTTONSWAP, vals[i], 0,
@@ -1408,7 +1407,7 @@ static void test_SPI_SETDRAGFULLWINDOWS( void )        /*     37 */
     if (!test_error_msg(rc,"SPI_{GET,SET}DRAGFULLWINDOWS"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -1430,21 +1429,21 @@ static void test_SPI_SETDRAGFULLWINDOWS( void )        /*     37 */
     ok(rc, "***warning*** failed to restore the original value: rc=%d err=%d\n", rc, GetLastError());
 }
 
-#define test_reg_metric( KEY, VAL, val) \
-{   INT regval;\
+#define test_reg_metric( KEY, VAL, val) do { \
+    INT regval;\
     regval = metricfromreg( KEY, VAL, dpi);\
     ok( regval==val, "wrong value \"%s\" in registry %d, expected %d\n", VAL, regval, val);\
-}
-#define test_reg_metric2( KEY1, KEY2, VAL, val) \
-{   INT regval;\
+} while(0)
+
+#define test_reg_metric2( KEY1, KEY2, VAL, val) do { \
+    INT regval;\
     regval = metricfromreg( KEY1, VAL, dpi);\
     if( regval != val) regval = metricfromreg( KEY2, VAL, dpi);\
     ok( regval==val, "wrong value \"%s\" in registry %d, expected %d\n", VAL, regval, val);\
-}
+} while(0)
 
-#define test_reg_font( KEY, VAL, LF) \
-{   LOGFONTA lfreg;\
+#define test_reg_font( KEY, VAL, LF) do { \
+    LOGFONTA lfreg;\
     lffromreg( KEY, VAL, &lfreg);\
     ok( (lfreg.lfHeight < 0 ? (LF).lfHeight == MulDiv( lfreg.lfHeight, dpi, real_dpi ) : \
                 MulDiv( -(LF).lfHeight , 72, dpi) == lfreg.lfHeight )&&\
@@ -1452,9 +1451,9 @@ static void test_SPI_SETDRAGFULLWINDOWS( void )        /*     37 */
         (LF).lfWeight == lfreg.lfWeight &&\
         !strcmp( (LF).lfFaceName, lfreg.lfFaceName)\
         , "wrong value \"%s\" in registry %d, %d\n", VAL, (LF).lfHeight, lfreg.lfHeight);\
-}
+} while(0)
 
-#define TEST_NONCLIENTMETRICS_REG( ncm) \
+#define TEST_NONCLIENTMETRICS_REG( ncm) do { \
 /*FIXME: test_reg_metric2( SPI_SETBORDER_REGKEY2, SPI_SETBORDER_REGKEY, SPI_SETBORDER_VALNAME, (ncm).iBorderWidth);*/\
 test_reg_metric( SPI_METRIC_REGKEY, SPI_SCROLLWIDTH_VALNAME, (ncm).iScrollWidth);\
 test_reg_metric( SPI_METRIC_REGKEY, SPI_SCROLLHEIGHT_VALNAME, (ncm).iScrollHeight);\
@@ -1468,16 +1467,30 @@ test_reg_font( SPI_METRIC_REGKEY, SPI_MENUFONT_VALNAME, (ncm).lfMenuFont);\
 test_reg_font( SPI_METRIC_REGKEY, SPI_CAPTIONFONT_VALNAME, (ncm).lfCaptionFont);\
 test_reg_font( SPI_METRIC_REGKEY, SPI_SMCAPTIONFONT_VALNAME, (ncm).lfSmCaptionFont);\
 test_reg_font( SPI_METRIC_REGKEY, SPI_STATUSFONT_VALNAME, (ncm).lfStatusFont);\
-test_reg_font( SPI_METRIC_REGKEY, SPI_MESSAGEFONT_VALNAME, (ncm).lfMessageFont);
+test_reg_font( SPI_METRIC_REGKEY, SPI_MESSAGEFONT_VALNAME, (ncm).lfMessageFont); } while(0)
 
 /* get text metric height value for the specified logfont */
 static int get_tmheight( LOGFONTA *plf, int flag)
 {
     TEXTMETRICA tm;
+    HDC hdc = GetDC(0);
     HFONT hfont = CreateFontIndirectA( plf);
     hfont = SelectObject( hdc, hfont);
     GetTextMetricsA( hdc, &tm);
     hfont = SelectObject( hdc, hfont);
+    ReleaseDC( 0, hdc );
+    return tm.tmHeight + (flag ? tm.tmExternalLeading : 0);
+}
+
+static int get_tmheightW( LOGFONTW *plf, int flag)
+{
+    TEXTMETRICW tm;
+    HDC hdc = GetDC(0);
+    HFONT hfont = CreateFontIndirectW( plf);
+    hfont = SelectObject( hdc, hfont);
+    GetTextMetricsW( hdc, &tm);
+    hfont = SelectObject( hdc, hfont);
+    ReleaseDC( 0, hdc );
     return tm.tmHeight + (flag ? tm.tmExternalLeading : 0);
 }
 
@@ -1515,8 +1528,7 @@ static void test_SPI_SETNONCLIENTMETRICS( void )               /*     44 */
     Ncmorig.iSmCaptionHeight = metricfromreg( SPI_METRIC_REGKEY, SPI_SMCAPTIONHEIGHT_VALNAME, dpi);
     Ncmorig.iMenuHeight = metricfromreg( SPI_METRIC_REGKEY, SPI_MENUHEIGHT_VALNAME, dpi);
     /* test registry entries */
-    TEST_NONCLIENTMETRICS_REG( Ncmorig)
-    Ncmorig.lfCaptionFont.lfHeight = MulDiv( Ncmorig.lfCaptionFont.lfHeight, real_dpi, dpi );
+    TEST_NONCLIENTMETRICS_REG( Ncmorig);
 
     /* make small changes */
     Ncmnew = Ncmstart;
@@ -1554,7 +1566,7 @@ static void test_SPI_SETNONCLIENTMETRICS( void )               /*     44 */
     rc=SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &Ncmcur, FALSE );
     ok(rc, "SystemParametersInfoA: rc=%d err=%d\n", rc, GetLastError());
     /* test registry entries */
-    TEST_NONCLIENTMETRICS_REG( Ncmcur)
+    TEST_NONCLIENTMETRICS_REG( Ncmcur );
     /* test the system metrics with these settings */
     test_GetSystemMetrics();
     /* now for something invalid: increase the {menu|caption|smcaption} fonts
@@ -1574,7 +1586,7 @@ static void test_SPI_SETNONCLIENTMETRICS( void )               /*     44 */
     ok(rc, "SystemParametersInfoA: rc=%d err=%d\n", rc, GetLastError());
     test_change_message( SPI_SETNONCLIENTMETRICS, 1 );
     /* raw values are in registry */
-    TEST_NONCLIENTMETRICS_REG( Ncmnew)
+    TEST_NONCLIENTMETRICS_REG( Ncmnew );
     /* get them back */
     rc=SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICSA), &Ncmcur, FALSE );
     ok(rc, "SystemParametersInfoA: rc=%d err=%d\n", rc, GetLastError());
@@ -1754,10 +1766,10 @@ static void test_SPI_SETICONMETRICS( void )               /*     46 */
         return;
    /* check some registry values */ 
     regval = metricfromreg( SPI_ICONHORIZONTALSPACING_REGKEY, SPI_ICONHORIZONTALSPACING_VALNAME, dpi);
-    ok( regval==im_orig.iHorzSpacing || broken(regval == -1), /* nt4 */
+    ok( regval==im_orig.iHorzSpacing,
         "wrong value in registry %d, expected %d\n", regval, im_orig.iHorzSpacing);
     regval = metricfromreg( SPI_ICONVERTICALSPACING_REGKEY, SPI_ICONVERTICALSPACING_VALNAME, dpi);
-    ok( regval==im_orig.iVertSpacing || broken(regval == -1), /* nt4 */
+    ok( regval==im_orig.iVertSpacing,
         "wrong value in registry %d, expected %d\n", regval, im_orig.iVertSpacing);
     regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY2, SPI_SETICONTITLEWRAP_VALNAME, dpi);
     if( regval != im_orig.iTitleWrap)
@@ -1915,11 +1927,10 @@ static void test_SPI_SETSHOWSOUNDS( void )             /*     57 */
     trace("testing SPI_{GET,SET}SHOWSOUNDS\n");
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA( SPI_GETSHOWSOUNDS, 0, &old_b, 0 );
-    /* SPI_{GET,SET}SHOWSOUNDS is completely broken on Win9x */
     if (!test_error_msg(rc,"SPI_{GET,SET}SHOWSOUNDS"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -1956,7 +1967,7 @@ static void test_SPI_SETKEYBOARDPREF( void )           /*     69 */
     if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDPREF"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         BOOL v;
 
@@ -1991,7 +2002,7 @@ static void test_SPI_SETSCREENREADER( void )           /*     71 */
     if (!test_error_msg(rc,"SPI_{GET,SET}SCREENREADER"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         BOOL v;
 
@@ -2022,7 +2033,6 @@ static void test_SPI_SETFONTSMOOTHING( void )         /*     75 */
     unsigned int i;
 
     trace("testing SPI_{GET,SET}FONTSMOOTHING\n");
-    if( iswin9x) return; /* 95/98/ME don't seem to implement this fully */ 
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA( SPI_GETFONTSMOOTHING, 0, &old_b, 0 );
     if (!test_error_msg(rc,"SPI_{GET,SET}FONTSMOOTHING"))
@@ -2031,7 +2041,7 @@ static void test_SPI_SETFONTSMOOTHING( void )         /*     75 */
     SystemParametersInfoA( SPI_GETFONTSMOOTHINGCONTRAST, 0, &old_contrast, 0 );
     SystemParametersInfoA( SPI_GETFONTSMOOTHINGORIENTATION, 0, &old_orient, 0 );
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -2108,7 +2118,7 @@ static void test_SPI_SETLOWPOWERACTIVE( void )         /*     85 */
     if (!test_error_msg(rc,"SPI_{GET,SET}LOWPOWERACTIVE"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -2125,9 +2135,7 @@ static void test_SPI_SETLOWPOWERACTIVE( void )         /*     85 */
         v = 0xdeadbeef;
         rc=SystemParametersInfoA( SPI_GETLOWPOWERACTIVE, 0, &v, 0 );
         ok(rc, "%d: rc=%d err=%d\n", i, rc, GetLastError());
-        ok(v == vals[i] ||
-           broken(v == (0xdead0000 | vals[i])) ||  /* win98 only sets the low word */
-           v == 0, /* win2k3 */
+        ok(v == vals[i] || v == 0, /* win2k3 */
            "SPI_GETLOWPOWERACTIVE: got %d instead of 0 or %d\n", v, vals[i]);
     }
 
@@ -2148,7 +2156,7 @@ static void test_SPI_SETPOWEROFFACTIVE( void )         /*     86 */
     if (!test_error_msg(rc,"SPI_{GET,SET}POWEROFFACTIVE"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -2165,9 +2173,7 @@ static void test_SPI_SETPOWEROFFACTIVE( void )         /*     86 */
         v = 0xdeadbeef;
         rc=SystemParametersInfoA( SPI_GETPOWEROFFACTIVE, 0, &v, 0 );
         ok(rc, "%d: rc=%d err=%d\n", i, rc, GetLastError());
-        ok(v == vals[i] ||
-           broken(v == (0xdead0000 | vals[i])) ||  /* win98 only sets the low word */
-           v == 0, /* win2k3 */
+        ok(v == vals[i] || v == 0, /* win2k3 */
            "SPI_GETPOWEROFFACTIVE: got %d instead of 0 or %d\n", v, vals[i]);
     }
 
@@ -2188,7 +2194,7 @@ static void test_SPI_SETSNAPTODEFBUTTON( void )         /*     95 */
     if (!test_error_msg(rc,"SPI_GETSNAPTODEFBUTTON"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
 
@@ -2220,13 +2226,10 @@ static void test_SPI_SETMOUSEHOVERWIDTH( void )      /*     99 */
     trace("testing SPI_{GET,SET}MOUSEHOVERWIDTH\n");
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA( SPI_GETMOUSEHOVERWIDTH, 0, &old_width, 0 );
-    /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
-    * what MSDN states (Verified on Win98SE)
-    */
     if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERWIDTH"))
         return;
-    
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2260,13 +2263,10 @@ static void test_SPI_SETMOUSEHOVERHEIGHT( void )      /*     101 */
     trace("testing SPI_{GET,SET}MOUSEHOVERHEIGHT\n");
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA( SPI_GETMOUSEHOVERHEIGHT, 0, &old_height, 0 );
-    /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
-     * what MSDN states (Verified on Win98SE)
-     */
     if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERHEIGHT"))
         return;
-    
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2304,13 +2304,10 @@ static void test_SPI_SETMOUSEHOVERTIME( void )      /*     103 */
     trace("testing SPI_{GET,SET}MOUSEHOVERTIME\n");
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA( SPI_GETMOUSEHOVERTIME, 0, &old_time, 0 );
-    /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
-     * what MSDN states (Verified on Win98SE)
-     */    
     if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERTIME"))
         return;
-    
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2349,7 +2346,7 @@ static void test_SPI_SETWHEELSCROLLLINES( void )      /*     105 */
     if (!test_error_msg(rc,"SPI_{GET,SET}WHEELSCROLLLINES"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2388,7 +2385,7 @@ static void test_SPI_SETMENUSHOWDELAY( void )      /*     107 */
     if (!test_error_msg(rc,"SPI_{GET,SET}MENUSHOWDELAY"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2427,7 +2424,7 @@ static void test_SPI_SETWHEELSCROLLCHARS( void )      /*     108 */
     if (!test_error_msg(rc,"SPI_{GET,SET}WHEELSCROLLCHARS"))
         return;
 
-    for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+    for (i=0;i<ARRAY_SIZE(vals);i++)
     {
         UINT v;
         char buf[10];
@@ -2459,9 +2456,6 @@ static void test_SPI_SETWALLPAPER( void )              /*   115 */
     trace("testing SPI_{GET,SET}DESKWALLPAPER\n");
     SetLastError(0xdeadbeef);
     rc=SystemParametersInfoA(SPI_GETDESKWALLPAPER, 260, oldval, 0);
-    /* SPI_{GET,SET}DESKWALLPAPER is completely broken on Win9x and
-     * unimplemented on NT4
-     */
     if (!test_error_msg(rc,"SPI_{GET,SET}DESKWALLPAPER"))
         return;
 
@@ -2500,7 +2494,7 @@ static void test_WM_DISPLAYCHANGE(void)
 
     displaychange_sem = CreateSemaphoreW(NULL, 0, 1, NULL);
 
-    for(i = 0; i < sizeof(test_bpps)/sizeof(test_bpps[0]); i++) {
+    for(i = 0; i < ARRAY_SIZE(test_bpps); i++) {
         last_bpp = -1;
 
         memset(&mode, 0, sizeof(mode));
@@ -2737,8 +2731,10 @@ static void test_GetSystemMetrics( void)
 
     ncm.cbSize = sizeof(ncm); /* Vista added padding */
     SetLastError(0xdeadbeef);
+    ncm.iPaddedBorderWidth = 0xcccc;
     rc = SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
     ok(rc || broken(!rc) /* before Vista */, "SystemParametersInfoA failed\n");
+    if (rc) ok( ncm.iPaddedBorderWidth == 0, "wrong iPaddedBorderWidth %u\n", ncm.iPaddedBorderWidth );
 
     minim.cbSize = sizeof( minim);
     ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICSA, iPaddedBorderWidth);
@@ -2925,6 +2921,165 @@ static void test_GetSystemMetrics( void)
     DeleteDC(hdc);
 }
 
+static void compare_font( const LOGFONTW *lf1, const LOGFONTW *lf2, int dpi, int custom_dpi, int line )
+{
+    ok_(__FILE__,line)( lf1->lfHeight == MulDiv( lf2->lfHeight, dpi, custom_dpi ),
+                        "wrong lfHeight %d vs %d\n", lf1->lfHeight, lf2->lfHeight );
+    ok_(__FILE__,line)( abs( lf1->lfWidth - MulDiv( lf2->lfWidth, dpi, custom_dpi )) <= 1,
+                        "wrong lfWidth %d vs %d\n", lf1->lfWidth, lf2->lfWidth );
+    ok_(__FILE__,line)( !memcmp( &lf1->lfEscapement, &lf2->lfEscapement,
+                                 offsetof( LOGFONTW, lfFaceName ) - offsetof( LOGFONTW, lfEscapement )),
+                        "font differs\n" );
+    ok_(__FILE__,line)( !lstrcmpW( lf1->lfFaceName, lf2->lfFaceName ), "wrong face name %s vs %s\n",
+                        wine_dbgstr_w( lf1->lfFaceName ), wine_dbgstr_w( lf2->lfFaceName ));
+}
+
+static void test_metrics_for_dpi( int custom_dpi )
+{
+    int i, val;
+    NONCLIENTMETRICSW ncm1, ncm2;
+    ICONMETRICSW im1, im2;
+    LOGFONTW lf1, lf2;
+    BOOL ret;
+
+    if (!pSystemParametersInfoForDpi)
+    {
+        win_skip( "custom dpi metrics not supported\n" );
+        return;
+    }
+
+    ncm1.cbSize = sizeof(ncm1);
+    ret = SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof(ncm1), &ncm1, FALSE );
+    ok( ret, "SystemParametersInfoW failed err %u\n", GetLastError() );
+    ncm2.cbSize = sizeof(ncm2);
+    ret = pSystemParametersInfoForDpi( SPI_GETNONCLIENTMETRICS, sizeof(ncm2), &ncm2, FALSE, custom_dpi );
+    ok( ret, "SystemParametersInfoForDpi failed err %u\n", GetLastError() );
+
+    for (i = 0; i < 92; i++)
+    {
+        int ret1 = GetSystemMetrics( i );
+        int ret2 = pGetSystemMetricsForDpi( i, custom_dpi );
+        switch (i)
+        {
+        case SM_CXVSCROLL:
+        case SM_CYHSCROLL:
+        case SM_CYVTHUMB:
+        case SM_CXHTHUMB:
+        case SM_CXICON:
+        case SM_CYICON:
+        case SM_CYVSCROLL:
+        case SM_CXHSCROLL:
+        case SM_CYSIZE:
+        case SM_CXICONSPACING:
+        case SM_CYICONSPACING:
+        case SM_CXSMSIZE:
+        case SM_CYSMSIZE:
+        case SM_CYMENUSIZE:
+            ok( ret1 == MulDiv( ret2, dpi, custom_dpi ), "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        case SM_CXSIZE:
+            ok( ret1 == ncm1.iCaptionWidth && ret2 == ncm2.iCaptionWidth,
+                "%u: wrong value %u vs %u caption %u vs %u\n",
+                i, ret1, ret2, ncm1.iCaptionWidth, ncm2.iCaptionWidth );
+            break;
+        case SM_CXCURSOR:
+        case SM_CYCURSOR:
+            val = MulDiv( 32, custom_dpi, USER_DEFAULT_SCREEN_DPI );
+            if (val < 48) val = 32;
+            else if (val < 64) val = 48;
+            else val = 64;
+            ok( val == ret2, "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        case SM_CYCAPTION:
+        case SM_CYSMCAPTION:
+        case SM_CYMENU:
+            ok( ret1 - 1 == MulDiv( ret2 - 1, dpi, custom_dpi ), "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        case SM_CXMENUSIZE:
+            ok( ret1 / 8 == MulDiv( ret2, dpi, custom_dpi ) / 8, "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        case SM_CXFRAME:
+        case SM_CYFRAME:
+            ok( ret1 == ncm1.iBorderWidth + 3 && ret2 == ncm2.iBorderWidth + 3,
+                "%u: wrong value %u vs %u borders %u+%u vs %u+%u\n", i, ret1, ret2,
+                ncm1.iBorderWidth, ncm1.iPaddedBorderWidth, ncm2.iBorderWidth, ncm2.iPaddedBorderWidth );
+            break;
+        case SM_CXSMICON:
+        case SM_CYSMICON:
+            ok( ret1 == (MulDiv( 16, dpi, USER_DEFAULT_SCREEN_DPI ) & ~1) &&
+                ret2 == (MulDiv( 16, custom_dpi, USER_DEFAULT_SCREEN_DPI ) & ~1),
+                "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        case SM_CXMENUCHECK:
+        case SM_CYMENUCHECK:
+            ok( ret1 == ((get_tmheightW( &ncm1.lfMenuFont, 1 ) - 1) | 1) &&
+                ret2 == ((get_tmheightW( &ncm2.lfMenuFont, 1 ) - 1) | 1),
+                "%u: wrong value %u vs %u font %u vs %u\n", i, ret1, ret2,
+                get_tmheightW( &ncm1.lfMenuFont, 1 ), get_tmheightW( &ncm2.lfMenuFont, 1 ));
+            break;
+        default:
+            ok( ret1 == ret2, "%u: wrong value %u vs %u\n", i, ret1, ret2 );
+            break;
+        }
+    }
+    im1.cbSize = sizeof(im1);
+    ret = SystemParametersInfoW( SPI_GETICONMETRICS, sizeof(im1), &im1, FALSE );
+    ok( ret, "SystemParametersInfoW failed err %u\n", GetLastError() );
+    im2.cbSize = sizeof(im2);
+    ret = pSystemParametersInfoForDpi( SPI_GETICONMETRICS, sizeof(im2), &im2, FALSE, custom_dpi );
+    ok( ret, "SystemParametersInfoForDpi failed err %u\n", GetLastError() );
+    ok( im1.iHorzSpacing == MulDiv( im2.iHorzSpacing, dpi, custom_dpi ), "wrong iHorzSpacing %u vs %u\n",
+        im1.iHorzSpacing, im2.iHorzSpacing );
+    ok( im1.iVertSpacing == MulDiv( im2.iVertSpacing, dpi, custom_dpi ), "wrong iVertSpacing %u vs %u\n",
+        im1.iVertSpacing, im2.iVertSpacing );
+    ok( im1.iTitleWrap == im2.iTitleWrap, "wrong iTitleWrap %u vs %u\n",
+        im1.iTitleWrap, im2.iTitleWrap );
+    compare_font( &im1.lfFont, &im2.lfFont, dpi, custom_dpi, __LINE__ );
+
+    ret = SystemParametersInfoW( SPI_GETICONTITLELOGFONT, sizeof(lf1), &lf1, FALSE );
+    ok( ret, "SystemParametersInfoW failed err %u\n", GetLastError() );
+    ret = pSystemParametersInfoForDpi( SPI_GETICONTITLELOGFONT, sizeof(lf2), &lf2, FALSE, custom_dpi );
+    ok( ret, "SystemParametersInfoForDpi failed err %u\n", GetLastError() );
+    compare_font( &lf1, &lf2, dpi, custom_dpi, __LINE__ );
+
+    /* on high-dpi iPaddedBorderWidth is used in addition to iBorderWidth */
+    ok( ncm1.iBorderWidth + ncm1.iPaddedBorderWidth == MulDiv( ncm2.iBorderWidth + ncm2.iPaddedBorderWidth, dpi, custom_dpi ),
+        "wrong iBorderWidth %u+%u vs %u+%u\n",
+        ncm1.iBorderWidth, ncm1.iPaddedBorderWidth, ncm2.iBorderWidth, ncm2.iPaddedBorderWidth );
+    ok( ncm1.iScrollWidth == MulDiv( ncm2.iScrollWidth, dpi, custom_dpi ),
+        "wrong iScrollWidth %u vs %u\n", ncm1.iScrollWidth, ncm2.iScrollWidth );
+    ok( ncm1.iScrollHeight == MulDiv( ncm2.iScrollHeight, dpi, custom_dpi ),
+        "wrong iScrollHeight %u vs %u\n", ncm1.iScrollHeight, ncm2.iScrollHeight );
+    ok( ((ncm1.iCaptionWidth + 1) & ~1) == ((MulDiv( ncm2.iCaptionWidth, dpi, custom_dpi ) + 1) & ~1),
+        "wrong iCaptionWidth %u vs %u\n", ncm1.iCaptionWidth, ncm2.iCaptionWidth );
+    ok( ncm1.iCaptionHeight == MulDiv( ncm2.iCaptionHeight, dpi, custom_dpi ),
+        "wrong iCaptionHeight %u vs %u\n", ncm1.iCaptionHeight, ncm2.iCaptionHeight );
+    compare_font( &ncm1.lfCaptionFont, &ncm2.lfCaptionFont, dpi, custom_dpi, __LINE__ );
+    ok( ncm1.iSmCaptionHeight == MulDiv( ncm2.iSmCaptionHeight, dpi, custom_dpi ),
+        "wrong iSmCaptionHeight %u vs %u\n", ncm1.iSmCaptionHeight, ncm2.iSmCaptionHeight );
+    compare_font( &ncm1.lfSmCaptionFont, &ncm2.lfSmCaptionFont, dpi, custom_dpi, __LINE__ );
+    ok( ncm1.iMenuHeight == MulDiv( ncm2.iMenuHeight, dpi, custom_dpi ),
+        "wrong iMenuHeight %u vs %u\n", ncm1.iMenuHeight, ncm2.iMenuHeight );
+    /* iSmCaptionWidth and iMenuWidth apparently need to be multiples of 8 */
+    ok( ncm1.iSmCaptionWidth / 8 == MulDiv( ncm2.iSmCaptionWidth, dpi, custom_dpi ) / 8,
+        "wrong iSmCaptionWidth %u vs %u\n", ncm1.iSmCaptionWidth, ncm2.iSmCaptionWidth );
+    ok( ncm1.iMenuWidth / 8 == MulDiv( ncm2.iMenuWidth, dpi, custom_dpi ) / 8,
+        "wrong iMenuWidth %u vs %u\n", ncm1.iMenuWidth, ncm2.iMenuWidth );
+    compare_font( &ncm1.lfMenuFont, &ncm2.lfMenuFont, dpi, custom_dpi, __LINE__ );
+    compare_font( &ncm1.lfStatusFont, &ncm2.lfStatusFont, dpi, custom_dpi, __LINE__ );
+    compare_font( &ncm1.lfMessageFont, &ncm2.lfMessageFont, dpi, custom_dpi, __LINE__ );
+
+    for (i = 1; i < 120; i++)
+    {
+        if (i == SPI_GETICONTITLELOGFONT || i == SPI_GETNONCLIENTMETRICS || i == SPI_GETICONMETRICS)
+            continue;
+        SetLastError( 0xdeadbeef );
+        ret = pSystemParametersInfoForDpi( i, 0, &val, 0, custom_dpi );
+        ok( !ret, "%u: SystemParametersInfoForDpi succeeded\n", i );
+            ok( GetLastError() == ERROR_INVALID_PARAMETER, "%u: wrong error %u\n", i, GetLastError() );
+    }
+}
+
 static void test_EnumDisplaySettings(void)
 {
     DEVMODEA devmode;
@@ -2933,13 +3088,11 @@ static void test_EnumDisplaySettings(void)
     DWORD num;
 
     memset(&devmode, 0, sizeof(devmode));
-    /* Win95 doesn't handle ENUM_CURRENT_SETTINGS correctly */
     EnumDisplaySettingsA(NULL, ENUM_CURRENT_SETTINGS, &devmode);
 
     hdc = GetDC(0);
     val = GetDeviceCaps(hdc, BITSPIXEL);
-    ok(devmode.dmBitsPerPel == val ||
-        broken(devmode.dmDeviceName[0] == 0), /* Win95 */
+    ok(devmode.dmBitsPerPel == val,
         "GetDeviceCaps(BITSPIXEL) returned %d, EnumDisplaySettings returned %d\n",
         val, devmode.dmBitsPerPel);
 
@@ -2988,6 +3141,378 @@ static void test_GetSysColorBrush(void)
         win_skip("COLOR_MENUBAR unsupported\n");
 }
 
+static void test_dpi_stock_objects( HDC hdc )
+{
+    DPI_AWARENESS_CONTEXT context;
+    HGDIOBJ obj[STOCK_LAST + 1], obj2[STOCK_LAST + 1];
+    LOGFONTW lf, lf2;
+    UINT i, dpi;
+
+    context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    ok( dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", dpi );
+    ok( !pIsProcessDPIAware(), "not aware\n" );
+    for (i = 0; i <= STOCK_LAST; i++) obj[i] = GetStockObject( i );
+
+    pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_SYSTEM_AWARE );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    ok( dpi == real_dpi, "wrong dpi %u\n", dpi );
+    ok( pIsProcessDPIAware(), "not aware\n" );
+    for (i = 0; i <= STOCK_LAST; i++) obj2[i] = GetStockObject( i );
+
+    for (i = 0; i <= STOCK_LAST; i++)
+    {
+        switch (i)
+        {
+        case OEM_FIXED_FONT:
+        case SYSTEM_FIXED_FONT:
+            ok( obj[i] != obj2[i], "%u: same object\n", i );
+            break;
+        case SYSTEM_FONT:
+        case DEFAULT_GUI_FONT:
+            ok( obj[i] != obj2[i], "%u: same object\n", i );
+            GetObjectW( obj[i], sizeof(lf), &lf );
+            GetObjectW( obj2[i], sizeof(lf2), &lf2 );
+            ok( lf.lfHeight == MulDiv( lf2.lfHeight, USER_DEFAULT_SCREEN_DPI, real_dpi ),
+                "%u: wrong height %d / %d\n", i, lf.lfHeight, lf2.lfHeight );
+            break;
+        default:
+            ok( obj[i] == obj2[i], "%u: different object\n", i );
+            break;
+        }
+    }
+
+    pSetThreadDpiAwarenessContext( context );
+}
+
+static void scale_point_dpi( POINT *pt, UINT src_dpi, UINT target_dpi )
+{
+    pt->x = MulDiv( pt->x, target_dpi, src_dpi );
+    pt->y = MulDiv( pt->y, target_dpi, src_dpi );
+}
+
+static void scale_rect_dpi( RECT *rect, UINT src_dpi, UINT target_dpi )
+{
+    rect->left = MulDiv( rect->left, target_dpi, src_dpi );
+    rect->top = MulDiv( rect->top, target_dpi, src_dpi );
+    rect->right = MulDiv( rect->right, target_dpi, src_dpi );
+    rect->bottom = MulDiv( rect->bottom, target_dpi, src_dpi );
+}
+
+static void scale_point_dpi_aware( POINT *pt, DPI_AWARENESS from, DPI_AWARENESS to )
+{
+    if (from == DPI_AWARENESS_UNAWARE && to != DPI_AWARENESS_UNAWARE)
+        scale_point_dpi( pt, USER_DEFAULT_SCREEN_DPI, real_dpi );
+    else if (from != DPI_AWARENESS_UNAWARE && to == DPI_AWARENESS_UNAWARE)
+        scale_point_dpi( pt, real_dpi, USER_DEFAULT_SCREEN_DPI );
+}
+
+static void scale_rect_dpi_aware( RECT *rect, DPI_AWARENESS from, DPI_AWARENESS to )
+{
+    if (from == DPI_AWARENESS_UNAWARE && to != DPI_AWARENESS_UNAWARE)
+        scale_rect_dpi( rect, USER_DEFAULT_SCREEN_DPI, real_dpi );
+    else if (from != DPI_AWARENESS_UNAWARE && to == DPI_AWARENESS_UNAWARE)
+        scale_rect_dpi( rect, real_dpi, USER_DEFAULT_SCREEN_DPI );
+}
+
+static void test_dpi_mapping(void)
+{
+    HWND hwnd, child;
+    HDC hdc;
+    UINT win_dpi, units;
+    POINT point;
+    BOOL ret;
+    HRGN rgn, update;
+    RECT rect, orig, client, desktop, expect;
+    ULONG_PTR i, j, k;
+    WINDOWPLACEMENT wpl_orig, wpl;
+    HMONITOR monitor;
+    MONITORINFO mon_info;
+    DPI_AWARENESS_CONTEXT context;
+
+    if (!pLogicalToPhysicalPointForPerMonitorDPI)
+    {
+        win_skip( "LogicalToPhysicalPointForPerMonitorDPI not supported\n" );
+        return;
+    }
+    context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
+    GetWindowRect( GetDesktopWindow(), &desktop );
+    for (i = DPI_AWARENESS_UNAWARE; i <= DPI_AWARENESS_PER_MONITOR_AWARE; i++)
+    {
+        pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i );
+        /* test desktop rect */
+        GetWindowRect( GetDesktopWindow(), &rect );
+        expect = desktop;
+        if (i == DPI_AWARENESS_UNAWARE) scale_rect_dpi( &expect, real_dpi, USER_DEFAULT_SCREEN_DPI );
+        ok( EqualRect( &expect, &rect ), "%lu: wrong desktop rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+        SetRect( &rect, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ));
+        ok( EqualRect( &expect, &rect ), "%lu: wrong desktop rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+        SetRect( &rect, 0, 0, GetSystemMetrics( SM_CXVIRTUALSCREEN ), GetSystemMetrics( SM_CYVIRTUALSCREEN ));
+        ok( EqualRect( &expect, &rect ), "%lu: wrong virt desktop rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+        SetRect( &rect, 0, 0, 1, 1 );
+        monitor = MonitorFromRect( &rect, MONITOR_DEFAULTTOPRIMARY );
+        ok( monitor != 0, "failed to get monitor\n" );
+        mon_info.cbSize = sizeof(mon_info);
+        ok( GetMonitorInfoW( monitor, &mon_info ), "GetMonitorInfoExW failed\n" );
+        ok( EqualRect( &expect, &mon_info.rcMonitor ), "%lu: wrong monitor rect %s expected %s\n",
+            i, wine_dbgstr_rect(&mon_info.rcMonitor), wine_dbgstr_rect(&expect) );
+        hdc = CreateDCA( "display", NULL, NULL, NULL );
+        SetRect( &rect, 0, 0, GetDeviceCaps( hdc, HORZRES ), GetDeviceCaps( hdc, VERTRES ));
+        ok( EqualRect( &expect, &rect ), "%lu: wrong caps desktop rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+        SetRect( &rect, 0, 0, GetDeviceCaps( hdc, DESKTOPHORZRES ), GetDeviceCaps( hdc, DESKTOPVERTRES ));
+        ok( EqualRect( &desktop, &rect ), "%lu: wrong caps virt desktop rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&desktop) );
+        DeleteDC( hdc );
+        /* test message window rect */
+        hwnd = CreateWindowA( "SysParamsTestClass", "test", WS_CHILD,
+                              10, 10, 20, 20, HWND_MESSAGE, 0, GetModuleHandleA(0), NULL );
+        GetWindowRect( GetAncestor( hwnd, GA_PARENT ), &rect );
+        SetRect( &expect, 0, 0, 100, 100 );
+        if (i == DPI_AWARENESS_UNAWARE) scale_rect_dpi( &expect, real_dpi, USER_DEFAULT_SCREEN_DPI );
+        ok( EqualRect( &expect, &rect ), "%lu: wrong message rect %s expected %s\n",
+            i, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+        DestroyWindow( hwnd );
+    }
+    for (i = DPI_AWARENESS_UNAWARE; i <= DPI_AWARENESS_PER_MONITOR_AWARE; i++)
+    {
+        pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i );
+        hwnd = CreateWindowA( "SysParamsTestClass", "test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+                              193, 177, 295, 303, 0, 0, GetModuleHandleA(0), NULL );
+        ok( hwnd != 0, "creating window failed err %u\n", GetLastError());
+        child = CreateWindowA( "SysParamsTestClass", "child", WS_CHILD | WS_VISIBLE,
+                               50, 60, 70, 80, hwnd, 0, GetModuleHandleA(0), NULL );
+        ok( child != 0, "creating child failed err %u\n", GetLastError());
+        GetWindowRect( hwnd, &orig );
+        SetRect( &rect, 0, 0, 0, 0 );
+        pAdjustWindowRectExForDpi( &rect, WS_OVERLAPPEDWINDOW, FALSE, 0, pGetDpiForWindow( hwnd ));
+        SetRect( &client, orig.left - rect.left, orig.top - rect.top,
+                 orig.right - rect.right, orig.bottom - rect.bottom );
+        ShowWindow( hwnd, SW_MINIMIZE );
+        ShowWindow( hwnd, SW_RESTORE );
+        GetWindowPlacement( hwnd, &wpl_orig );
+        units = GetDialogBaseUnits();
+
+        for (j = DPI_AWARENESS_UNAWARE; j <= DPI_AWARENESS_PER_MONITOR_AWARE; j++)
+        {
+            pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+            /* test window rect */
+            GetWindowRect( hwnd, &rect );
+            expect = orig;
+            scale_rect_dpi_aware( &expect, i, j );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong window rect %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            /* test client rect */
+            GetClientRect( hwnd, &rect );
+            expect = client;
+            OffsetRect( &expect, -expect.left, -expect.top );
+            scale_rect_dpi_aware( &expect, i, j );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong client rect %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            /* test window placement */
+            GetWindowPlacement( hwnd, &wpl );
+            point = wpl_orig.ptMinPosition;
+            if (point.x != -1 || point.y != -1) scale_point_dpi_aware( &point, i, j );
+            ok( wpl.ptMinPosition.x == point.x && wpl.ptMinPosition.y == point.y,
+                "%lu/%lu: wrong placement min pos %d,%d expected %d,%d\n", i, j,
+                wpl.ptMinPosition.x, wpl.ptMinPosition.y, point.x, point.y );
+            point = wpl_orig.ptMaxPosition;
+            if (point.x != -1 || point.y != -1) scale_point_dpi_aware( &point, i, j );
+            ok( wpl.ptMaxPosition.x == point.x && wpl.ptMaxPosition.y == point.y,
+                "%lu/%lu: wrong placement max pos %d,%d expected %d,%d\n", i, j,
+                wpl.ptMaxPosition.x, wpl.ptMaxPosition.y, point.x, point.y );
+            expect = wpl_orig.rcNormalPosition;
+            scale_rect_dpi_aware( &expect, i, j );
+            ok( EqualRect( &wpl.rcNormalPosition, &expect ),
+                "%lu/%lu: wrong placement rect %s expect %s\n", i, j,
+                wine_dbgstr_rect(&wpl.rcNormalPosition), wine_dbgstr_rect(&expect));
+            /* test DC rect */
+            hdc = GetDC( hwnd );
+            GetClipBox( hdc, &rect );
+            SetRect( &expect, 0, 0, client.right - client.left, client.bottom - client.top );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong clip box %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            /* test DC resolution */
+            SetRect( &rect, 0, 0, GetDeviceCaps( hdc, HORZRES ), GetDeviceCaps( hdc, VERTRES ));
+            expect = desktop;
+            if (j == DPI_AWARENESS_UNAWARE) scale_rect_dpi( &expect, real_dpi, USER_DEFAULT_SCREEN_DPI );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong DC resolution %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            SetRect( &rect, 0, 0, GetDeviceCaps( hdc, DESKTOPHORZRES ), GetDeviceCaps( hdc, DESKTOPVERTRES ));
+            ok( EqualRect( &desktop, &rect ), "%lu/%lu: wrong desktop resolution %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&desktop) );
+            ReleaseDC( hwnd, hdc );
+            /* test DC win rect */
+            hdc = GetWindowDC( hwnd );
+            GetClipBox( hdc, &rect );
+            SetRect( &expect, 0, 0, 295, 303 );
+            todo_wine
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong clip box win DC %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            ReleaseDC( hwnd, hdc );
+            /* test window invalidation */
+            UpdateWindow( hwnd );
+            update = CreateRectRgn( 0, 0, 0, 0 );
+            ret = GetUpdateRgn( hwnd, update, FALSE );
+            ok( ret == NULLREGION, "update region not empty\n" );
+            rgn = CreateRectRgn( 20, 20, 25, 25 );
+            for (k = DPI_AWARENESS_UNAWARE; k <= DPI_AWARENESS_PER_MONITOR_AWARE; k++)
+            {
+                pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~k );
+                RedrawWindow( hwnd, 0, rgn, RDW_INVALIDATE );
+                pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+                GetUpdateRgn( hwnd, update, FALSE );
+                GetRgnBox( update, &rect );
+                SetRect( &expect, 20, 20, 25, 25 );
+                ok( EqualRect( &expect, &rect ), "%lu/%lu/%lu: wrong update region %s expected %s\n",
+                    i, j, k, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+                GetUpdateRect( hwnd, &rect, FALSE );
+                scale_rect_dpi_aware( &expect, i, j );
+                ok( EqualRect( &expect, &rect ), "%lu/%lu/%lu: wrong update rect %s expected %s\n",
+                    i, j, k, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+                UpdateWindow( hwnd );
+            }
+            for (k = DPI_AWARENESS_UNAWARE; k <= DPI_AWARENESS_PER_MONITOR_AWARE; k++)
+            {
+                RedrawWindow( hwnd, 0, rgn, RDW_INVALIDATE );
+                pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~k );
+                GetUpdateRgn( hwnd, update, FALSE );
+                pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+                GetRgnBox( update, &rect );
+                SetRect( &expect, 20, 20, 25, 25 );
+                ok( EqualRect( &expect, &rect ), "%lu/%lu/%lu: wrong update region %s expected %s\n",
+                    i, j, k, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+                GetUpdateRect( hwnd, &rect, FALSE );
+                scale_rect_dpi_aware( &expect, i, j );
+                ok( EqualRect( &expect, &rect ), "%lu/%lu/%lu: wrong update rect %s expected %s\n",
+                    i, j, k, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+                UpdateWindow( hwnd );
+            }
+            /* test desktop window invalidation */
+            pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
+            GetClientRect( hwnd, &rect );
+            InflateRect( &rect, -50, -50 );
+            expect = rect;
+            MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 );
+            pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+            RedrawWindow( 0, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN );
+            GetUpdateRgn( hwnd, update, TRUE );
+            GetRgnBox( update, &rect );
+            if (i == DPI_AWARENESS_UNAWARE) scale_rect_dpi( &expect, real_dpi, USER_DEFAULT_SCREEN_DPI );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong update region %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            GetUpdateRect( hwnd, &rect, FALSE );
+            scale_rect_dpi_aware( &expect, i, j );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong update rect %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            UpdateWindow( hwnd );
+            DeleteObject( update );
+            /* test dialog units */
+            ret = GetDialogBaseUnits();
+            point.x = LOWORD( units );
+            point.y = HIWORD( units );
+            scale_point_dpi_aware( &point, i, j );
+            ok( LOWORD(ret) == point.x && HIWORD(ret) == point.y, "%lu/%lu: wrong units %d,%d / %d,%d\n",
+                i, j, LOWORD(ret), HIWORD(ret), point.x, point.y );
+            /* test window points mapping */
+            SetRect( &rect, 0, 0, 100, 100 );
+            rect.right = rect.left + 100;
+            rect.bottom = rect.top + 100;
+            MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 );
+            expect = client;
+            scale_rect_dpi_aware( &expect, i, j );
+            expect.right = expect.left + 100;
+            expect.bottom = expect.top + 100;
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong MapWindowPoints rect %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            SetRect( &rect, 50, 60, 70, 80 );
+            scale_rect_dpi_aware( &rect, i, j );
+            SetRect( &expect, 40, 30, 60, 80 );
+            OffsetRect( &expect, -rect.left, -rect.top );
+            SetRect( &rect, 40, 30, 60, 80 );
+            MapWindowPoints( hwnd, child, (POINT *)&rect, 2 );
+            ok( EqualRect( &expect, &rect ), "%lu/%lu: wrong MapWindowPoints child rect %s expected %s\n",
+                i, j, wine_dbgstr_rect(&rect), wine_dbgstr_rect(&expect) );
+            /* test logical<->physical coords mapping */
+            win_dpi = pGetDpiForWindow( hwnd );
+            if (i == DPI_AWARENESS_UNAWARE)
+                ok( win_dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", win_dpi );
+            else if (i == DPI_AWARENESS_SYSTEM_AWARE)
+                ok( win_dpi == real_dpi, "wrong dpi %u / %u\n", win_dpi, real_dpi );
+            point.x = 373;
+            point.y = 377;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI failed\n", i, j );
+            ok( point.x == MulDiv( 373, real_dpi, win_dpi ) &&
+                point.y == MulDiv( 377, real_dpi, win_dpi ),
+                "%lu/%lu: wrong pos %d,%d dpi %u\n", i, j, point.x, point.y, win_dpi );
+            point.x = 405;
+            point.y = 423;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI failed\n", i, j );
+            ok( point.x == MulDiv( 405, win_dpi, real_dpi ) &&
+                point.y == MulDiv( 423, win_dpi, real_dpi ),
+                "%lu/%lu: wrong pos %d,%d dpi %u\n", i, j, point.x, point.y, win_dpi );
+            /* point outside the window fails, but note that Windows (wrongly) checks against the
+             * window rect transformed relative to the thread's awareness */
+            GetWindowRect( hwnd, &rect );
+            point.x = rect.left - 1;
+            point.y = rect.top;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x++;
+            point.y--;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.y++;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI failed\n", i, j );
+            point.x = rect.right;
+            point.y = rect.bottom + 1;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x++;
+            point.y--;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x--;
+            ret = pLogicalToPhysicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: LogicalToPhysicalPointForPerMonitorDPI failed\n", i, j );
+            /* get physical window rect */
+            pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
+            GetWindowRect( hwnd, &rect );
+            pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+            point.x = rect.left - 1;
+            point.y = rect.top;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x++;
+            point.y--;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.y++;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI failed\n", i, j );
+            point.x = rect.right;
+            point.y = rect.bottom + 1;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x++;
+            point.y--;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( !ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI succeeded\n", i, j );
+            point.x--;
+            ret = pPhysicalToLogicalPointForPerMonitorDPI( hwnd, &point );
+            ok( ret, "%lu/%lu: PhysicalToLogicalPointForPerMonitorDPI failed\n", i, j );
+        }
+        DestroyWindow( hwnd );
+    }
+    pSetThreadDpiAwarenessContext( context );
+}
+
 static void test_dpi_aware(void)
 {
     BOOL ret;
@@ -3006,6 +3531,347 @@ static void test_dpi_aware(void)
 
     dpi = real_dpi;
     test_GetSystemMetrics();
+    test_metrics_for_dpi( 96 );
+    test_metrics_for_dpi( 192 );
+}
+
+static void test_dpi_context(void)
+{
+    DPI_AWARENESS awareness;
+    DPI_AWARENESS_CONTEXT context;
+    ULONG_PTR i, flags;
+    BOOL ret;
+    UINT dpi;
+    HDC hdc = GetDC( 0 );
+
+    context = pGetThreadDpiAwarenessContext();
+    /* Windows 10 >= 1709 adds extra 0x6000 flags */
+    flags = (ULONG_PTR)context & 0x6000;
+    todo_wine
+        ok( context == (DPI_AWARENESS_CONTEXT)(0x10 | flags), "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    todo_wine
+        ok( awareness == DPI_AWARENESS_UNAWARE, "wrong awareness %u\n", awareness );
+    todo_wine
+        ok( !pIsProcessDPIAware(), "already aware\n" );
+    dpi = pGetDpiForSystem();
+    todo_wine_if (real_dpi != USER_DEFAULT_SCREEN_DPI)
+        ok( dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", dpi );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    todo_wine_if (real_dpi != USER_DEFAULT_SCREEN_DPI)
+        ok( dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", dpi );
+    SetLastError( 0xdeadbeef );
+    ret = pSetProcessDpiAwarenessContext( NULL );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    ret = pSetProcessDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)-6 );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    ret = pSetProcessDpiAwarenessContext( DPI_AWARENESS_CONTEXT_SYSTEM_AWARE );
+    todo_wine
+    ok( ret, "got %d\n", ret );
+    ok( pIsProcessDPIAware(), "not aware\n" );
+    real_dpi = pGetDpiForSystem();
+    SetLastError( 0xdeadbeef );
+    ret = pSetProcessDpiAwarenessContext( DPI_AWARENESS_CONTEXT_SYSTEM_AWARE );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_ACCESS_DENIED, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    ret = pSetProcessDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_ACCESS_DENIED, "wrong error %u\n", GetLastError() );
+
+    ret = pSetProcessDpiAwarenessInternal( DPI_AWARENESS_INVALID );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    ret = pSetProcessDpiAwarenessInternal( DPI_AWARENESS_UNAWARE );
+    ok( !ret, "got %d\n", ret );
+    ok( GetLastError() == ERROR_ACCESS_DENIED, "wrong error %u\n", GetLastError() );
+    ret = pGetProcessDpiAwarenessInternal( 0, &awareness );
+    ok( ret, "got %d\n", ret );
+    todo_wine
+    ok( awareness == DPI_AWARENESS_SYSTEM_AWARE, "wrong value %d\n", awareness );
+    ret = pGetProcessDpiAwarenessInternal( GetCurrentProcess(), &awareness );
+    ok( ret, "got %d\n", ret );
+    todo_wine
+    ok( awareness == DPI_AWARENESS_SYSTEM_AWARE, "wrong value %d\n", awareness );
+    ret = pGetProcessDpiAwarenessInternal( (HANDLE)0xdeadbeef, &awareness );
+    ok( ret, "got %d\n", ret );
+    ok( awareness == DPI_AWARENESS_UNAWARE, "wrong value %d\n", awareness );
+
+    ret = pIsProcessDPIAware();
+    ok(ret, "got %d\n", ret);
+    context = pGetThreadDpiAwarenessContext();
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    todo_wine
+    ok( awareness == DPI_AWARENESS_SYSTEM_AWARE, "wrong awareness %u\n", awareness );
+    SetLastError( 0xdeadbeef );
+    context = pSetThreadDpiAwarenessContext( 0 );
+    ok( !context, "got %p\n", context );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    context = pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)-6 );
+    ok( !context, "got %p\n", context );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER, "wrong error %u\n", GetLastError() );
+    context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x80000011 | flags), "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    todo_wine
+    ok( awareness == DPI_AWARENESS_SYSTEM_AWARE, "wrong awareness %u\n", awareness );
+    dpi = pGetDpiForSystem();
+    ok( dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", dpi );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    ok( dpi == USER_DEFAULT_SCREEN_DPI, "wrong dpi %u\n", dpi );
+    ok( !pIsProcessDPIAware(), "still aware\n" );
+    context = pGetThreadDpiAwarenessContext();
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x10 | flags), "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_UNAWARE, "wrong awareness %u\n", awareness );
+    context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x10 | flags), "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_UNAWARE, "wrong awareness %u\n", awareness );
+    dpi = pGetDpiForSystem();
+    ok( dpi == real_dpi, "wrong dpi %u/%u\n", dpi, real_dpi );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    ok( dpi == real_dpi, "wrong dpi %u\n", dpi );
+    context = pGetThreadDpiAwarenessContext();
+    ok( context == (DPI_AWARENESS_CONTEXT)0x12, "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_PER_MONITOR_AWARE, "wrong awareness %u\n", awareness );
+    context = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_SYSTEM_AWARE );
+    ok( context == (DPI_AWARENESS_CONTEXT)0x12, "wrong context %p\n", context );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_PER_MONITOR_AWARE, "wrong awareness %u\n", awareness );
+    dpi = pGetDpiForSystem();
+    ok( dpi == real_dpi, "wrong dpi %u/%u\n", dpi, real_dpi );
+    dpi = GetDeviceCaps( hdc, LOGPIXELSX );
+    ok( dpi == real_dpi, "wrong dpi %u\n", dpi );
+    ok( pIsProcessDPIAware(), "not aware\n" );
+    context = pGetThreadDpiAwarenessContext();
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    context = pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)(0x80000010 | flags) );
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    context = pGetThreadDpiAwarenessContext();
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    context = pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)(0x80000011 | flags) );
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x80000011 | flags), "wrong context %p\n", context );
+    context = pGetThreadDpiAwarenessContext();
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    context = pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)0x12 );
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x80000011 | flags), "wrong context %p\n", context );
+    context = pSetThreadDpiAwarenessContext( context );
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x12), "wrong context %p\n", context );
+    context = pGetThreadDpiAwarenessContext();
+    todo_wine
+    ok( context == (DPI_AWARENESS_CONTEXT)(0x11 | flags), "wrong context %p\n", context );
+    for (i = 0; i < 0x100; i++)
+    {
+        awareness = pGetAwarenessFromDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)i );
+        switch (i)
+        {
+        case 0x10:
+        case 0x11:
+        case 0x12:
+            ok( awareness == (i & ~0x10), "%lx: wrong value %u\n", i, awareness );
+            ok( pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)i ), "%lx: not valid\n", i );
+            break;
+        default:
+            ok( awareness == DPI_AWARENESS_INVALID, "%lx: wrong value %u\n", i, awareness );
+            ok( !pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)i ), "%lx: valid\n", i );
+            break;
+        }
+        awareness = pGetAwarenessFromDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)(i | 0x80000000) );
+        switch (i)
+        {
+        case 0x10:
+        case 0x11:
+        case 0x12:
+            ok( awareness == (i & ~0x10), "%lx: wrong value %u\n", i | 0x80000000, awareness );
+            ok( pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)(i | 0x80000000) ),
+                "%lx: not valid\n", i | 0x80000000 );
+            break;
+        default:
+            ok( awareness == DPI_AWARENESS_INVALID, "%lx: wrong value %u\n", i | 0x80000000, awareness );
+            ok( !pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)(i | 0x80000000) ),
+                "%lx: valid\n", i | 0x80000000 );
+            break;
+        }
+        awareness = pGetAwarenessFromDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i );
+        switch (~i)
+        {
+        case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE:
+        case (ULONG_PTR)DPI_AWARENESS_CONTEXT_SYSTEM_AWARE:
+        case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE:
+            ok( awareness == i, "%lx: wrong value %u\n", ~i, awareness );
+            ok( pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i ), "%lx: not valid\n", ~i );
+            break;
+        case (ULONG_PTR)DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2:
+            if (pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i ))
+                ok( awareness == DPI_AWARENESS_PER_MONITOR_AWARE, "%lx: wrong value %u\n", ~i, awareness );
+            else
+                ok( awareness == DPI_AWARENESS_INVALID, "%lx: wrong value %u\n", ~i, awareness );
+            break;
+        case (ULONG_PTR)DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED:
+            if (pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i ))
+                ok( awareness == DPI_AWARENESS_UNAWARE, "%lx: wrong value %u\n", ~i, awareness );
+            else
+                ok( awareness == DPI_AWARENESS_INVALID, "%lx: wrong value %u\n", ~i, awareness );
+            break;
+        default:
+            ok( awareness == DPI_AWARENESS_INVALID, "%lx: wrong value %u\n", ~i, awareness );
+            ok( !pIsValidDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i ), "%lx: valid\n", ~i );
+            break;
+        }
+    }
+    if (real_dpi != USER_DEFAULT_SCREEN_DPI) test_dpi_stock_objects( hdc );
+    ReleaseDC( 0, hdc );
+}
+
+static LRESULT CALLBACK dpi_winproc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
+{
+    DPI_AWARENESS_CONTEXT ctx = pGetWindowDpiAwarenessContext( hwnd );
+    DPI_AWARENESS_CONTEXT ctx2 = pGetThreadDpiAwarenessContext();
+    DWORD pos, pos2;
+
+    ok( pGetAwarenessFromDpiAwarenessContext( ctx ) == pGetAwarenessFromDpiAwarenessContext( ctx2 ),
+        "msg %04x wrong awareness %p / %p\n", msg, ctx, ctx2 );
+    pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    pos = GetMessagePos();
+    pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE );
+    pos2 = GetMessagePos();
+    ok( pos == pos2, "wrong pos %08x / %08x\n", pos, pos2 );
+    pSetThreadDpiAwarenessContext( ctx2 );
+    return DefWindowProcA( hwnd, msg, wp, lp );
+}
+
+static void test_dpi_window(void)
+{
+    DPI_AWARENESS_CONTEXT context, orig;
+    DPI_AWARENESS awareness;
+    ULONG_PTR i, j;
+    UINT dpi;
+    HWND hwnd, child, ret;
+    MSG msg = { 0, WM_USER + 1, 0, 0 };
+
+    if (!pGetWindowDpiAwarenessContext)
+    {
+        win_skip( "GetWindowDpiAwarenessContext not supported\n" );
+        return;
+    }
+    orig = pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    for (i = DPI_AWARENESS_UNAWARE; i <= DPI_AWARENESS_PER_MONITOR_AWARE; i++)
+    {
+        pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~i );
+        hwnd = CreateWindowA( "DpiTestClass", "Test",
+                              WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
+        ok( hwnd != 0, "failed to create window\n" );
+        context = pGetWindowDpiAwarenessContext( hwnd );
+        awareness = pGetAwarenessFromDpiAwarenessContext( context );
+        ok( awareness == i, "%lu: wrong awareness %u\n", i, awareness );
+        dpi = pGetDpiForWindow( hwnd );
+        ok( dpi == (i == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+            "%lu: got %u / %u\n", i, dpi, real_dpi );
+        if (pGetDpiForMonitorInternal)
+        {
+            BOOL res;
+            SetLastError( 0xdeadbeef );
+            res = pGetDpiForMonitorInternal( MonitorFromWindow( hwnd, 0 ), 0, &dpi, NULL );
+            ok( !res, "succeeded\n" );
+            ok( GetLastError() == ERROR_INVALID_ADDRESS, "wrong error %u\n", GetLastError() );
+            SetLastError( 0xdeadbeef );
+            res = pGetDpiForMonitorInternal( MonitorFromWindow( hwnd, 0 ), 3, &dpi, &dpi );
+            ok( !res, "succeeded\n" );
+            ok( GetLastError() == ERROR_BAD_ARGUMENTS, "wrong error %u\n", GetLastError() );
+            SetLastError( 0xdeadbeef );
+            res = pGetDpiForMonitorInternal( MonitorFromWindow( hwnd, 0 ), 3, &dpi, NULL );
+            ok( !res, "succeeded\n" );
+            ok( GetLastError() == ERROR_BAD_ARGUMENTS, "wrong error %u\n", GetLastError() );
+            res = pGetDpiForMonitorInternal( MonitorFromWindow( hwnd, 0 ), 0, &dpi, &dpi );
+            ok( res, "failed err %u\n", GetLastError() );
+            ok( dpi == (i == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+                "%lu: got %u / %u\n", i, dpi, real_dpi );
+        }
+        msg.hwnd = hwnd;
+        for (j = DPI_AWARENESS_UNAWARE; j <= DPI_AWARENESS_PER_MONITOR_AWARE; j++)
+        {
+            pSetThreadDpiAwarenessContext( (DPI_AWARENESS_CONTEXT)~j );
+            SendMessageA( hwnd, WM_USER, 0, 0 );
+            DispatchMessageA( &msg );
+            CallWindowProcA( dpi_winproc, hwnd, WM_USER + 2, 0, 0 );
+            child = CreateWindowA( "DpiTestClass", "Test",
+                                   WS_CHILD, 0, 0, 100, 100, hwnd, 0, GetModuleHandleA(0), NULL );
+            context = pGetWindowDpiAwarenessContext( child );
+            awareness = pGetAwarenessFromDpiAwarenessContext( context );
+            ok( awareness == i, "%lu/%lu: wrong awareness %u\n", i, j, awareness );
+            dpi = pGetDpiForWindow( child );
+            ok( dpi == (i == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+                "%lu/%lu: got %u / %u\n", i, j, dpi, real_dpi );
+            ret = SetParent( child, NULL );
+            ok( ret != 0, "SetParent failed err %u\n", GetLastError() );
+            context = pGetWindowDpiAwarenessContext( child );
+            awareness = pGetAwarenessFromDpiAwarenessContext( context );
+            ok( awareness == i, "%lu/%lu: wrong awareness %u\n", i, j, awareness );
+            dpi = pGetDpiForWindow( child );
+            ok( dpi == (i == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+                "%lu/%lu: got %u / %u\n", i, j, dpi, real_dpi );
+            DestroyWindow( child );
+            child = CreateWindowA( "DpiTestClass", "Test",
+                                   WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL );
+            context = pGetWindowDpiAwarenessContext( child );
+            awareness = pGetAwarenessFromDpiAwarenessContext( context );
+            ok( awareness == j, "%lu/%lu: wrong awareness %u\n", i, j, awareness );
+            dpi = pGetDpiForWindow( child );
+            ok( dpi == (j == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+                "%lu/%lu: got %u / %u\n", i, j, dpi, real_dpi );
+            ret = SetParent( child, hwnd );
+            ok( ret != 0 || GetLastError() == ERROR_INVALID_STATE,
+                "SetParent failed err %u\n", GetLastError() );
+            context = pGetWindowDpiAwarenessContext( child );
+            awareness = pGetAwarenessFromDpiAwarenessContext( context );
+            ok( awareness == (ret ? i : j), "%lu/%lu: wrong awareness %u\n", i, j, awareness );
+            dpi = pGetDpiForWindow( child );
+            ok( dpi == (i == DPI_AWARENESS_UNAWARE ? USER_DEFAULT_SCREEN_DPI : real_dpi),
+                "%lu/%lu: got %u / %u\n", i, j, dpi, real_dpi );
+            DestroyWindow( child );
+        }
+        DestroyWindow( hwnd );
+    }
+
+    SetLastError( 0xdeadbeef );
+    context = pGetWindowDpiAwarenessContext( (HWND)0xdeadbeef );
+    ok( !context, "got %p\n", context );
+    ok( GetLastError() == ERROR_INVALID_WINDOW_HANDLE, "wrong error %u\n", GetLastError() );
+    SetLastError( 0xdeadbeef );
+    dpi = pGetDpiForWindow( (HWND)0xdeadbeef );
+    ok( !dpi, "got %u\n", dpi );
+    ok( GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_INVALID_WINDOW_HANDLE,
+        "wrong error %u\n", GetLastError() );
+
+    SetLastError( 0xdeadbeef );
+    context = pGetWindowDpiAwarenessContext( GetDesktopWindow() );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_PER_MONITOR_AWARE, "wrong awareness %u\n", awareness );
+    dpi = pGetDpiForWindow( GetDesktopWindow() );
+    ok( dpi == real_dpi, "got %u / %u\n", dpi, real_dpi );
+
+    pSetThreadDpiAwarenessContext( DPI_AWARENESS_CONTEXT_UNAWARE );
+    SetLastError( 0xdeadbeef );
+    context = pGetWindowDpiAwarenessContext( GetDesktopWindow() );
+    awareness = pGetAwarenessFromDpiAwarenessContext( context );
+    ok( awareness == DPI_AWARENESS_PER_MONITOR_AWARE, "wrong awareness %u\n", awareness );
+    dpi = pGetDpiForWindow( GetDesktopWindow() );
+    ok( dpi == real_dpi, "got %u / %u\n", dpi, real_dpi );
+
+    pSetThreadDpiAwarenessContext( orig );
 }
 
 static void test_GetAutoRotationState(void)
@@ -3027,7 +3893,6 @@ static void test_GetAutoRotationState(void)
     state = 0;
     ret = pGetAutoRotationState(&state);
     ok(ret, "Expected GetAutoRotationState to succeed, error %d\n", GetLastError());
-    ok((state & AR_NOSENSOR) != 0, "Expected AR_NOSENSOR, got %d\n", state);
 }
 
 START_TEST(sysparams)
@@ -3036,6 +3901,7 @@ START_TEST(sysparams)
     char** argv;
     WNDCLASSA wc;
     MSG msg;
+    HDC hdc;
     HANDLE hThread;
     DWORD dwThreadId;
     HANDLE hInstance, hdll;
@@ -3044,6 +3910,22 @@ START_TEST(sysparams)
     pChangeDisplaySettingsExA = (void*)GetProcAddress(hdll, "ChangeDisplaySettingsExA");
     pIsProcessDPIAware = (void*)GetProcAddress(hdll, "IsProcessDPIAware");
     pSetProcessDPIAware = (void*)GetProcAddress(hdll, "SetProcessDPIAware");
+    pGetDpiForSystem = (void*)GetProcAddress(hdll, "GetDpiForSystem");
+    pGetDpiForWindow = (void*)GetProcAddress(hdll, "GetDpiForWindow");
+    pGetDpiForMonitorInternal = (void*)GetProcAddress(hdll, "GetDpiForMonitorInternal");
+    pSetProcessDpiAwarenessContext = (void*)GetProcAddress(hdll, "SetProcessDpiAwarenessContext");
+    pGetProcessDpiAwarenessInternal = (void*)GetProcAddress(hdll, "GetProcessDpiAwarenessInternal");
+    pSetProcessDpiAwarenessInternal = (void*)GetProcAddress(hdll, "SetProcessDpiAwarenessInternal");
+    pGetThreadDpiAwarenessContext = (void*)GetProcAddress(hdll, "GetThreadDpiAwarenessContext");
+    pSetThreadDpiAwarenessContext = (void*)GetProcAddress(hdll, "SetThreadDpiAwarenessContext");
+    pGetWindowDpiAwarenessContext = (void*)GetProcAddress(hdll, "GetWindowDpiAwarenessContext");
+    pGetAwarenessFromDpiAwarenessContext = (void*)GetProcAddress(hdll, "GetAwarenessFromDpiAwarenessContext");
+    pIsValidDpiAwarenessContext = (void*)GetProcAddress(hdll, "IsValidDpiAwarenessContext");
+    pGetSystemMetricsForDpi = (void*)GetProcAddress(hdll, "GetSystemMetricsForDpi");
+    pSystemParametersInfoForDpi = (void*)GetProcAddress(hdll, "SystemParametersInfoForDpi");
+    pAdjustWindowRectExForDpi = (void*)GetProcAddress(hdll, "AdjustWindowRectExForDpi");
+    pLogicalToPhysicalPointForPerMonitorDPI = (void*)GetProcAddress(hdll, "LogicalToPhysicalPointForPerMonitorDPI");
+    pPhysicalToLogicalPointForPerMonitorDPI = (void*)GetProcAddress(hdll, "PhysicalToLogicalPointForPerMonitorDPI");
     pGetAutoRotationState = (void*)GetProcAddress(hdll, "GetAutoRotationState");
 
     hInstance = GetModuleHandleA( NULL );
@@ -3051,7 +3933,7 @@ START_TEST(sysparams)
     dpi = GetDeviceCaps( hdc, LOGPIXELSY);
     real_dpi = get_real_dpi();
     trace("dpi %d real_dpi %d\n", dpi, real_dpi);
-    iswin9x = GetVersion() & 0x80000000;
+    ReleaseDC( 0, hdc);
 
     /* This test requires interactivity, if we don't have it, give up */
     if (!SystemParametersInfoA( SPI_SETBEEP, TRUE, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ) &&
@@ -3063,7 +3945,7 @@ START_TEST(sysparams)
 
     trace("testing GetSystemMetrics with your current desktop settings\n");
     test_GetSystemMetrics( );
-    trace("testing EnumDisplaySettings vs GetDeviceCaps\n");
+    test_metrics_for_dpi( 192 );
     test_EnumDisplaySettings( );
     test_GetSysColorBrush( );
     test_GetAutoRotationState( );
@@ -3082,6 +3964,9 @@ START_TEST(sysparams)
     wc.cbClsExtra = 0;
     wc.cbWndExtra = 0;
     RegisterClassA( &wc );
+    wc.lpszClassName = "DpiTestClass";
+    wc.lpfnWndProc = dpi_winproc;
+    RegisterClassA( &wc );
 
     ghTestWnd = CreateWindowA( "SysParamsTestClass", "Test System Parameters Application",
                                WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, hInstance, NULL );
@@ -3094,7 +3979,14 @@ START_TEST(sysparams)
         TranslateMessage( &msg );
         DispatchMessageA( &msg );
     }
-    ReleaseDC( 0, hdc);
+
+    if (pSetThreadDpiAwarenessContext)
+    {
+        test_dpi_context();
+        test_dpi_mapping();
+        test_dpi_window();
+    }
+    else win_skip( "SetThreadDpiAwarenessContext not supported\n" );
 
     test_dpi_aware();
 }
index 3923a38..d3d99a7 100644 (file)
@@ -32,6 +32,7 @@ extern void func_msg_controls(void);
 extern void func_msg_layered_window(void);
 extern void func_msg_dialog(void);
 extern void func_msg_clipboard(void);
+extern void func_rawinput(void);
 extern void func_resource(void);
 extern void func_scroll(void);
 extern void func_static(void);
@@ -72,6 +73,7 @@ const struct test winetest_testlist[] =
     { "msg_layered_window", func_msg_layered_window},
     { "msg_dialog", func_msg_dialog},
     { "msg_clipboard", func_msg_clipboard},
+    { "rawinput", func_rawinput },
     { "resource", func_resource },
     { "scroll", func_scroll },
     { "static", func_static },
index 3cc9571..359721f 100644 (file)
@@ -277,6 +277,21 @@ static void test_DrawTextCalcRect(void)
     ok(textheight==0,"Got textheight from DrawTextA\n");
     ok(textheight == heightcheck,"DrawTextEx and DrawText differ in return\n");
 
+    /* When offset to top is zero, return 1 */
+    SetRectEmpty(&rect);
+    textheight = DrawTextExW(hdc, textW, -1, &rect, DT_SINGLELINE | DT_CALCRECT | DT_BOTTOM, NULL);
+    ok(textheight == 1, "Expect returned height:1 got:%d\n", textheight);
+
+    SetRect(&rect, 0, 100, 0, 100);
+    textheight = DrawTextExW(hdc, textW, -1, &rect, DT_SINGLELINE | DT_CALCRECT | DT_BOTTOM, NULL);
+    ok(textheight == 1, "Expect returned height:1 got:%d\n", textheight);
+
+    SetRectEmpty(&rect);
+    textheight = DrawTextExW(hdc, textW, -1, &rect, DT_SINGLELINE | DT_CALCRECT | DT_TOP, NULL);
+    /* Set top to text height and bottom zero, so bottom of drawn text to top is zero when DT_VCENTER is used */
+    SetRect(&rect, 0, textheight, 0, 0);
+    textheight = DrawTextExW(hdc, textW, -1, &rect, DT_SINGLELINE | DT_CALCRECT | DT_VCENTER, NULL);
+    ok(textheight == 1, "Expect returned height:1 got:%d\n", textheight);
 
     /* invalid dtp size test */
     dtp.cbSize = -1; /* Invalid */
@@ -750,7 +765,7 @@ static void test_CharToOem_OemToChar(void)
     char oem;
     WCHAR uni, expect;
 
-    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
     {
         const char *expected = tests[i].ret ? helloWorld : "";
         const char *src = tests[i].src ? helloWorld : NULL;
@@ -777,7 +792,7 @@ static void test_CharToOem_OemToChar(void)
         ok(!strcmp(buf, expected), "test %d: got '%s'\n", i, buf);
     }
 
-    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
     {
         const char *expected = tests[i].ret ? helloWorld : "";
         const WCHAR *src = tests[i].src ? helloWorldW : NULL;
@@ -789,12 +804,12 @@ static void test_CharToOem_OemToChar(void)
         ok(!strcmp(buf, expected), "test %d: got '%s'\n", i, buf);
&nbs