Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43073
-- v3: comctl32: Skip restoring window proc if it has been modified. comctl32: Always use unicode messages for subclass procedures.
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43073 --- dlls/comctl32/tests/subclass.c | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+)
diff --git a/dlls/comctl32/tests/subclass.c b/dlls/comctl32/tests/subclass.c index e2e8573f4c0..684593e6ed7 100644 --- a/dlls/comctl32/tests/subclass.c +++ b/dlls/comctl32/tests/subclass.c @@ -34,9 +34,14 @@ static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
+#define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16)) + #define SEND_NEST 0x01 #define DELETE_SELF 0x02 #define DELETE_PREV 0x04 +#define EXPECT_UNICODE 0x10 +#define EXPECT_WNDPROC_1 0x20 +#define EXPECT_WNDPROC_3 0x40
struct message { int procnum; /* WndProc id message is expected from */ @@ -166,15 +171,47 @@ static void ok_sequence(const struct message *expected, const char *context) flush_sequence(); }
+static LRESULT WINAPI wnd_proc_1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); +static LRESULT WINAPI wnd_proc_3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +#define check_unicode(a, b) check_unicode_(__LINE__, a, b) +static void check_unicode_(int line, HWND hwnd, DWORD flags) +{ + WNDPROC proc; + BOOL ret; + + ret = IsWindowUnicode(hwnd); + ok_(__FILE__, line)(ret == !!(flags & EXPECT_UNICODE), "IsWindowUnicode returned %u\n", ret); + + proc = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC); + ok_(__FILE__, line)(IS_WNDPROC_HANDLE(proc) == !(flags & EXPECT_UNICODE), "got proc %p\n", proc); + + proc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC); + if (flags & EXPECT_UNICODE) + ok_(__FILE__, line)(IS_WNDPROC_HANDLE(proc), "got proc %p\n", proc); + else if (flags & EXPECT_WNDPROC_1) + ok_(__FILE__, line)(proc == wnd_proc_1, "got proc %p\n", proc); + else if (flags & EXPECT_WNDPROC_3) + todo_wine_if(proc == wnd_proc_1) ok_(__FILE__, line)(proc == wnd_proc_3, "got proc %p\n", proc); + else + ok_(__FILE__, line)(!IS_WNDPROC_HANDLE(proc), "got proc %p\n", proc); +} + static LRESULT WINAPI wnd_proc_1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { + DWORD flags = GetWindowLongA(hwnd, GWLP_USERDATA); struct message msg; + + todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags);
if(message == WM_USER) { msg.wParam = wParam; msg.procnum = 1; add_message(&msg); } + if (message == WM_CHAR) { + ok(!(wParam & ~0xff), "got wParam %#Ix\n", wParam); + } return DefWindowProcA(hwnd, message, wParam, lParam); }
@@ -194,7 +231,10 @@ static LRESULT WINAPI wnd_proc_3(HWND hwnd, UINT message, WPARAM wParam, LPARAM
static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uldSubclass, DWORD_PTR dwRefData) { + DWORD flags = GetWindowLongA(hwnd, GWLP_USERDATA); struct message msg; + + todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags);
if(message == WM_USER) { msg.wParam = wParam; @@ -205,13 +245,24 @@ static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARA if(dwRefData & DELETE_SELF) { pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass); pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass); + todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); } if(dwRefData & DELETE_PREV) + { pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass-1); + todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + } if(dwRefData & SEND_NEST) + { SendMessageA(hwnd, WM_USER, wParam+1, 0); + todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + } } } + if (message == WM_CHAR) { + todo_wine + ok(wParam == 0x30c2, "got wParam %#Ix\n", wParam); + } return pDefSubclassProc(hwnd, message, wParam, lParam); }
@@ -221,17 +272,27 @@ static void test_subclass(void) HWND hwnd = CreateWindowExA(0, "TestSubclass", "Test subclass", WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, 0, 0, 0, NULL); ok(hwnd != NULL, "failed to create test subclass wnd\n"); + check_unicode(hwnd, EXPECT_WNDPROC_1); + SetWindowLongA(hwnd, GWLP_USERDATA, EXPECT_WNDPROC_1);
ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); ok(ret == TRUE, "Expected TRUE\n"); + todo_wine check_unicode(hwnd, EXPECT_UNICODE); + SetWindowLongA(hwnd, GWLP_USERDATA, EXPECT_UNICODE); + SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_BasicTest, "Basic"); + SendMessageW(hwnd, WM_CHAR, 0x30c2, 1);
ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); ok(ret == TRUE, "Expected TRUE\n"); + todo_wine check_unicode(hwnd, EXPECT_UNICODE); + SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_DeletedTest, "Deleted"); + check_unicode(hwnd, EXPECT_WNDPROC_1); + SetWindowLongA(hwnd, GWLP_USERDATA, EXPECT_WNDPROC_1);
SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_AfterDeletedTest, "After Deleted"); @@ -239,15 +300,22 @@ static void test_subclass(void) ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); ok(ret == TRUE, "Expected TRUE\n"); orig_proc_3 = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)wnd_proc_3); + check_unicode(hwnd, EXPECT_WNDPROC_3); + SetWindowLongA(hwnd, GWLP_USERDATA, EXPECT_WNDPROC_3); + SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_OldAfterNewTest, "Old after New");
ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); ok(ret == TRUE, "Expected TRUE\n"); + check_unicode(hwnd, EXPECT_WNDPROC_3); + SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_MixTest, "Mix");
+ check_unicode(hwnd, EXPECT_WNDPROC_3); + /* Now the fun starts */ ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST); ok(ret == TRUE, "Expected TRUE\n"); @@ -275,6 +343,8 @@ static void test_subclass(void) pRemoveWindowSubclass(hwnd, wnd_proc_sub, 2); pRemoveWindowSubclass(hwnd, wnd_proc_sub, 5);
+ check_unicode(hwnd, EXPECT_WNDPROC_3); + DestroyWindow(hwnd); }
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43073 --- dlls/comctl32/comctl32.h | 1 + dlls/comctl32/commctrl.c | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h index 7dfdf089eb1..faff3f30717 100644 --- a/dlls/comctl32/comctl32.h +++ b/dlls/comctl32/comctl32.h @@ -209,6 +209,7 @@ typedef struct SUBCLASSPROCS *SubclassProcs; SUBCLASSPROCS *stackpos; WNDPROC origproc; + int is_unicode; int running; } SUBCLASS_INFO, *LPSUBCLASS_INFO;
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c index c5910b40869..37cb66b9bbb 100644 --- a/dlls/comctl32/commctrl.c +++ b/dlls/comctl32/commctrl.c @@ -1103,7 +1103,8 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass, SetPropW (hWnd, COMCTL32_wSubclass, stack);
/* set window procedure to our own and save the current one */ - if (IsWindowUnicode (hWnd)) + stack->is_unicode = IsWindowUnicode (hWnd); + if (stack->is_unicode) stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)COMCTL32_SubclassProc); else @@ -1127,7 +1128,7 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass, proc = Alloc(sizeof(SUBCLASSPROCS)); if (!proc) { ERR ("Failed to allocate subclass entry in stack\n"); - if (IsWindowUnicode (hWnd)) + if (stack->is_unicode) SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); else SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); @@ -1246,7 +1247,7 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u if (!stack->SubclassProcs && !stack->running) { TRACE("Last Subclass removed, cleaning up\n"); /* clean up our heap and reset the original window procedure */ - if (IsWindowUnicode (hWnd)) + if (stack->is_unicode) SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); else SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); @@ -1288,7 +1289,7 @@ static LRESULT WINAPI COMCTL32_SubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam if (!stack->SubclassProcs && !stack->running) { TRACE("Last Subclass removed, cleaning up\n"); /* clean up our heap and reset the original window procedure */ - if (IsWindowUnicode (hWnd)) + if (stack->is_unicode) SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); else SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); @@ -1331,7 +1332,7 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar /* If we are at the end of stack then we have to call the original * window procedure */ if (!stack->stackpos) { - if (IsWindowUnicode (hWnd)) + if (stack->is_unicode) ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam); else ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam);
From: Rémi Bernon rbernon@codeweavers.com
Final Fantasy XIV Online depends on this for its text input.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43073 --- dlls/comctl32/commctrl.c | 13 +++---------- dlls/comctl32/tests/subclass.c | 15 +++++++-------- 2 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c index 37cb66b9bbb..eae64022d65 100644 --- a/dlls/comctl32/commctrl.c +++ b/dlls/comctl32/commctrl.c @@ -1104,12 +1104,8 @@ BOOL WINAPI SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass,
/* set window procedure to our own and save the current one */ stack->is_unicode = IsWindowUnicode (hWnd); - if (stack->is_unicode) - stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC, - (DWORD_PTR)COMCTL32_SubclassProc); - else - stack->origproc = (WNDPROC)SetWindowLongPtrA (hWnd, GWLP_WNDPROC, - (DWORD_PTR)COMCTL32_SubclassProc); + stack->origproc = (WNDPROC)SetWindowLongPtrW (hWnd, GWLP_WNDPROC, + (DWORD_PTR)COMCTL32_SubclassProc); } else { /* Check to see if we have called this function with the same uIDSubClass @@ -1332,10 +1328,7 @@ LRESULT WINAPI DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar /* If we are at the end of stack then we have to call the original * window procedure */ if (!stack->stackpos) { - if (stack->is_unicode) - ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam); - else - ret = CallWindowProcA (stack->origproc, hWnd, uMsg, wParam, lParam); + ret = CallWindowProcW (stack->origproc, hWnd, uMsg, wParam, lParam); } else { const SUBCLASSPROCS *proc = stack->stackpos; stack->stackpos = stack->stackpos->next; diff --git a/dlls/comctl32/tests/subclass.c b/dlls/comctl32/tests/subclass.c index 684593e6ed7..3c07a9d221c 100644 --- a/dlls/comctl32/tests/subclass.c +++ b/dlls/comctl32/tests/subclass.c @@ -202,7 +202,7 @@ static LRESULT WINAPI wnd_proc_1(HWND hwnd, UINT message, WPARAM wParam, LPARAM DWORD flags = GetWindowLongA(hwnd, GWLP_USERDATA); struct message msg;
- todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + check_unicode(hwnd, flags);
if(message == WM_USER) { msg.wParam = wParam; @@ -234,7 +234,7 @@ static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARA DWORD flags = GetWindowLongA(hwnd, GWLP_USERDATA); struct message msg;
- todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + check_unicode(hwnd, flags);
if(message == WM_USER) { msg.wParam = wParam; @@ -245,22 +245,21 @@ static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARA if(dwRefData & DELETE_SELF) { pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass); pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass); - todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + check_unicode(hwnd, flags); } if(dwRefData & DELETE_PREV) { pRemoveWindowSubclass(hwnd, wnd_proc_sub, uldSubclass-1); - todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + check_unicode(hwnd, flags); } if(dwRefData & SEND_NEST) { SendMessageA(hwnd, WM_USER, wParam+1, 0); - todo_wine_if(flags & EXPECT_UNICODE) check_unicode(hwnd, flags); + check_unicode(hwnd, flags); } } } if (message == WM_CHAR) { - todo_wine ok(wParam == 0x30c2, "got wParam %#Ix\n", wParam); } return pDefSubclassProc(hwnd, message, wParam, lParam); @@ -277,7 +276,7 @@ static void test_subclass(void)
ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); ok(ret == TRUE, "Expected TRUE\n"); - todo_wine check_unicode(hwnd, EXPECT_UNICODE); + check_unicode(hwnd, EXPECT_UNICODE); SetWindowLongA(hwnd, GWLP_USERDATA, EXPECT_UNICODE);
SendMessageA(hwnd, WM_USER, 1, 0); @@ -287,7 +286,7 @@ static void test_subclass(void)
ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); ok(ret == TRUE, "Expected TRUE\n"); - todo_wine check_unicode(hwnd, EXPECT_UNICODE); + check_unicode(hwnd, EXPECT_UNICODE);
SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_DeletedTest, "Deleted");
From: Rémi Bernon rbernon@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=43073 --- dlls/comctl32/commctrl.c | 4 +++- dlls/comctl32/tests/subclass.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c index eae64022d65..4c5900bc37f 100644 --- a/dlls/comctl32/commctrl.c +++ b/dlls/comctl32/commctrl.c @@ -1243,7 +1243,9 @@ BOOL WINAPI RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR u if (!stack->SubclassProcs && !stack->running) { TRACE("Last Subclass removed, cleaning up\n"); /* clean up our heap and reset the original window procedure */ - if (stack->is_unicode) + if ((WNDPROC)GetWindowLongPtrW (hWnd, GWLP_WNDPROC) != COMCTL32_SubclassProc) + WARN("Window procedure has been modified, skipping restore\n"); + else if (stack->is_unicode) SetWindowLongPtrW (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); else SetWindowLongPtrA (hWnd, GWLP_WNDPROC, (DWORD_PTR)stack->origproc); diff --git a/dlls/comctl32/tests/subclass.c b/dlls/comctl32/tests/subclass.c index 3c07a9d221c..bc3fc6f1d91 100644 --- a/dlls/comctl32/tests/subclass.c +++ b/dlls/comctl32/tests/subclass.c @@ -192,7 +192,7 @@ static void check_unicode_(int line, HWND hwnd, DWORD flags) else if (flags & EXPECT_WNDPROC_1) ok_(__FILE__, line)(proc == wnd_proc_1, "got proc %p\n", proc); else if (flags & EXPECT_WNDPROC_3) - todo_wine_if(proc == wnd_proc_1) ok_(__FILE__, line)(proc == wnd_proc_3, "got proc %p\n", proc); + ok_(__FILE__, line)(proc == wnd_proc_3, "got proc %p\n", proc); else ok_(__FILE__, line)(!IS_WNDPROC_HANDLE(proc), "got proc %p\n", proc); }
v3: Actually it just works OOTB if CallWindowProcW is used unconditionally.
On Thu Jan 26 15:23:20 2023 +0000, **** wrote:
Doesn't seem to happen anymore with the latest revision.
This merge request was approved by Zhiyi Zhang.
This merge request was approved by Nikolay Sivov.