When comctl32 v6 is loaded and v6 manifest is disabled, comctl32 v5 should be used when creating a common control window. However, before this patch, GetModuleHandleW(L"comctl32") return a handle to comctl32 v6 in this case, so comctl32 v5 doesn't get loaded and creating comctl32 v5 windows fails. We need to check if comctl32 v5 is actually loaded by using GetModuleHandleW() with the absolute path of the comctl32 v5 dll.
Fix Word 2010 file open dialog doesn't show a listview.
-- v3: comctl32: Use a magic value to check if an image list is valid. comctl32/tests: Test image list interoperation with comctl32 v5 and v6. user32: Fix loading comctl32 v5 after comctl32 v6 is loaded. comctl32/tests: Test creating v5 windows after v6 manifest is deactivated.
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/comctl32/tests/misc.c | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c index 29ba90cffb2..064f7d06da3 100644 --- a/dlls/comctl32/tests/misc.c +++ b/dlls/comctl32/tests/misc.c @@ -45,6 +45,7 @@ static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
static HMODULE hComctl32; +static BOOL todo_create_v5_window;
/* For message tests */ enum seq_index @@ -415,7 +416,10 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno ok( !wc.hInstance, "System class %s has hInstance %p\n", name, wc.hInstance );
hwnd = CreateWindowA(name, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA(NULL), 0); + todo_wine_if(todo_create_v5_window) ok( hwnd != NULL, "Failed to create window for class %s.\n", name ); + if (!hwnd) + return; GetClassNameA(hwnd, buff, ARRAY_SIZE(buff)); ok( !strcmp(name, buff), "Unexpected class name %s, expected %s.\n", buff, name );
@@ -1448,5 +1452,10 @@ START_TEST(misc) test_CCM_SETVERSION(TRUE);
unload_v6_module(ctx_cookie, hCtx); + + /* Now that v6 manifest is deactivated, test that comctl32 v5 windows can be created */ + todo_create_v5_window = TRUE; + test_comctl32_classes(FALSE); + FreeLibrary(hComctl32); }
From: Zhiyi Zhang zzhang@codeweavers.com
When comctl32 v6 is loaded and v6 manifest is disabled, comctl32 v5 should be used when creating a common control window. However, before this patch, GetModuleHandleW(L"comctl32") return a handle to comctl32 v6 in this case, so comctl32 v5 doesn't get loaded and creating comctl32 v5 windows fails. We need to check if comctl32 v5 is actually loaded by using GetModuleHandleW() with the absolute path of the comctl32 v5 dll.
Fix Word 2010 file open dialog doesn't show a listview. --- dlls/comctl32/tests/misc.c | 5 ----- dlls/user32/class.c | 11 ++++++----- 2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/dlls/comctl32/tests/misc.c b/dlls/comctl32/tests/misc.c index 064f7d06da3..c61b71b6309 100644 --- a/dlls/comctl32/tests/misc.c +++ b/dlls/comctl32/tests/misc.c @@ -45,7 +45,6 @@ static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
static HMODULE hComctl32; -static BOOL todo_create_v5_window;
/* For message tests */ enum seq_index @@ -416,10 +415,7 @@ static void check_class( const char *name, int must_exist, UINT style, UINT igno ok( !wc.hInstance, "System class %s has hInstance %p\n", name, wc.hInstance );
hwnd = CreateWindowA(name, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA(NULL), 0); - todo_wine_if(todo_create_v5_window) ok( hwnd != NULL, "Failed to create window for class %s.\n", name ); - if (!hwnd) - return; GetClassNameA(hwnd, buff, ARRAY_SIZE(buff)); ok( !strcmp(name, buff), "Unexpected class name %s, expected %s.\n", buff, name );
@@ -1454,7 +1450,6 @@ START_TEST(misc) unload_v6_module(ctx_cookie, hCtx);
/* Now that v6 manifest is deactivated, test that comctl32 v5 windows can be created */ - todo_create_v5_window = TRUE; test_comctl32_classes(FALSE);
FreeLibrary(hComctl32); diff --git a/dlls/user32/class.c b/dlls/user32/class.c index 43636975e89..a754720caec 100644 --- a/dlls/user32/class.c +++ b/dlls/user32/class.c @@ -198,11 +198,6 @@ void get_class_version( UNICODE_STRING *name, UNICODE_STRING *version, BOOL load
if (IS_INTRESOURCE( name->Buffer ) || is_builtin_class( name->Buffer )) return;
- if (is_comctl32_class( name->Buffer )) - { - if (load && !(hmod = GetModuleHandleW( L"comctl32" ))) hmod = LoadLibraryW( L"comctl32" ); - } - if (!RtlFindActivationContextSectionString( 0, NULL, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, name, &data )) { struct wndclass_redirect_data @@ -228,6 +223,12 @@ void get_class_version( UNICODE_STRING *name, UNICODE_STRING *version, BOOL load name->Length = wndclass->name_len; name->Buffer[name->Length / sizeof(WCHAR)] = 0; } + /* comctl32 v5 */ + else if (load && is_comctl32_class( name->Buffer )) + { + hmod = GetModuleHandleW( L"C:\windows\system32\comctl32.dll" ); + if (!hmod) hmod = LoadLibraryW( L"C:\windows\system32\comctl32.dll" ); + }
if (load && hmod) {
From: Zhiyi Zhang zzhang@codeweavers.com
--- dlls/comctl32/tests/imagelist.c | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/dlls/comctl32/tests/imagelist.c b/dlls/comctl32/tests/imagelist.c index 3b547732fd5..8647fd5a62a 100644 --- a/dlls/comctl32/tests/imagelist.c +++ b/dlls/comctl32/tests/imagelist.c @@ -36,6 +36,7 @@ #include "initguid.h" #include "commoncontrols.h" #include "shellapi.h" +#include "shlwapi.h"
#include "wine/test.h" #include "v6util.h" @@ -2896,6 +2897,75 @@ static void init_functions(void) #undef X2 }
+static void test_imagelist_interop(void) +{ + HRESULT (WINAPI *pDllGetVersion_v5)(DLLVERSIONINFO *); + HRESULT (WINAPI *pDllGetVersion_v6)(DLLVERSIONINFO *); + HIMAGELIST (WINAPI *pImageList_Create_v5)(int, int, UINT, int, int); + BOOL (WINAPI *pImageList_Destroy_v5)(HIMAGELIST); + BOOL (WINAPI *pImageList_GetIconSize_v5)(HIMAGELIST, int *, int *); + DLLVERSIONINFO info; + HMODULE comctl32_v5; + HIMAGELIST himl; + HRESULT hr; + INT cx, cy; + BOOL ret; + + comctl32_v5 = LoadLibraryW(L"C:\windows\system32\comctl32.dll"); + ok(!!comctl32_v5, "Failed to load comctl32 v5.\n"); + + pDllGetVersion_v5 = (void *)GetProcAddress(comctl32_v5, "DllGetVersion"); + pDllGetVersion_v6 = (void *)GetProcAddress(GetModuleHandleA("comctl32.dll"), "DllGetVersion"); + pImageList_Create_v5 = (void *)GetProcAddress(comctl32_v5, "ImageList_Create"); + pImageList_Destroy_v5 = (void *)GetProcAddress(comctl32_v5, "ImageList_Destroy"); + pImageList_GetIconSize_v5 = (void *)GetProcAddress(comctl32_v5, "ImageList_GetIconSize"); + + /* Make sure we are testing the correct versions of comctl32 */ + info.cbSize = sizeof(info); + hr = pDllGetVersion_v5(&info); + ok(hr == S_OK, "DllGetVersion failed, hr %#lx.\n", hr); + ok(info.dwMajorVersion == 5, "Got unexpected major version %lu.\n", info.dwMajorVersion); + + info.cbSize = sizeof(info); + hr = pDllGetVersion_v6(&info); + ok(hr == S_OK, "DllGetVersion failed, hr %#lx.\n", hr); + ok(info.dwMajorVersion == 6, "Got unexpected major version %lu.\n", info.dwMajorVersion); + + /* Create a v5 imagelist and use it with v6 imagelist functions */ + himl = pImageList_Create_v5(1, 1, ILC_COLOR, 0, 1); + ok(!!himl, "ImageList_Create failed.\n"); + + cx = 0; + cy = 0; + ret = pImageList_GetIconSize(himl, &cx, &cy); + todo_wine + ok(ret, "ImageList_GetIconSize failed.\n"); + todo_wine + ok(cx == 1 && cy == 1, "Got unexpected size %dx%d.\n", cx, cy); + + ret = pImageList_Destroy(himl); + todo_wine + ok(ret, "ImageList_Destroy failed.\n"); + + /* Create a v6 imagelist and use it with v5 imagelist functions */ + himl = pImageList_Create(1, 1, ILC_COLOR, 0, 1); + ok(!!himl, "ImageList_Create failed.\n"); + + cx = 0; + cy = 0; + ret = pImageList_GetIconSize_v5(himl, &cx, &cy); + todo_wine + ok(ret, "ImageList_GetIconSize failed.\n"); + todo_wine + ok(cx == 1 && cy == 1, "Got unexpected size %dx%d.\n", cx, cy); + + ret = pImageList_Destroy_v5(himl); + todo_wine + ok(ret, "ImageList_Destroy failed.\n"); + + FreeLibrary(comctl32_v5); +} + START_TEST(imagelist) { ULONG_PTR ctx_cookie; @@ -2945,6 +3015,7 @@ START_TEST(imagelist) test_ImageList_DrawIndirect(); test_shell_imagelist(); test_iimagelist(); + test_imagelist_interop();
test_IImageList_Add_Remove(); test_IImageList_Get_SetImageCount();
From: Zhiyi Zhang zzhang@codeweavers.com
Use a magic value to check if an image list is valid instead of using vtable addresses. So that an image list from comctl32 v5 can be used with comctl32 v6 image list functions and vice versa.
Fix missing icons in the listview window of the Word 2010 file open dialog. --- dlls/comctl32/imagelist.c | 5 ++++- dlls/comctl32/tests/imagelist.c | 6 ------ 2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/dlls/comctl32/imagelist.c b/dlls/comctl32/imagelist.c index 493aebd0b45..93c502ec0bd 100644 --- a/dlls/comctl32/imagelist.c +++ b/dlls/comctl32/imagelist.c @@ -71,6 +71,7 @@ struct _IMAGELIST INT nOvlIdx[MAX_OVERLAYIMAGE]; /* 38: overlay images index */
/* not yet found out */ + DWORD magic; HBRUSH hbrBlend25; HBRUSH hbrBlend50; INT cInitial; @@ -3214,6 +3215,7 @@ static ULONG WINAPI ImageListImpl_Release(IImageList2 *iface) if (This->hbrBlend50) DeleteObject (This->hbrBlend50);
This->IImageList2_iface.lpVtbl = NULL; + This->magic = 0; Free(This->item_flags); Free(This); } @@ -3797,7 +3799,7 @@ static BOOL is_valid(HIMAGELIST himl) BOOL valid; __TRY { - valid = himl && himl->IImageList2_iface.lpVtbl == &ImageListImpl_Vtbl; + valid = himl && himl->magic == IMAGELIST_MAGIC; } __EXCEPT_PAGE_FAULT { @@ -3844,6 +3846,7 @@ static HRESULT ImageListImpl_CreateInstance(const IUnknown *pUnkOuter, REFIID ii if (!This) return E_OUTOFMEMORY;
This->IImageList2_iface.lpVtbl = &ImageListImpl_Vtbl; + This->magic = IMAGELIST_MAGIC; This->ref = 1;
ret = IImageList2_QueryInterface(&This->IImageList2_iface, iid, ppv); diff --git a/dlls/comctl32/tests/imagelist.c b/dlls/comctl32/tests/imagelist.c index 8647fd5a62a..c8a1a25b71d 100644 --- a/dlls/comctl32/tests/imagelist.c +++ b/dlls/comctl32/tests/imagelist.c @@ -2938,13 +2938,10 @@ static void test_imagelist_interop(void) cx = 0; cy = 0; ret = pImageList_GetIconSize(himl, &cx, &cy); - todo_wine ok(ret, "ImageList_GetIconSize failed.\n"); - todo_wine ok(cx == 1 && cy == 1, "Got unexpected size %dx%d.\n", cx, cy);
ret = pImageList_Destroy(himl); - todo_wine ok(ret, "ImageList_Destroy failed.\n");
/* Create a v6 imagelist and use it with v5 imagelist functions */ @@ -2954,13 +2951,10 @@ static void test_imagelist_interop(void) cx = 0; cy = 0; ret = pImageList_GetIconSize_v5(himl, &cx, &cy); - todo_wine ok(ret, "ImageList_GetIconSize failed.\n"); - todo_wine ok(cx == 1 && cy == 1, "Got unexpected size %dx%d.\n", cx, cy);
ret = pImageList_Destroy_v5(himl); - todo_wine ok(ret, "ImageList_Destroy failed.\n");
FreeLibrary(comctl32_v5);
On Mon Nov 3 06:47:12 2025 +0000, Nikolay Sivov wrote:
Yes, we can use this fix for now with a full path, but for remaining issue we'll need some tests.
Okay. Let's get these fixes in first to fix Office 2010. I will work on the GetModuleHandle() issue next.
This merge request was approved by Nikolay Sivov.