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