Module: wine Branch: master Commit: c25c0198833885581dd381240882fc9312c81f74 URL: http://source.winehq.org/git/wine.git/?a=commit;h=c25c0198833885581dd3812408...
Author: Alexandre Julliard julliard@winehq.org Date: Mon Oct 28 18:41:04 2013 +0100
user32: Automatically load comctl32 when one of its classes is requested.
---
dlls/user32/class.c | 84 ++++++++++++++++++++++++++++++++++--------- dlls/user32/tests/class.c | 89 +++++++++++++++++++++++++++++++++++++++++++-- dlls/user32/win.c | 9 ++++- 3 files changed, 160 insertions(+), 22 deletions(-)
diff --git a/dlls/user32/class.c b/dlls/user32/class.c index fa340dc..349aedf 100644 --- a/dlls/user32/class.c +++ b/dlls/user32/class.c @@ -121,6 +121,47 @@ ATOM get_int_atom_value( LPCWSTR name )
/*********************************************************************** + * is_comctl32_class + */ +static BOOL is_comctl32_class( const WCHAR *name ) +{ + static const WCHAR classesW[][20] = + { + {'C','o','m','b','o','B','o','x','E','x','3','2',0}, + {'m','s','c','t','l','s','_','h','o','t','k','e','y','3','2',0}, + {'m','s','c','t','l','s','_','p','r','o','g','r','e','s','s','3','2',0}, + {'m','s','c','t','l','s','_','s','t','a','t','u','s','b','a','r','3','2',0}, + {'m','s','c','t','l','s','_','t','r','a','c','k','b','a','r','3','2',0}, + {'m','s','c','t','l','s','_','u','p','d','o','w','n','3','2',0}, + {'N','a','t','i','v','e','F','o','n','t','C','t','l',0}, + {'R','e','B','a','r','W','i','n','d','o','w','3','2',0}, + {'S','y','s','A','n','i','m','a','t','e','3','2',0}, + {'S','y','s','D','a','t','e','T','i','m','e','P','i','c','k','3','2',0}, + {'S','y','s','H','e','a','d','e','r','3','2',0}, + {'S','y','s','I','P','A','d','d','r','e','s','s','3','2',0}, + {'S','y','s','L','i','s','t','V','i','e','w','3','2',0}, + {'S','y','s','M','o','n','t','h','C','a','l','3','2',0}, + {'S','y','s','P','a','g','e','r',0}, + {'S','y','s','T','a','b','C','o','n','t','r','o','l','3','2',0}, + {'S','y','s','T','r','e','e','V','i','e','w','3','2',0}, + {'T','o','o','l','b','a','r','W','i','n','d','o','w','3','2',0}, + {'t','o','o','l','t','i','p','s','_','c','l','a','s','s','3','2',0}, + }; + + int min = 0, max = (sizeof(classesW) / sizeof(classesW[0])) - 1; + + while (min <= max) + { + int res, pos = (min + max) / 2; + if (!(res = strcmpiW( name, classesW[pos] ))) return TRUE; + if (res < 0) max = pos - 1; + else min = pos + 1; + } + return FALSE; +} + + +/*********************************************************************** * set_server_info * * Set class info with the wine server. @@ -261,35 +302,44 @@ static void CLASS_FreeClass( CLASS *classPtr ) * CLASS_FindClass * * Return a pointer to the class. - * hinstance has been normalized by the caller. */ static CLASS *CLASS_FindClass( LPCWSTR name, HINSTANCE hinstance ) { + static const WCHAR comctl32W[] = {'c','o','m','c','t','l','3','2','.','d','l','l',0}; struct list *ptr; ATOM atom = get_int_atom_value( name );
GetDesktopWindow(); /* create the desktop window to trigger builtin class registration */
- USER_Lock(); - - LIST_FOR_EACH( ptr, &class_list ) + for (;;) { - CLASS *class = LIST_ENTRY( ptr, CLASS, entry ); - if (atom) - { - if (class->atomName != atom) continue; - } - else - { - if (!name || strcmpiW( class->name, name )) continue; - } - if (!hinstance || !class->local || class->hInstance == hinstance) + USER_Lock(); + + LIST_FOR_EACH( ptr, &class_list ) { - TRACE("%s %p -> %p\n", debugstr_w(name), hinstance, class); - return class; + CLASS *class = LIST_ENTRY( ptr, CLASS, entry ); + if (atom) + { + if (class->atomName != atom) continue; + } + else + { + if (!name || strcmpiW( class->name, name )) continue; + } + if (!class->local || class->hInstance == hinstance) + { + TRACE("%s %p -> %p\n", debugstr_w(name), hinstance, class); + return class; + } } + USER_Unlock(); + + if (!is_comctl32_class( name )) break; + if (GetModuleHandleW( comctl32W )) break; + if (!LoadLibraryW( comctl32W )) break; + TRACE( "%s retrying after loading comctl32\n", debugstr_w(name) ); } - USER_Unlock(); + TRACE("%s %p -> not found\n", debugstr_w(name), hinstance); return NULL; } diff --git a/dlls/user32/tests/class.c b/dlls/user32/tests/class.c index f32938c..c8d6a26 100644 --- a/dlls/user32/tests/class.c +++ b/dlls/user32/tests/class.c @@ -28,13 +28,11 @@ #include "wine/test.h" #include "windef.h" #include "winbase.h" +#include "winnls.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}; +#include "commctrl.h"
#define NUMCLASSWORDS 4
@@ -1029,9 +1027,91 @@ static void test_icons(void) DestroyWindow(hwnd); }
+static void test_comctl32_class( const char *name ) +{ + WNDCLASSA wcA; + WNDCLASSW wcW; + BOOL ret; + HMODULE module; + WCHAR nameW[20]; + HWND hwnd; + + module = GetModuleHandleA( "comctl32" ); + ok( !module, "comctl32 already loaded\n" ); + 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) ); + ret = GetClassInfoW( 0, nameW, &wcW ); + ok( ret, "GetClassInfoW failed for %s\n", name ); + module = GetModuleHandleA( "comctl32" ); + ok( module != 0, "comctl32 not loaded\n" ); + FreeLibrary( module ); + module = GetModuleHandleA( "comctl32" ); + ok( !module, "comctl32 still loaded\n" ); + hwnd = CreateWindowA( name, "test", WS_OVERLAPPEDWINDOW, 0, 0, 10, 10, NULL, NULL, NULL, 0 ); + ok( hwnd != 0, "failed to create window for %s\n", name ); + module = GetModuleHandleA( "comctl32" ); + ok( module != 0, "comctl32 not loaded\n" ); +} + +/* verify that comctl32 classes are automatically loaded by user32 */ +static void test_comctl32_classes(void) +{ + char path_name[MAX_PATH]; + PROCESS_INFORMATION info; + STARTUPINFOA startup; + char **argv; + int i; + + static const char *classes[] = + { + ANIMATE_CLASSA, + WC_COMBOBOXEXA, + DATETIMEPICK_CLASSA, + WC_HEADERA, + HOTKEY_CLASSA, + WC_IPADDRESSA, + WC_LISTVIEWA, + MONTHCAL_CLASSA, + WC_NATIVEFONTCTLA, + WC_PAGESCROLLERA, + PROGRESS_CLASSA, + REBARCLASSNAMEA, + STATUSCLASSNAMEA, + WC_TABCONTROLA, + TOOLBARCLASSNAMEA, + TOOLTIPS_CLASSA, + TRACKBAR_CLASSA, + WC_TREEVIEWA, + UPDOWN_CLASSA + }; + + winetest_get_mainargs( &argv ); + for (i = 0; i < sizeof(classes) / sizeof(classes[0]); i++) + { + memset( &startup, 0, sizeof(startup) ); + startup.cb = sizeof( startup ); + sprintf( path_name, "%s class %s", argv[0], classes[i] ); + ok( CreateProcessA( NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info ), + "CreateProcess failed.\n" ); + winetest_wait_child_process( info.hProcess ); + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + } +} + START_TEST(class) { + char **argv; HANDLE hInstance = GetModuleHandleA( NULL ); + int argc = winetest_get_mainargs( &argv ); + + if (argc >= 3) + { + test_comctl32_class( argv[2] ); + return; + }
test_GetClassInfo(); test_extra_values(); @@ -1048,6 +1128,7 @@ START_TEST(class) test_styles(); test_builtinproc(); test_icons(); + test_comctl32_classes();
/* this test unregisters the Button class so it should be executed at the end */ test_instances(); diff --git a/dlls/user32/win.c b/dlls/user32/win.c index 30a7c6f..7223b41 100644 --- a/dlls/user32/win.c +++ b/dlls/user32/win.c @@ -1448,7 +1448,14 @@ HWND WIN_CreateWindowEx( CREATESTRUCTW *cs, LPCWSTR className, HINSTANCE module, /* Create the window structure */
if (!(wndPtr = create_window_handle( parent, owner, className, module, unicode ))) - return 0; + { + WNDCLASSW wc; + /* if it's a comctl32 class, GetClassInfo will load it, then we can retry */ + if (GetLastError() != ERROR_INVALID_HANDLE || + !GetClassInfoW( 0, className, &wc ) || + !(wndPtr = create_window_handle( parent, owner, className, module, unicode ))) + return 0; + } hwnd = wndPtr->obj.handle;
/* Fill the window structure */