[USER32_WINETEST]
[reactos.git] / rostests / winetests / user32 / class.c
index b11b5d2..b7332ac 100755 (executable)
@@ -19,9 +19,8 @@
  */
 
 /* To get CS_DROPSHADOW with the MSVC headers */
-#define _WIN32_WINNT 0x0501
+//#define _WIN32_WINNT 0x0501
 
-#include <assert.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -315,11 +314,13 @@ static void check_thread_instance( const char *name, HINSTANCE inst, HINSTANCE i
 static void test_instances(void)
 {
     WNDCLASSA cls, wc;
+    WNDCLASSEXA wcexA;
     HWND hwnd, hwnd2;
     const char *name = "__test__";
     HINSTANCE kernel32 = GetModuleHandleA("kernel32");
     HINSTANCE user32 = GetModuleHandleA("user32");
     HINSTANCE main_module = GetModuleHandleA(NULL);
+    HINSTANCE zero_instance = 0;
     DWORD r;
     char buffer[0x10];
 
@@ -347,6 +348,25 @@ static void test_instances(void)
     check_thread_instance( name, kernel32, kernel32, kernel32 );
     ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
 
+    ZeroMemory(&wcexA, sizeof(wcexA));
+    wcexA.lpfnWndProc = DefWindowProcA;
+    wcexA.lpszClassName = "__classex_test__";
+    SetLastError(0xdeadbeef);
+    wcexA.cbSize = sizeof(wcexA) - 1;
+    ok( ((RegisterClassExA( &wcexA ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+          "Succeeded with invalid number of cbSize bytes\n");
+    SetLastError(0xdeadbeef);
+    wcexA.cbSize = sizeof(wcexA) + 1;
+    ok( ((RegisterClassExA( &wcexA ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+          "Succeeded with invalid number of cbSize bytes\n");
+    SetLastError(0xdeadbeef);
+    wcexA.cbSize = sizeof(wcexA);
+    ok( RegisterClassExA( &wcexA ), "Failed with valid number of cbSize bytes\n");
+    wcexA.cbSize = 0xdeadbeef;
+    ok( GetClassInfoEx(main_module, wcexA.lpszClassName, &wcexA), "GetClassInfoEx failed\n");
+    ok( wcexA.cbSize == 0xdeadbeef, "GetClassInfoEx returned wrong cbSize value %d\n", wcexA.cbSize);
+    UnregisterClassA(wcexA.lpszClassName, main_module);
+
     /* Bug 2631 - Supplying an invalid number of bytes fails */
     cls.cbClsExtra    = 0;
     cls.cbWndExtra    = -1;
@@ -397,15 +417,19 @@ static void test_instances(void)
     /* GetClassInfo with instance 0 finds user32 instance */
     SetClassLongPtrA( hwnd, GCLP_HMODULE, (LONG_PTR)user32 );
     ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+    if (!GetClassInfo( 0, name, &wc )) zero_instance = user32; /* instance 0 not supported on wow64 */
+    else
+    {
+        check_instance( name, 0, 0, kernel32 );
+        check_thread_instance( name, 0, 0, kernel32 );
+    }
     check_class( kernel32, name, "kernel32" );
     check_class( user32, name, "main_module" );
-    check_class( 0, name, "main_module" );
+    check_class( zero_instance, name, "main_module" );
     check_instance( name, kernel32, kernel32, kernel32 );
-    check_instance( name, user32, 0, user32 );
-    check_instance( name, 0, 0, kernel32 );
+    check_instance( name, user32, zero_instance, user32 );
     check_thread_instance( name, kernel32, kernel32, kernel32 );
-    check_thread_instance( name, user32, 0, user32 );
-    check_thread_instance( name, 0, 0, kernel32 );
+    check_thread_instance( name, user32, zero_instance, user32 );
     ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
 
     SetClassLongPtrA( hwnd, GCLP_HMODULE, 0x12345678 );
@@ -551,10 +575,10 @@ static void test_instances(void)
     /* GetClassInfo sets instance to passed value for global classes */
     check_instance( "BUTTON", 0, 0, user32 );
     check_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
-    check_instance( "BUTTON", user32, 0, user32 );
+    check_instance( "BUTTON", user32, zero_instance, user32 );
     check_thread_instance( "BUTTON", 0, 0, user32 );
     check_thread_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
-    check_thread_instance( "BUTTON", user32, 0, user32 );
+    check_thread_instance( "BUTTON", user32, zero_instance, user32 );
 
     /* we can unregister system classes */
     ok( GetClassInfo( 0, "BUTTON", &wc ), "Button class not found with null instance\n" );
@@ -592,6 +616,7 @@ static void test_builtinproc(void)
     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 */
     WNDPROC pDefWindowProcA, pDefWindowProcW;
+    WNDPROC pNtdllDefWindowProcA, pNtdllDefWindowProcW;
     WNDPROC oldproc;
     WNDCLASSEXA cls;  /* the memory layout of WNDCLASSEXA and WNDCLASSEXW is the same */
     WCHAR buf[128];
@@ -601,45 +626,56 @@ static void test_builtinproc(void)
 
     pDefWindowProcA = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcA");
     pDefWindowProcW = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcW");
-
-    for (i = 0; i < 4; i++)
+    pNtdllDefWindowProcA = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtdllDefWindowProc_A");
+    pNtdllDefWindowProcW = (void *)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtdllDefWindowProc_W");
+
+    /* On Vista+, the user32.dll export DefWindowProcA/W is forwarded to  */
+    /* ntdll.NtdllDefWindowProc_A/W. However, the wndproc returned by     */
+    /* GetClassLong/GetWindowLong points to an unexported user32 function */
+    if (pDefWindowProcA == pNtdllDefWindowProcA &&
+        pDefWindowProcW == pNtdllDefWindowProcW)
+        skip("user32.DefWindowProcX forwarded to ntdll.NtdllDefWindowProc_X\n");
+    else
     {
-        ZeroMemory(&cls, sizeof(cls));
-        cls.cbSize = sizeof(cls);
-        cls.hInstance = GetModuleHandle(NULL);
-        cls.hbrBackground = GetStockObject (WHITE_BRUSH);
-        if (i & 1)
-            cls.lpfnWndProc = pDefWindowProcA;
-        else
-            cls.lpfnWndProc = pDefWindowProcW;
-
-        if (i & 2)
+        for (i = 0; i < 4; i++)
         {
-            cls.lpszClassName = classA;
-            atom = RegisterClassExA(&cls);
+            ZeroMemory(&cls, sizeof(cls));
+            cls.cbSize = sizeof(cls);
+            cls.hInstance = GetModuleHandle(NULL);
+            cls.hbrBackground = GetStockObject (WHITE_BRUSH);
+            if (i & 1)
+                cls.lpfnWndProc = pDefWindowProcA;
+            else
+                cls.lpfnWndProc = pDefWindowProcW;
+
+            if (i & 2)
+            {
+                cls.lpszClassName = classA;
+                atom = RegisterClassExA(&cls);
+            }
+            else
+            {
+                cls.lpszClassName = (LPCSTR)classW;
+                atom = RegisterClassExW((WNDCLASSEXW *)&cls);
+            }
+            ok(atom != 0, "Couldn't register class, i=%d, %d\n", i, GetLastError());
+
+            hwnd = CreateWindowA(classA, NULL, 0, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), NULL);
+            ok(hwnd != NULL, "Couldn't create window i=%d\n", i);
+
+            ok(GetWindowLongPtrA(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcA, "Wrong ANSI wndproc: %p vs %p\n",
+                (void *)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), pDefWindowProcA);
+            ok(GetClassLongPtrA(hwnd, GCLP_WNDPROC) == (ULONG_PTR)pDefWindowProcA, "Wrong ANSI wndproc: %p vs %p\n",
+                (void *)GetClassLongPtrA(hwnd, GCLP_WNDPROC), pDefWindowProcA);
+
+            ok(GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcW, "Wrong Unicode wndproc: %p vs %p\n",
+                (void *)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), pDefWindowProcW);
+            ok(GetClassLongPtrW(hwnd, GCLP_WNDPROC) == (ULONG_PTR)pDefWindowProcW, "Wrong Unicode wndproc: %p vs %p\n",
+                (void *)GetClassLongPtrW(hwnd, GCLP_WNDPROC), pDefWindowProcW);
+
+            DestroyWindow(hwnd);
+            UnregisterClass((LPSTR)(DWORD_PTR)atom, GetModuleHandle(NULL));
         }
-        else
-        {
-            cls.lpszClassName = (LPCSTR)classW;
-            atom = RegisterClassExW((WNDCLASSEXW *)&cls);
-        }
-        ok(atom != 0, "Couldn't register class, i=%d, %d\n", i, GetLastError());
-
-        hwnd = CreateWindowA(classA, NULL, 0, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), NULL);
-        ok(hwnd != NULL, "Couldn't create window i=%d\n", i);
-
-        ok(GetWindowLongPtrA(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcA, "Wrong ANSI wndproc: %p vs %p\n",
-            (void *)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), pDefWindowProcA);
-        ok(GetClassLongPtrA(hwnd, GCLP_WNDPROC) == (ULONG_PTR)pDefWindowProcA, "Wrong ANSI wndproc: %p vs %p\n",
-            (void *)GetClassLongPtrA(hwnd, GCLP_WNDPROC), pDefWindowProcA);
-
-        ok(GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcW, "Wrong Unicode wndproc: %p vs %p\n",
-            (void *)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), pDefWindowProcW);
-        ok(GetClassLongPtrW(hwnd, GCLP_WNDPROC) == (ULONG_PTR)pDefWindowProcW, "Wrong Unicode wndproc: %p vs %p\n",
-            (void *)GetClassLongPtrW(hwnd, GCLP_WNDPROC), pDefWindowProcW);
-
-        DestroyWindow(hwnd);
-        UnregisterClass((LPSTR)(DWORD_PTR)atom, GetModuleHandle(NULL));
     }
 
     /* built-in winproc - window A/W type automatically detected */
@@ -656,8 +692,16 @@ static void test_builtinproc(void)
     ok(IsWindowUnicode(hwnd), "Windows should be Unicode\n");
     SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)pDefWindowProcA);
     ok(IsWindowUnicode(hwnd), "Windows should have remained Unicode\n");
-    ok(GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcW, "Invalid ANSI winproc\n");
-    ok(GetWindowLongPtrA(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcA, "Invalid Unicode winproc\n");
+    if (GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcA)
+    {
+        /* DefWindowProc isn't magic on wow64 */
+        ok(IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Ansi winproc is not a handle\n");
+    }
+    else
+    {
+        ok(GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcW, "Invalid Unicode winproc\n");
+        ok(GetWindowLongPtrA(hwnd, GWLP_WNDPROC) == (LONG_PTR)pDefWindowProcA, "Invalid Ansi winproc\n");
+    }
     SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc);
     ok(IsWindowUnicode(hwnd) == FALSE, "SetWindowLongPtrA should have switched window to ANSI\n");
 
@@ -867,10 +911,129 @@ static void test_extra_values(void)
     }
 }
 
+static void test_GetClassInfo(void)
+{
+    static const WCHAR staticW[] = {'s','t','a','t','i','c',0};
+    WNDCLASSA wc;
+    WNDCLASSEXA wcx;
+    BOOL ret;
+
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoA(0, "static", &wc);
+    ok(ret, "GetClassInfoA() error %d\n", GetLastError());
+
+if (0) { /* crashes under XP */
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoA(0, "static", NULL);
+    ok(ret, "GetClassInfoA() error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoW(0, staticW, NULL);
+    ok(ret, "GetClassInfoW() error %d\n", GetLastError());
+}
+
+    wcx.cbSize = sizeof(wcx);
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExA(0, "static", &wcx);
+    ok(ret, "GetClassInfoExA() error %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExA(0, "static", NULL);
+    ok(!ret, "GetClassInfoExA() should fail\n");
+    ok(GetLastError() == ERROR_NOACCESS ||
+       broken(GetLastError() == 0xdeadbeef), /* win9x */
+       "expected ERROR_NOACCESS, got %d\n", GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExW(0, staticW, NULL);
+    ok(!ret, "GetClassInfoExW() should fail\n");
+    ok(GetLastError() == ERROR_NOACCESS ||
+       broken(GetLastError() == 0xdeadbeef) /* NT4 */ ||
+       broken(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), /* win9x */
+       "expected ERROR_NOACCESS, got %d\n", GetLastError());
+
+    wcx.cbSize = 0;
+    wcx.lpfnWndProc = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExA(0, "static", &wcx);
+    ok(ret, "GetClassInfoExA() error %d\n", GetLastError());
+    ok(wcx.cbSize == 0, "expected 0, got %u\n", wcx.cbSize);
+    ok(wcx.lpfnWndProc != NULL, "got null proc\n");
+
+    wcx.cbSize = sizeof(wcx) - 1;
+    wcx.lpfnWndProc = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExA(0, "static", &wcx);
+    ok(ret, "GetClassInfoExA() error %d\n", GetLastError());
+    ok(wcx.cbSize == sizeof(wcx) - 1, "expected sizeof(wcx)-1, got %u\n", wcx.cbSize);
+    ok(wcx.lpfnWndProc != NULL, "got null proc\n");
+
+    wcx.cbSize = sizeof(wcx) + 1;
+    wcx.lpfnWndProc = NULL;
+    SetLastError(0xdeadbeef);
+    ret = GetClassInfoExA(0, "static", &wcx);
+    ok(ret, "GetClassInfoExA() error %d\n", GetLastError());
+    ok(wcx.cbSize == sizeof(wcx) + 1, "expected sizeof(wcx)+1, got %u\n", wcx.cbSize);
+    ok(wcx.lpfnWndProc != NULL, "got null proc\n");
+}
+
+static void test_icons(void)
+{
+    WNDCLASSEXW wcex, ret_wcex;
+    WCHAR cls_name[] = {'I','c','o','n','T','e','s','t','C','l','a','s','s',0};
+    HWND hwnd;
+    HINSTANCE hinst = GetModuleHandleW(0);
+    HICON hsmicon, hsmallnew;
+    ICONINFO icinf;
+
+    memset(&wcex, 0, sizeof wcex);
+    wcex.cbSize        = sizeof wcex;
+    wcex.lpfnWndProc   = ClassTest_WndProc;
+    wcex.hIcon         = LoadIconW(0, (LPCWSTR)IDI_APPLICATION);
+    wcex.hInstance     = hinst;
+    wcex.lpszClassName = cls_name;
+    ok(RegisterClassExW(&wcex), "RegisterClassExW returned 0\n");
+    hwnd = CreateWindowExW(0, cls_name, NULL, WS_OVERLAPPEDWINDOW,
+                        0, 0, 0, 0, NULL, NULL, hinst, 0);
+    ok(hwnd != NULL, "Window was not created\n");
+
+    ok(GetClassInfoExW(hinst, cls_name, &ret_wcex), "Class info was not retrieved\n");
+    ok(wcex.hIcon == ret_wcex.hIcon, "Icons don't match\n");
+    ok(ret_wcex.hIconSm != NULL, "hIconSm should be non-zero handle\n");
+
+    hsmicon = (HICON)GetClassLongPtrW(hwnd, GCLP_HICONSM);
+    ok(hsmicon != NULL, "GetClassLong should return non-zero handle\n");
+
+    hsmallnew = CopyImage(wcex.hIcon, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON),
+                                                GetSystemMetrics(SM_CYSMICON), 0);
+    ok(!SetClassLongPtrW(hwnd, GCLP_HICONSM, (LONG_PTR)hsmallnew),
+                    "Previous hIconSm should be zero\n");
+    ok(hsmallnew == (HICON)GetClassLongPtrW(hwnd, GCLP_HICONSM),
+                    "Should return explicitly assigned small icon\n");
+    ok(!GetIconInfo(hsmicon, &icinf), "Previous small icon should be destroyed\n");
+
+    SetClassLongPtrW(hwnd, GCLP_HICONSM, 0);
+    hsmicon = (HICON)GetClassLongPtrW(hwnd, GCLP_HICONSM);
+    ok( hsmicon != NULL, "GetClassLong should return non-zero handle\n");
+
+    SetClassLongPtrW(hwnd, GCLP_HICON, 0);
+    ok(!GetClassLongPtrW(hwnd, GCLP_HICONSM), "GetClassLong should return zero handle\n");
+
+    SetClassLongPtrW(hwnd, GCLP_HICON, (LONG_PTR)LoadIconW(NULL, (LPCWSTR)IDI_QUESTION));
+    hsmicon = (HICON)GetClassLongPtrW(hwnd, GCLP_HICONSM);
+    ok(hsmicon != NULL, "GetClassLong should return non-zero handle\n");
+    UnregisterClassW(cls_name, hinst);
+    ok(GetIconInfo(hsmicon, &icinf), "Icon should NOT be destroyed\n");
+
+    DestroyIcon(hsmallnew);
+    DestroyWindow(hwnd);
+}
+
 START_TEST(class)
 {
     HANDLE hInstance = GetModuleHandleA( NULL );
 
+    test_GetClassInfo();
     test_extra_values();
 
     if (!GetModuleHandleW(0))
@@ -884,6 +1047,7 @@ START_TEST(class)
     CreateDialogParamTest(hInstance);
     test_styles();
     test_builtinproc();
+    test_icons();
 
     /* this test unregisters the Button class so it should be executed at the end */
     test_instances();