Alexandre Julliard : user32: Automatically load comctl32 when one of its classes is requested.
Alexandre Julliard
julliard at winehq.org
Mon Oct 28 15:13:05 CDT 2013
Module: wine
Branch: master
Commit: c25c0198833885581dd381240882fc9312c81f74
URL: http://source.winehq.org/git/wine.git/?a=commit;h=c25c0198833885581dd381240882fc9312c81f74
Author: Alexandre Julliard <julliard at 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 */
More information about the wine-cvs
mailing list