| /* Unit test suite for window classes. | |
| * | |
| * Copyright 2002 Mike McCormack | |
| * Copyright 2003 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| /* To get CS_DROPSHADOW with the MSVC headers */ | |
| #define _WIN32_WINNT 0x0501 | |
| #include <assert.h> | |
| #include <stdlib.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include "wine/test.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| /* we don't want to include commctrl.h: */ | |
| static const CHAR WC_EDITA[] = "Edit"; | |
| static const WCHAR WC_EDITW[] = {'E','d','i','t',0}; | |
| #define NUMCLASSWORDS 4 | |
| #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16)) | |
| static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| if (msg == WM_NCCREATE) return 1; | |
| return DefWindowProcW (hWnd, msg, wParam, lParam); | |
| } | |
| static LRESULT WINAPI ClassTest_WndProc2 (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| if (msg == WM_NCCREATE) return 1; | |
| return DefWindowProcA (hWnd, msg, wParam, lParam); | |
| } | |
| /*********************************************************************** | |
| */ | |
| static void ClassTest(HINSTANCE hInstance, BOOL global) | |
| { | |
| WNDCLASSW cls, wc; | |
| static const WCHAR className[] = {'T','e','s','t','C','l','a','s','s',0}; | |
| static const WCHAR winName[] = {'W','i','n','C','l','a','s','s','T','e','s','t',0}; | |
| ATOM test_atom; | |
| HWND hTestWnd; | |
| LONG i; | |
| WCHAR str[20]; | |
| ATOM classatom; | |
| cls.style = CS_HREDRAW | CS_VREDRAW | (global?CS_GLOBALCLASS:0); | |
| cls.lpfnWndProc = ClassTest_WndProc; | |
| cls.cbClsExtra = NUMCLASSWORDS*sizeof(DWORD); | |
| cls.cbWndExtra = 12; | |
| cls.hInstance = hInstance; | |
| cls.hIcon = LoadIconW (0, (LPWSTR)IDI_APPLICATION); | |
| cls.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW); | |
| cls.hbrBackground = GetStockObject (WHITE_BRUSH); | |
| cls.lpszMenuName = 0; | |
| cls.lpszClassName = className; | |
| classatom=RegisterClassW(&cls); | |
| if (!classatom && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) | |
| return; | |
| ok(classatom, "failed to register class\n"); | |
| ok(!RegisterClassW (&cls), | |
| "RegisterClass of the same class should fail for the second time\n"); | |
| /* Setup windows */ | |
| hTestWnd = CreateWindowW (className, winName, | |
| WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL, | |
| CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, | |
| 0, hInstance, 0); | |
| ok(hTestWnd!=0, "Failed to create window\n"); | |
| /* test initial values of valid classwords */ | |
| for(i=0; i<NUMCLASSWORDS; i++) | |
| { | |
| SetLastError(0); | |
| ok(!GetClassLongW(hTestWnd,i*sizeof (DWORD)), | |
| "GetClassLongW initial value nonzero!\n"); | |
| ok(!GetLastError(), | |
| "GetClassLongW failed!\n"); | |
| } | |
| if (0) | |
| { | |
| /* | |
| * GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD)) | |
| * does not fail on Win 98, though MSDN says it should | |
| */ | |
| SetLastError(0); | |
| GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD)); | |
| ok(GetLastError(), | |
| "GetClassLongW() with invalid offset did not fail\n"); | |
| } | |
| /* set values of valid class words */ | |
| for(i=0; i<NUMCLASSWORDS; i++) | |
| { | |
| SetLastError(0); | |
| ok(!SetClassLongW(hTestWnd,i*sizeof(DWORD),i+1), | |
| "GetClassLongW(%d) initial value nonzero!\n",i); | |
| ok(!GetLastError(), | |
| "SetClassLongW(%d) failed!\n",i); | |
| } | |
| /* test values of valid classwords that we set */ | |
| for(i=0; i<NUMCLASSWORDS; i++) | |
| { | |
| SetLastError(0); | |
| ok( (i+1) == GetClassLongW(hTestWnd,i*sizeof (DWORD)), | |
| "GetClassLongW value doesn't match what was set!\n"); | |
| ok(!GetLastError(), | |
| "GetClassLongW failed!\n"); | |
| } | |
| /* check GetClassName */ | |
| i = GetClassNameW(hTestWnd, str, sizeof(str)/sizeof(str[0])); | |
| ok(i == lstrlenW(className), | |
| "GetClassName returned incorrect length\n"); | |
| ok(!lstrcmpW(className,str), | |
| "GetClassName returned incorrect name for this window's class\n"); | |
| /* check GetClassInfo with our hInstance */ | |
| if((test_atom = GetClassInfoW(hInstance, str, &wc))) | |
| { | |
| ok(test_atom == classatom, | |
| "class atom did not match\n"); | |
| ok(wc.cbClsExtra == cls.cbClsExtra, | |
| "cbClsExtra did not match\n"); | |
| ok(wc.cbWndExtra == cls.cbWndExtra, | |
| "cbWndExtra did not match\n"); | |
| ok(wc.hbrBackground == cls.hbrBackground, | |
| "hbrBackground did not match\n"); | |
| ok(wc.hCursor== cls.hCursor, | |
| "hCursor did not match\n"); | |
| ok(wc.hInstance== cls.hInstance, | |
| "hInstance did not match\n"); | |
| } | |
| else | |
| ok(FALSE,"GetClassInfo (hinstance) failed!\n"); | |
| /* check GetClassInfo with zero hInstance */ | |
| if(global) | |
| { | |
| if((test_atom = GetClassInfoW(0, str, &wc))) | |
| { | |
| ok(test_atom == classatom, | |
| "class atom did not match %x != %x\n", test_atom, classatom); | |
| ok(wc.cbClsExtra == cls.cbClsExtra, | |
| "cbClsExtra did not match %x!=%x\n",wc.cbClsExtra,cls.cbClsExtra); | |
| ok(wc.cbWndExtra == cls.cbWndExtra, | |
| "cbWndExtra did not match %x!=%x\n",wc.cbWndExtra,cls.cbWndExtra); | |
| ok(wc.hbrBackground == cls.hbrBackground, | |
| "hbrBackground did not match %p!=%p\n",wc.hbrBackground,cls.hbrBackground); | |
| ok(wc.hCursor== cls.hCursor, | |
| "hCursor did not match %p!=%p\n",wc.hCursor,cls.hCursor); | |
| ok(!wc.hInstance, | |
| "hInstance not zero for global class %p\n",wc.hInstance); | |
| } | |
| else | |
| ok(FALSE,"GetClassInfo (0) failed for global class!\n"); | |
| } | |
| else | |
| { | |
| ok(!GetClassInfoW(0, str, &wc), | |
| "GetClassInfo (0) succeeded for local class!\n"); | |
| } | |
| ok(!UnregisterClassW(className, hInstance), | |
| "Unregister class succeeded with window existing\n"); | |
| ok(DestroyWindow(hTestWnd), | |
| "DestroyWindow() failed!\n"); | |
| ok(UnregisterClassW(className, hInstance), | |
| "UnregisterClass() failed\n"); | |
| return; | |
| } | |
| static void check_style( const char *name, int must_exist, UINT style, UINT ignore ) | |
| { | |
| WNDCLASS wc; | |
| if (GetClassInfo( 0, name, &wc )) | |
| { | |
| ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n", | |
| name, ~wc.style & style, wc.style, style ); | |
| ok( !(wc.style & ~style), "System class %s has extra bits %x (%08x/%08x)\n", | |
| name, wc.style & ~style, wc.style, style ); | |
| } | |
| else | |
| ok( !must_exist, "System class %s does not exist\n", name ); | |
| } | |
| /* test styles of system classes */ | |
| static void test_styles(void) | |
| { | |
| /* check style bits */ | |
| check_style( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 ); | |
| check_style( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 ); | |
| check_style( "Edit", 1, CS_PARENTDC | CS_DBLCLKS, 0 ); | |
| check_style( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS, CS_PARENTDC /*FIXME*/ ); | |
| check_style( "MDIClient", 1, 0, 0 ); | |
| check_style( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 ); | |
| check_style( "Static", 1, CS_PARENTDC | CS_DBLCLKS, 0 ); | |
| check_style( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS, 0 ); | |
| check_style( "DDEMLEvent", 0, 0, 0 ); | |
| check_style( "Message", 0, 0, 0 ); | |
| check_style( "#32768", 1, CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, CS_DROPSHADOW ); /* menu */ | |
| check_style( "#32769", 1, CS_DBLCLKS, 0 ); /* desktop */ | |
| check_style( "#32770", 1, CS_SAVEBITS | CS_DBLCLKS, 0 ); /* dialog */ | |
| todo_wine { check_style( "#32771", 1, CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW, 0 ); } /* task switch */ | |
| check_style( "#32772", 1, 0, 0 ); /* icon title */ | |
| } | |
| static void check_class_(int line, HINSTANCE inst, const char *name, const char *menu_name) | |
| { | |
| WNDCLASS wc; | |
| UINT atom = GetClassInfo(inst,name,&wc); | |
| ok_(__FILE__,line)( atom, "Class %s %p not found\n", name, inst ); | |
| if (atom) | |
| { | |
| if (wc.lpszMenuName && menu_name) | |
| ok_(__FILE__,line)( !strcmp( menu_name, wc.lpszMenuName ), | |
| "Wrong name %s/%s for class %s %p\n", | |
| wc.lpszMenuName, menu_name, name, inst ); | |
| else | |
| ok_(__FILE__,line)( !menu_name == !wc.lpszMenuName, "Wrong name %p/%p for class %s %p\n", | |
| wc.lpszMenuName, menu_name, name, inst ); | |
| } | |
| } | |
| #define check_class(inst,name,menu) check_class_(__LINE__,inst,name,menu) | |
| static void check_instance_( int line, const char *name, HINSTANCE inst, | |
| HINSTANCE info_inst, HINSTANCE gcl_inst ) | |
| { | |
| WNDCLASSA wc; | |
| HWND hwnd; | |
| ok_(__FILE__,line)( GetClassInfo( inst, name, &wc ), "Couldn't find class %s inst %p\n", name, inst ); | |
| ok_(__FILE__,line)( wc.hInstance == info_inst, "Wrong info instance %p/%p for class %s\n", | |
| wc.hInstance, info_inst, name ); | |
| hwnd = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, inst, 0 ); | |
| ok_(__FILE__,line)( hwnd != NULL, "Couldn't create window for class %s inst %p\n", name, inst ); | |
| ok_(__FILE__,line)( (HINSTANCE)GetClassLongPtrA( hwnd, GCLP_HMODULE ) == gcl_inst, | |
| "Wrong GCL instance %p/%p for class %s\n", | |
| (HINSTANCE)GetClassLongPtrA( hwnd, GCLP_HMODULE ), gcl_inst, name ); | |
| ok_(__FILE__,line)( (HINSTANCE)GetWindowLongPtrA( hwnd, GWLP_HINSTANCE ) == inst, | |
| "Wrong GWL instance %p/%p for window %s\n", | |
| (HINSTANCE)GetWindowLongPtrA( hwnd, GWLP_HINSTANCE ), inst, name ); | |
| ok_(__FILE__,line)(!UnregisterClassA(name, inst), | |
| "UnregisterClassA should fail while exists a class window\n"); | |
| ok_(__FILE__,line)(GetLastError() == ERROR_CLASS_HAS_WINDOWS, | |
| "GetLastError() should be set to ERROR_CLASS_HAS_WINDOWS not %d\n", GetLastError()); | |
| DestroyWindow(hwnd); | |
| } | |
| #define check_instance(name,inst,info_inst,gcl_inst) check_instance_(__LINE__,name,inst,info_inst,gcl_inst) | |
| struct class_info | |
| { | |
| const char *name; | |
| HINSTANCE inst, info_inst, gcl_inst; | |
| }; | |
| static DWORD WINAPI thread_proc(void *param) | |
| { | |
| struct class_info *class_info = param; | |
| check_instance(class_info->name, class_info->inst, class_info->info_inst, class_info->gcl_inst); | |
| return 0; | |
| } | |
| static void check_thread_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst ) | |
| { | |
| HANDLE hThread; | |
| DWORD tid; | |
| struct class_info class_info; | |
| class_info.name = name; | |
| class_info.inst = inst; | |
| class_info.info_inst = info_inst; | |
| class_info.gcl_inst = gcl_inst; | |
| hThread = CreateThread(NULL, 0, thread_proc, &class_info, 0, &tid); | |
| ok(hThread != NULL, "CreateThread failed, error %d\n", GetLastError()); | |
| ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n"); | |
| CloseHandle(hThread); | |
| } | |
| /* test various instance parameters */ | |
| static void test_instances(void) | |
| { | |
| WNDCLASSA cls, wc; | |
| HWND hwnd, hwnd2; | |
| const char *name = "__test__"; | |
| HINSTANCE kernel32 = GetModuleHandleA("kernel32"); | |
| HINSTANCE user32 = GetModuleHandleA("user32"); | |
| HINSTANCE main_module = GetModuleHandleA(NULL); | |
| DWORD r; | |
| char buffer[0x10]; | |
| memset( &cls, 0, sizeof(cls) ); | |
| cls.style = CS_HREDRAW | CS_VREDRAW; | |
| cls.lpfnWndProc = ClassTest_WndProc; | |
| cls.cbClsExtra = 0; | |
| cls.cbWndExtra = 0; | |
| cls.lpszClassName = name; | |
| cls.lpszMenuName = "main_module"; | |
| cls.hInstance = main_module; | |
| ok( RegisterClassA( &cls ), "Failed to register local class for main module\n" ); | |
| check_class( main_module, name, "main_module" ); | |
| check_instance( name, main_module, main_module, main_module ); | |
| check_thread_instance( name, main_module, main_module, main_module ); | |
| cls.lpszMenuName = "kernel32"; | |
| cls.hInstance = kernel32; | |
| ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); | |
| check_class( kernel32, name, "kernel32" ); | |
| check_class( main_module, name, "main_module" ); | |
| check_instance( name, kernel32, kernel32, kernel32 ); | |
| check_thread_instance( name, kernel32, kernel32, kernel32 ); | |
| ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); | |
| /* Bug 2631 - Supplying an invalid number of bytes fails */ | |
| cls.cbClsExtra = 0; | |
| cls.cbWndExtra = -1; | |
| SetLastError(0xdeadbeef); | |
| ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), | |
| "Failed with invalid number of WndExtra bytes\n"); | |
| cls.cbClsExtra = -1; | |
| cls.cbWndExtra = 0; | |
| SetLastError(0xdeadbeef); | |
| ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), | |
| "Failed with invalid number of ClsExtra bytes\n"); | |
| cls.cbClsExtra = -1; | |
| cls.cbWndExtra = -1; | |
| SetLastError(0xdeadbeef); | |
| ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)), | |
| "Failed with invalid number of ClsExtra and cbWndExtra bytes\n"); | |
| cls.cbClsExtra = 0; | |
| cls.cbWndExtra = 0; | |
| SetLastError(0xdeadbeef); | |
| /* setting global flag doesn't change status of class */ | |
| hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); | |
| ok( hwnd != 0, "CreateWindow failed error %u\n", GetLastError()); | |
| SetClassLongA( hwnd, GCL_STYLE, CS_GLOBALCLASS ); | |
| cls.lpszMenuName = "kernel32"; | |
| cls.hInstance = kernel32; | |
| ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); | |
| check_class( kernel32, name, "kernel32" ); | |
| check_class( main_module, name, "main_module" ); | |
| check_instance( name, kernel32, kernel32, kernel32 ); | |
| check_instance( name, main_module, main_module, main_module ); | |
| check_thread_instance( name, kernel32, kernel32, kernel32 ); | |
| check_thread_instance( name, main_module, main_module, main_module ); | |
| ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); | |
| /* changing the instance doesn't make it global */ | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, 0 ); | |
| ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); | |
| check_class( kernel32, name, "kernel32" ); | |
| check_instance( name, kernel32, kernel32, kernel32 ); | |
| check_thread_instance( name, kernel32, kernel32, kernel32 ); | |
| ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); | |
| ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); | |
| /* GetClassInfo with instance 0 finds user32 instance */ | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, (LONG_PTR)user32 ); | |
| ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); | |
| check_class( kernel32, name, "kernel32" ); | |
| check_class( user32, name, "main_module" ); | |
| check_class( 0, name, "main_module" ); | |
| check_instance( name, kernel32, kernel32, kernel32 ); | |
| check_instance( name, user32, 0, user32 ); | |
| check_instance( name, 0, 0, kernel32 ); | |
| check_thread_instance( name, kernel32, kernel32, kernel32 ); | |
| check_thread_instance( name, user32, 0, user32 ); | |
| check_thread_instance( name, 0, 0, kernel32 ); | |
| ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, 0x12345678 ); | |
| ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" ); | |
| check_class( kernel32, name, "kernel32" ); | |
| check_class( (HINSTANCE)0x12345678, name, "main_module" ); | |
| check_instance( name, kernel32, kernel32, kernel32 ); | |
| check_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 ); | |
| check_thread_instance( name, kernel32, kernel32, kernel32 ); | |
| check_thread_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 ); | |
| ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); | |
| /* creating a window with instance 0 uses the first class found */ | |
| cls.hInstance = (HINSTANCE)0xdeadbeef; | |
| cls.lpszMenuName = "deadbeef"; | |
| cls.style = 3; | |
| ok( RegisterClassA( &cls ), "Failed to register local class for deadbeef\n" ); | |
| hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); | |
| ok( GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == 0xdeadbeef, | |
| "Didn't get deadbeef class for null instance\n" ); | |
| DestroyWindow( hwnd2 ); | |
| ok( UnregisterClassA( name, (HINSTANCE)0xdeadbeef ), "Unregister failed for deadbeef\n" ); | |
| hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); | |
| ok( (HINSTANCE)GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == kernel32, | |
| "Didn't get kernel32 class for null instance\n" ); | |
| DestroyWindow( hwnd2 ); | |
| r = GetClassName( hwnd, buffer, 4 ); | |
| ok( r == 3, "expected 3, got %d\n", r ); | |
| ok( !strcmp( buffer, "__t"), "name wrong: %s\n", buffer ); | |
| ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" ); | |
| hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 ); | |
| ok( GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == 0x12345678, | |
| "Didn't get 12345678 class for null instance\n" ); | |
| DestroyWindow( hwnd2 ); | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, (LONG_PTR)main_module ); | |
| DestroyWindow( hwnd ); | |
| /* null handle means the same thing as main module */ | |
| cls.lpszMenuName = "null"; | |
| cls.hInstance = 0; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering local class for null instance\n" ); | |
| ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %d\n", GetLastError() ); | |
| ok( UnregisterClassA( name, main_module ), "Unregister failed for main module\n" ); | |
| ok( RegisterClassA( &cls ), "Failed to register local class for null instance\n" ); | |
| /* must be found with main module handle */ | |
| check_class( main_module, name, "null" ); | |
| check_instance( name, main_module, main_module, main_module ); | |
| check_thread_instance( name, main_module, main_module, main_module ); | |
| ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" ); | |
| ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %d\n", GetLastError() ); | |
| ok( UnregisterClassA( name, 0 ), "Unregister failed for null instance\n" ); | |
| /* registering for user32 always fails */ | |
| cls.lpszMenuName = "user32"; | |
| cls.hInstance = user32; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering local class for user32\n" ); | |
| ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %d\n", GetLastError() ); | |
| cls.style |= CS_GLOBALCLASS; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering global class for user32\n" ); | |
| ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %d\n", GetLastError() ); | |
| /* unregister is OK though */ | |
| cls.hInstance = main_module; | |
| ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" ); | |
| ok( UnregisterClassA( name, user32 ), "Unregister failed for user32\n" ); | |
| /* instance doesn't matter for global class */ | |
| cls.style |= CS_GLOBALCLASS; | |
| cls.lpszMenuName = "main_module"; | |
| cls.hInstance = main_module; | |
| ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" ); | |
| cls.lpszMenuName = "kernel32"; | |
| cls.hInstance = kernel32; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" ); | |
| ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %d\n", GetLastError() ); | |
| /* even if global flag is cleared */ | |
| hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); | |
| SetClassLongA( hwnd, GCL_STYLE, 0 ); | |
| ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" ); | |
| ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %d\n", GetLastError() ); | |
| check_class( main_module, name, "main_module" ); | |
| check_class( kernel32, name, "main_module" ); | |
| check_class( 0, name, "main_module" ); | |
| check_class( (HINSTANCE)0x12345678, name, "main_module" ); | |
| check_instance( name, main_module, main_module, main_module ); | |
| check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module ); | |
| check_thread_instance( name, main_module, main_module, main_module ); | |
| check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module ); | |
| /* changing the instance for global class doesn't make much difference */ | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, 0xdeadbeef ); | |
| check_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef ); | |
| check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef ); | |
| check_thread_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef ); | |
| check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef ); | |
| DestroyWindow( hwnd ); | |
| ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" ); | |
| ok( !UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister succeeded the second time\n" ); | |
| ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %d\n", GetLastError() ); | |
| cls.hInstance = (HINSTANCE)0x12345678; | |
| ok( RegisterClassA( &cls ), "Failed to register global class for dummy instance\n" ); | |
| check_instance( name, main_module, main_module, (HINSTANCE)0x12345678 ); | |
| check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 ); | |
| check_thread_instance( name, main_module, main_module, (HINSTANCE)0x12345678 ); | |
| check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 ); | |
| ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" ); | |
| /* check system classes */ | |
| /* we cannot register a global class with the name of a system class */ | |
| cls.style |= CS_GLOBALCLASS; | |
| cls.lpszMenuName = "button_main_module"; | |
| cls.lpszClassName = "BUTTON"; | |
| cls.hInstance = main_module; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering global button class for main module\n" ); | |
| ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %d\n", GetLastError() ); | |
| cls.hInstance = kernel32; | |
| ok( !RegisterClassA( &cls ), "Succeeded registering global button class for kernel32\n" ); | |
| ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %d\n", GetLastError() ); | |
| /* local class is OK however */ | |
| cls.style &= ~CS_GLOBALCLASS; | |
| cls.lpszMenuName = "button_main_module"; | |
| cls.hInstance = main_module; | |
| ok( RegisterClassA( &cls ), "Failed to register local button class for main module\n" ); | |
| check_class( main_module, "BUTTON", "button_main_module" ); | |
| cls.lpszMenuName = "button_kernel32"; | |
| cls.hInstance = kernel32; | |
| ok( RegisterClassA( &cls ), "Failed to register local button class for kernel32\n" ); | |
| check_class( kernel32, "BUTTON", "button_kernel32" ); | |
| check_class( main_module, "BUTTON", "button_main_module" ); | |
| ok( UnregisterClassA( "BUTTON", kernel32 ), "Unregister failed for kernel32 button\n" ); | |
| ok( UnregisterClassA( "BUTTON", main_module ), "Unregister failed for main module button\n" ); | |
| /* GetClassInfo sets instance to passed value for global classes */ | |
| check_instance( "BUTTON", 0, 0, user32 ); | |
| check_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); | |
| check_instance( "BUTTON", user32, 0, user32 ); | |
| check_thread_instance( "BUTTON", 0, 0, user32 ); | |
| check_thread_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); | |
| check_thread_instance( "BUTTON", user32, 0, user32 ); | |
| /* we can unregister system classes */ | |
| ok( GetClassInfo( 0, "BUTTON", &wc ), "Button class not found with null instance\n" ); | |
| ok( GetClassInfo( kernel32, "BUTTON", &wc ), "Button class not found with kernel32\n" ); | |
| ok( UnregisterClass( "BUTTON", (HINSTANCE)0x12345678 ), "Failed to unregister button\n" ); | |
| ok( !UnregisterClass( "BUTTON", (HINSTANCE)0x87654321 ), "Unregistered button a second time\n" ); | |
| ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %d\n", GetLastError() ); | |
| ok( !GetClassInfo( 0, "BUTTON", &wc ), "Button still exists\n" ); | |
| /* last error not set reliably */ | |
| /* we can change the instance of a system class */ | |
| check_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); | |
| check_thread_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 ); | |
| hwnd = CreateWindowExA( 0, "EDIT", "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 ); | |
| SetClassLongPtrA( hwnd, GCLP_HMODULE, 0xdeadbeef ); | |
| check_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef ); | |
| check_thread_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef ); | |
| DestroyWindow(hwnd); | |
| } | |
| static void test_builtinproc(void) | |
| { | |
| /* Edit behaves differently */ | |
| static const CHAR NORMAL_CLASSES[][10] = { | |
| "Button", | |
| "Static", | |
| "ComboBox", | |
| "ComboLBox", | |
| "ListBox", | |
| "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 */ | |
| WNDPROC pDefWindowProcA, pDefWindowProcW; | |
| WNDPROC oldproc; | |
| WNDCLASSEXA cls; /* the memory layout of WNDCLASSEXA and WNDCLASSEXW is the same */ | |
| WCHAR buf[128]; | |
| ATOM atom; | |
| HWND hwnd; | |
| int i; | |
| pDefWindowProcA = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcA"); | |
| pDefWindowProcW = (void *)GetProcAddress(GetModuleHandle("user32.dll"), "DefWindowProcW"); | |
| for (i = 0; i < 4; i++) | |
| { | |
| 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)); | |
| } | |
| /* built-in winproc - window A/W type automatically detected */ | |
| ZeroMemory(&cls, sizeof(cls)); | |
| cls.cbSize = sizeof(cls); | |
| cls.hInstance = GetModuleHandle(NULL); | |
| cls.hbrBackground = GetStockObject (WHITE_BRUSH); | |
| cls.lpszClassName = classA; | |
| cls.lpfnWndProc = pDefWindowProcW; | |
| atom = RegisterClassExA(&cls); | |
| hwnd = CreateWindowExW(0, classW, NULL, WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0); | |
| 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"); | |
| SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); | |
| ok(IsWindowUnicode(hwnd) == FALSE, "SetWindowLongPtrA should have switched window to ANSI\n"); | |
| DestroyWindow(hwnd); | |
| UnregisterClass((LPSTR)(DWORD_PTR)atom, GetModuleHandle(NULL)); | |
| /* custom winproc - the same function can be used as both A and W*/ | |
| ZeroMemory(&cls, sizeof(cls)); | |
| cls.cbSize = sizeof(cls); | |
| cls.hInstance = GetModuleHandle(NULL); | |
| cls.hbrBackground = GetStockObject (WHITE_BRUSH); | |
| cls.lpszClassName = classA; | |
| cls.lpfnWndProc = ClassTest_WndProc2; | |
| atom = RegisterClassExA(&cls); | |
| hwnd = CreateWindowExW(0, classW, NULL, WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0); | |
| ok(IsWindowUnicode(hwnd) == FALSE, "Window should be ANSI\n"); | |
| SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); | |
| ok(IsWindowUnicode(hwnd), "SetWindowLongPtrW should have changed window to Unicode\n"); | |
| SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); | |
| ok(IsWindowUnicode(hwnd) == FALSE, "SetWindowLongPtrA should have changed window to ANSI\n"); | |
| DestroyWindow(hwnd); | |
| UnregisterClass((LPSTR)(DWORD_PTR)atom, GetModuleHandle(NULL)); | |
| /* 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++) | |
| { | |
| WNDPROC procA, procW; | |
| hwnd = CreateWindowExA(0, NORMAL_CLASSES[i], classA, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, | |
| NULL, NULL, NULL, 0); | |
| ok(hwnd != NULL, "Couldn't create window of class %s\n", NORMAL_CLASSES[i]); | |
| SetWindowText(hwnd, classA); /* ComboBox needs this */ | |
| procA = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC); | |
| procW = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC); | |
| ok(!IS_WNDPROC_HANDLE(procA), "procA should not be a handle for %s (%p)\n", NORMAL_CLASSES[i], procA); | |
| ok(!IS_WNDPROC_HANDLE(procW), "procW should not be a handle for %s (%p)\n", NORMAL_CLASSES[i], procW); | |
| CallWindowProcA(procA, hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT A/A invalid return for class %s\n", NORMAL_CLASSES[i]); | |
| CallWindowProcA(procW, hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT A/W invalid return for class %s\n", NORMAL_CLASSES[i]); | |
| CallWindowProcW(procA, hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT W/A invalid return for class %s\n", NORMAL_CLASSES[i]); | |
| CallWindowProcW(procW, hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT W/W invalid return for class %s\n", NORMAL_CLASSES[i]); | |
| oldproc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); | |
| ok(IS_WNDPROC_HANDLE(oldproc) == FALSE, "Class %s shouldn't return a handle\n", NORMAL_CLASSES[i]); | |
| SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)oldproc); | |
| DestroyWindow(hwnd); | |
| } | |
| /* Edit controls are special - they return a wndproc handle when GetWindowLongPtr is called with a different A/W. | |
| * On the other hand there is no W->A->W conversion so this control is treated specially. */ | |
| hwnd = CreateWindowW(WC_EDITW, unistring, WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0); | |
| /* GetClassLongPtr returns that both the Unicode and ANSI wndproc */ | |
| ok(IS_WNDPROC_HANDLE(GetClassLongPtrA(hwnd, GCLP_WNDPROC)) == FALSE, "Edit control class should have a Unicode wndproc\n"); | |
| ok(IS_WNDPROC_HANDLE(GetClassLongPtrW(hwnd, GCLP_WNDPROC)) == FALSE, "Edit control class should have a ANSI wndproc\n"); | |
| /* But GetWindowLongPtr returns only a handle for the ANSI one */ | |
| ok(IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Edit control should return a wndproc handle\n"); | |
| ok(!IS_WNDPROC_HANDLE(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)), "Edit control shouldn't return a W wndproc handle\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| SetWindowTextW(hwnd, classW); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc2); | |
| /* SetWindowLongPtr returns a wndproc handle - like GetWindowLongPtr */ | |
| ok(IS_WNDPROC_HANDLE(oldproc), "Edit control should return a wndproc handle\n"); | |
| ok(IsWindowUnicode(hwnd) == FALSE, "SetWindowLongPtrA should have changed window to ANSI\n"); | |
| SetWindowTextA(hwnd, classA); /* Windows resets the title to WideStringToMultiByte(unistring) */ | |
| memset(buf, 0, sizeof(buf)); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT invalid return\n"); | |
| SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)oldproc); | |
| DestroyWindow(hwnd); | |
| hwnd = CreateWindowA(WC_EDITA, classA, WS_OVERLAPPEDWINDOW, | |
| CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, NULL, 0); | |
| /* GetClassLongPtr returns that both the Unicode and ANSI wndproc */ | |
| ok(!IS_WNDPROC_HANDLE(GetClassLongPtrA(hwnd, GCLP_WNDPROC)), "Edit control class should have a Unicode wndproc\n"); | |
| ok(!IS_WNDPROC_HANDLE(GetClassLongPtrW(hwnd, GCLP_WNDPROC)), "Edit control class should have a ANSI wndproc\n"); | |
| /* But GetWindowLongPtr returns only a handle for the Unicode one */ | |
| ok(!IS_WNDPROC_HANDLE(GetWindowLongPtrA(hwnd, GWLP_WNDPROC)), "Edit control shouldn't return an A wndproc handle\n"); | |
| ok(IS_WNDPROC_HANDLE(GetWindowLongPtrW(hwnd, GWLP_WNDPROC)), "Edit control should return a wndproc handle\n"); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classW, sizeof(classW)) == 0, "WM_GETTEXT invalid return\n"); | |
| oldproc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)ClassTest_WndProc); | |
| SetWindowTextW(hwnd, unistring); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| CallWindowProcW((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, unistring, sizeof(unistring)) == 0, "WM_GETTEXT invalid return\n"); | |
| SetWindowTextW(hwnd, classW); | |
| CallWindowProcA((WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC), hwnd, WM_GETTEXT, 120, (LPARAM)buf); | |
| ok(memcmp(buf, classA, sizeof(classA)) == 0, "WM_GETTEXT invalid return\n"); | |
| SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)oldproc); | |
| DestroyWindow(hwnd); | |
| } | |
| static LRESULT WINAPI TestDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
| { | |
| return DefWindowProc(hWnd, uMsg, wParam, lParam); | |
| } | |
| static BOOL RegisterTestDialog(HINSTANCE hInstance) | |
| { | |
| WNDCLASSEX wcx; | |
| ATOM atom = 0; | |
| ZeroMemory(&wcx, sizeof(WNDCLASSEX)); | |
| wcx.cbSize = sizeof(wcx); | |
| wcx.lpfnWndProc = TestDlgProc; | |
| wcx.cbClsExtra = 0; | |
| wcx.cbWndExtra = DLGWINDOWEXTRA; | |
| wcx.hInstance = hInstance; | |
| wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); | |
| wcx.hCursor = LoadCursor(NULL, IDC_ARROW); | |
| wcx.hbrBackground = GetStockObject(WHITE_BRUSH); | |
| wcx.lpszClassName = "TestDialog"; | |
| wcx.lpszMenuName = "TestDialog"; | |
| wcx.hIconSm = LoadImage(hInstance, MAKEINTRESOURCE(5), IMAGE_ICON, | |
| GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), | |
| LR_DEFAULTCOLOR); | |
| atom = RegisterClassEx(&wcx); | |
| ok(atom != 0, "RegisterClassEx returned 0\n"); | |
| return atom; | |
| } | |
| /* test registering a dialog box created by using the CLASS directive in a | |
| resource file, then test creating the dialog using CreateDialogParam. */ | |
| static void CreateDialogParamTest(HINSTANCE hInstance) | |
| { | |
| HWND hWndMain; | |
| if (RegisterTestDialog(hInstance)) | |
| { | |
| hWndMain = CreateDialogParam(hInstance, "CLASS_TEST_DIALOG", NULL, 0, 0); | |
| ok(hWndMain != NULL, "CreateDialogParam returned NULL\n"); | |
| ShowWindow(hWndMain, SW_SHOW); | |
| DestroyWindow(hWndMain); | |
| } | |
| } | |
| static const struct | |
| { | |
| const char name[9]; | |
| int value; | |
| int badvalue; | |
| } extra_values[] = | |
| { | |
| {"#32770",30,30}, /* Dialog */ | |
| #ifdef _WIN64 | |
| {"Edit",8,8}, | |
| #else | |
| {"Edit",6,8}, /* Windows XP 64-bit returns 8 also to 32-bit applications */ | |
| #endif | |
| }; | |
| static void test_extra_values(void) | |
| { | |
| int i; | |
| for(i=0; i< sizeof(extra_values)/sizeof(extra_values[0]); i++) | |
| { | |
| WNDCLASSEX wcx; | |
| BOOL ret = GetClassInfoEx(NULL,extra_values[i].name,&wcx); | |
| ok( ret, "GetClassInfo (0) failed for global class %s\n", extra_values[i].name); | |
| if (!ret) continue; | |
| ok(extra_values[i].value == wcx.cbWndExtra || broken(extra_values[i].badvalue == wcx.cbWndExtra), | |
| "expected %d, got %d\n", extra_values[i].value, wcx.cbWndExtra); | |
| } | |
| } | |
| START_TEST(class) | |
| { | |
| HANDLE hInstance = GetModuleHandleA( NULL ); | |
| test_extra_values(); | |
| if (!GetModuleHandleW(0)) | |
| { | |
| trace("Class test is incompatible with Win9x implementation, skipping\n"); | |
| return; | |
| } | |
| ClassTest(hInstance,FALSE); | |
| ClassTest(hInstance,TRUE); | |
| CreateDialogParamTest(hInstance); | |
| test_styles(); | |
| test_builtinproc(); | |
| /* this test unregisters the Button class so it should be executed at the end */ | |
| test_instances(); | |
| } |