user32: Automatically load comctl32 when one of its classes is requested.
This commit is contained in:
parent
4c41c20e02
commit
c25c019883
|
@ -120,6 +120,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
|
||||
*
|
||||
|
@ -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)
|
||||
USER_Lock();
|
||||
|
||||
LIST_FOR_EACH( ptr, &class_list )
|
||||
{
|
||||
if (class->atomName != atom) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!name || strcmpiW( class->name, name )) continue;
|
||||
}
|
||||
if (!hinstance || !class->local || class->hInstance == hinstance)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue