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.
-- v2: comctl32: Use a magic value to check if an image list is valid. comctl32/tests: Test image list interoperation with comctl32 v5 and v6.
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);
Let's get the image list fix in first. I will work on the GetModuleHandle() issue.
On Mon Nov 3 06:36:04 2025 +0000, Zhiyi Zhang wrote:
I see. You worry that user applications might use GetModuleHandle() with a relative path without activation contexts and expect the handle to be comctl32 v5, right?
Yes, we can use this fix for now with a full path, but for remaining issue we'll need some tests.