Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=17205
-- v4: user32/msgbox: Use a windows hook to trap Ctrl+C user32/msgbox: Support WM_COPY Message
From: Alistair Leslie-Hughes leslie_alistair@hotmail.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=17205
Signed-off-by: Alistair Leslie-Hughes leslie_alistair@hotmail.com --- dlls/user32/msgbox.c | 87 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 5 deletions(-)
diff --git a/dlls/user32/msgbox.c b/dlls/user32/msgbox.c index 4d345777a10..2a1d81d1462 100644 --- a/dlls/user32/msgbox.c +++ b/dlls/user32/msgbox.c @@ -41,6 +41,11 @@ struct ThreadWindows HWND *handles; };
+/* Index the order the buttons need to appear to an ID* constant */ +static const int buttonOrder[10] = { IDYES, IDNO, IDOK, IDABORT, IDRETRY, + IDCANCEL, IDIGNORE, IDTRYAGAIN, + IDCONTINUE, IDHELP }; + static BOOL CALLBACK MSGBOX_EnumProc(HWND hwnd, LPARAM lParam) { struct ThreadWindows *threadWindows = (struct ThreadWindows *)lParam; @@ -74,11 +79,6 @@ static void MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb) WCHAR *buffer = NULL; const WCHAR *ptr;
- /* Index the order the buttons need to appear to an ID* constant */ - static const int buttonOrder[10] = { IDYES, IDNO, IDOK, IDABORT, IDRETRY, - IDCANCEL, IDIGNORE, IDTRYAGAIN, - IDCONTINUE, IDHELP }; - nclm.cbSize = sizeof(nclm); SystemParametersInfoW (SPI_GETNONCLIENTMETRICS, 0, &nclm, 0);
@@ -320,6 +320,79 @@ static void MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb) HeapFree( GetProcessHeap(), 0, buffer ); }
+static void MSGBOX_CopyToClipbaord( HWND hwnd ) +{ + int i; + static const WCHAR line[] = L"---------------------------\r\n"; + static const WCHAR carriage[] = L"\r\n"; + static const WCHAR spaces[] = L" "; + int lenTitle = GetWindowTextLengthW(hwnd) + 1; + int lenMsg = GetWindowTextLengthW(GetDlgItem(hwnd, MSGBOX_IDTEXT)) + 1; + HGLOBAL hMem; + WCHAR *data; + + + /* + --------------------------- + Dialog Title + --------------------------- + Dialog Message + --------------------------- + Button(s) Text. OK + --------------------------- + */ + int len = ((wcslen(carriage) * 3) + (wcslen(line) * 4) + lenTitle + lenMsg) * sizeof(WCHAR); + WCHAR *text = malloc(len); + if (!text) + return; + + lstrcpyW(text, line); + if (!GetWindowTextW(hwnd, text + lstrlenW(text), lenTitle)) + { + free(text); + return; + } + + lstrcatW(text, carriage); + lstrcatW(text, line); + GetWindowTextW(GetDlgItem(hwnd, MSGBOX_IDTEXT), text + lstrlenW(text), lenMsg); + lstrcatW(text, carriage); + lstrcatW(text, line); + + for (i = 0; i < ARRAY_SIZE(buttonOrder); i++) + { + HWND hItem = GetDlgItem(hwnd, buttonOrder[i]); + if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE) + { + WCHAR buffer[1024] = {0}; + int j = 0, k = lstrlenW(text); + GetWindowTextW(hItem, buffer, 1024); + while(buffer[j] != 0) + { + if(buffer[j] != '&') + text[k++] = buffer[j]; + j++; + } + text[k] = 0; + lstrcatW(text, spaces); + } + } + + lstrcatW(text, carriage); + lstrcatW(text, line); + + hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, len); + data = GlobalLock(hMem); + lstrcpyW(data, text); + GlobalUnlock(hMem); + + OpenClipboard(hwnd); + NtUserEmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, hMem); + NtUserCloseClipboard(); + + free(text); +}
/************************************************************************** * MSGBOX_DlgProc @@ -339,6 +412,10 @@ static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message, break; }
+ case WM_COPY: + MSGBOX_CopyToClipbaord(hwnd); + break; + case WM_COMMAND: switch (LOWORD(wParam)) {
From: Alistair Leslie-Hughes leslie_alistair@hotmail.com
--- dlls/user32/msgbox.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+)
diff --git a/dlls/user32/msgbox.c b/dlls/user32/msgbox.c index 2a1d81d1462..cae7b730f75 100644 --- a/dlls/user32/msgbox.c +++ b/dlls/user32/msgbox.c @@ -394,6 +394,22 @@ static void MSGBOX_CopyToClipbaord( HWND hwnd ) free(text); }
+HHOOK msghook_handle; + +LRESULT CALLBACK msg_hook_proc(int nCode, WPARAM wParam, LPARAM lParam) +{ + MSG *msg = (MSG *)lParam; + if (nCode == MSGF_DIALOGBOX && msg->message == WM_KEYUP) + { + if ( (msg->wParam == 'C' || msg->wParam == 'c') && (NtUserGetKeyState(VK_CONTROL) & 0x8000)) + { + MSGBOX_CopyToClipbaord(GetParent(msg->hwnd)); + } + } + + return NtUserCallNextHookEx(msghook_handle, nCode, wParam, lParam); +} + /************************************************************************** * MSGBOX_DlgProc * @@ -409,6 +425,7 @@ static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message, SetWindowContextHelpId(hwnd, mbp->dwContextHelpId); MSGBOX_OnInit(hwnd, mbp); SetPropA(hwnd, "WINE_MSGBOX_HELPCALLBACK", mbp->lpfnMsgBoxCallback); + msghook_handle = SetWindowsHookExA(WH_MSGFILTER, msg_hook_proc, NULL, GetCurrentThreadId()); break; }
@@ -416,6 +433,10 @@ static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message, MSGBOX_CopyToClipbaord(hwnd); break;
+ case WM_DESTROY: + NtUserUnhookWindowsHookEx(msghook_handle); + break; + case WM_COMMAND: switch (LOWORD(wParam)) {
On Sat Mar 16 18:33:22 2024 +0000, Zebediah Figura wrote:
That doesn't work; there are multiple orderings of key presses and releases that should yield Ctrl-C, and this won't catch the case where the Ctrl key is released before C. You probably want WM_CHAR.
I couldn't get this to work with WM_CHAR. For some reason, it returned VK_CANCEL, so not exactly what I was expecting. I'm not really interest if we missing ctrl being released before the C.
On Sat Mar 16 18:33:23 2024 +0000, Zebediah Figura wrote:
A message hook does not at all look like the right way to handle this. Can we not get the right messages from the dialog procedure? And if not, even subclassing the dialog wndproc seems better.
I attempted to use a subclass however it just end up drawing badly and the dialog didn't get the key press message. Not sure, what I missed though.
Do dialogs box actually get a key press message?
I couldn't get this to work with WM_CHAR. For some reason, it returned VK_CANCEL, so not exactly what I was expecting.
Well, WM_CHAR returns Unicode characters, not vkeys. I'm not sure if there's a problem with interpreting \x03 here, but either way, WM_KEYDOWN is probably more correct.
Note also that vkeys are not case sensitive, and 'c' is VK_NUMPAD3.
I'm not really interest if we missing ctrl being released before the C.
I'm... not sure why not? That's a common sequence of events when pressing Ctrl+C, and it works correctly on Windows.
On Sun Mar 24 03:09:06 2024 +0000, Alistair Leslie-Hughes wrote:
I attempted to use a subclass however it just end up drawing badly and the dialog didn't get the key press message. Not sure, what I missed though. Do dialogs box actually get a key press message?
I'm not sure why subclassing broke things, but I suppose it's not enough by itself, since the problem (which I didn't realize) is that keyboard messages are going to the individual controls, not the parent dialog window.
Still, a message hook seems like an ugly solution. One possibly better one is to use a modeless dialog and a custom message loop, and catch WM_KEYDOWN messages in the message loop.
I don't think these tests make sense. They're very awkward, both because of the need to poll until the window is visible, and because of the attempt to compare translated text (which I don't think we do anywhere else). They're also not very valuable, since this feature is just for debugging anyway.
I think this is a case where an out-of-tree test (or really just manual verification) is better.