[v2 PATCH] user32/msgbox: Support WM_COPY Message

Alistair Leslie-Hughes leslie_alistair at hotmail.com
Tue Nov 8 04:44:06 CST 2016


Fixes: https://bugs.winehq.org/show_bug.cgi?id=17205

Signed-off-by: Alistair Leslie-Hughes <leslie_alistair at hotmail.com>
---
 dlls/user32/msgbox.c       |  86 +++++++++++++++++++--
 dlls/user32/tests/dialog.c | 187 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 267 insertions(+), 6 deletions(-)

diff --git a/dlls/user32/msgbox.c b/dlls/user32/msgbox.c
index 2ba98c9..4735b8a 100644
--- a/dlls/user32/msgbox.c
+++ b/dlls/user32/msgbox.c
@@ -44,6 +44,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;
@@ -77,11 +82,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);
 
@@ -319,6 +319,77 @@ static void MSGBOX_OnInit(HWND hwnd, LPMSGBOXPARAMSW lpmb)
     HeapFree( GetProcessHeap(), 0, buffer );
 }
 
+static void MSGBOX_CopyToClipboard( HWND hwnd )
+{
+    int i;
+    static const WCHAR line[] = {'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-',
+                           '-','-','-','-','-','-','-','-','\r','\n', 0};
+    static const WCHAR crlf[] = {'\r','\n', 0};
+    static const WCHAR spaces[] = {' ',' ',' ', 0};
+    int lenTitle = GetWindowTextLengthW(hwnd) + 1;
+    int lenMsg = GetWindowTextLengthW(GetDlgItem(hwnd, MSGBOX_IDTEXT)) + 1;
+
+    /*
+    ---------------------------
+    Dialog Title
+    ---------------------------
+    Dialog Message
+    ---------------------------
+    Button(s) Text. OK
+    ---------------------------
+    */
+    int len = ((sizeof(crlf) * 3) + (sizeof(line) * 4) + lenTitle + lenMsg) * sizeof(WCHAR);
+    WCHAR *text = HeapAlloc( GetProcessHeap(), 0,  len);
+    if(text)
+    {
+        lstrcpyW(text, line);
+        if (GetWindowTextW(hwnd, text + lstrlenW(text), lenTitle))
+        {
+            HGLOBAL hMem;
+            WCHAR *data;
+
+            lstrcatW(text, crlf);
+            lstrcatW(text, line);
+            GetWindowTextW(GetDlgItem(hwnd, MSGBOX_IDTEXT), text + lstrlenW(text), lenMsg);
+            lstrcatW(text, crlf);
+            lstrcatW(text, line);
+
+            for (i = 0; i < (sizeof(buttonOrder) / sizeof(buttonOrder[0])); 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, crlf);
+            lstrcatW(text, line);
+
+            hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE|GMEM_ZEROINIT, (len + 1) * sizeof(WCHAR));
+            data = GlobalLock(hMem);
+            lstrcpyW(data, text);
+            GlobalUnlock(hMem);
+
+            OpenClipboard(hwnd);
+            EmptyClipboard();
+            SetClipboardData(CF_UNICODETEXT, hMem);
+            CloseClipboard();
+        }
+
+        HeapFree(GetProcessHeap(), 0, text);
+    }
+}
 
 /**************************************************************************
  *           MSGBOX_DlgProc
@@ -337,6 +408,11 @@ static INT_PTR CALLBACK MSGBOX_DlgProc( HWND hwnd, UINT message,
        SetPropA(hwnd, "WINE_MSGBOX_HELPCALLBACK", mbp->lpfnMsgBoxCallback);
        break;
    }
+   case WM_COPY:
+   {
+        MSGBOX_CopyToClipboard(hwnd);
+        break;
+   }
 
    case WM_COMMAND:
     switch (LOWORD(wParam))
diff --git a/dlls/user32/tests/dialog.c b/dlls/user32/tests/dialog.c
index a37c678..0bb7dca 100644
--- a/dlls/user32/tests/dialog.c
+++ b/dlls/user32/tests/dialog.c
@@ -40,6 +40,7 @@
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winnls.h"
 
 #define MAXHWNDS 1024
 static HWND hwnd [MAXHWNDS];
@@ -1392,6 +1393,187 @@ static void test_MessageBoxFontTest(void)
     DestroyWindow(hDlg);
 }
 
+static const char msgbox_title[] = "%5!z9ZXw*ia;57n/FGl.bCH,Su\"mfKN;foCqAU\'j6AmoJgAc_D:Z0A\'E6PF_O/w";
+static WCHAR expectedOK[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'O','K',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedOkCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'O','K',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedAbortRetryIgnore[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'A','b','o','r','t',' ',' ',' ','R','e','t','r','y',' ',' ',' ','I','g','n','o','r','e',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+
+static WCHAR expectedYesNo[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'Y','e','s',' ',' ',' ','N','o',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedYesNoCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'Y','e','s',' ',' ',' ','N','o',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedRetryCancel[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'R','e','t','r','y',' ',' ',' ','C','a','n','c','e','l',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+static WCHAR expectedCancelTryContinue[] =
+{
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'%','5','!','z','9','Z','X','w','*','i','a',';','5','7','n','/','F','G','l','.','b','C','H',',','S','u','"','m','f',
+'K','N',';','f','o','C','q','A','U','\'','j','6','A','m','o','J','g','A','c','_','D',':','Z','0','A','\'','E','6','P',
+'F','_','O','/','w','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'M','e','s','s','a','g','e','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n',
+'C','a','n','c','e','l',' ',' ',' ','T','r','y',' ','A','g','a','i','n',' ',' ',' ','C','o','n','t','i','n','u','e',' ',' ',' ','\r','\n',
+'-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','-','\r','\n', 0
+};
+
+BOOL non_english = FALSE;
+
+DWORD WINAPI WorkerThread(void *param)
+{
+    WCHAR *expected = param;
+    char windowTitle[sizeof(msgbox_title)];
+    HWND hwndMbox;
+    BOOL succeeded = FALSE;
+
+    Sleep(200);
+
+    hwndMbox = GetForegroundWindow();
+
+    /* Find the Window, if it doesn't have focus */
+    if (!(IsWindow(hwndMbox) &&
+        GetWindowTextA(hwndMbox, windowTitle, sizeof(msgbox_title)) &&
+        lstrcmpA(msgbox_title, windowTitle) == 0))
+    {
+        hwndMbox = FindWindowA(NULL, msgbox_title);
+
+        if (!IsWindow(hwndMbox))
+            goto cleanup;
+    }
+
+    SendMessageA(hwndMbox, WM_COPY, 0, 0);
+
+    if (IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL))
+    {
+        HANDLE textHandle = GetClipboardData(CF_UNICODETEXT);
+        WCHAR *text = GlobalLock(textHandle);
+
+        if (text != NULL)
+        {
+            if(non_english)
+                ok(lstrlenW(text) > 0, "Empty string on clipboard\n");
+            else
+            {
+                succeeded = lstrcmpW(expected, text) == 0;
+                if(!succeeded)
+                {
+                    ok(0, "%s\n", wine_dbgstr_w(text));
+                    ok(0, "%s\n", wine_dbgstr_w(expected));
+                }
+            }
+
+            GlobalUnlock(textHandle);
+        }
+        else
+            ok(0, "No text on clipboard.\n");
+
+        CloseClipboard();
+
+    }
+    else
+        trace("Clipboard error\n");
+
+    PostMessageA(hwndMbox, WM_COMMAND, IDIGNORE, 0); /* For MB_ABORTRETRYIGNORE dialog. */
+    PostMessageA(hwndMbox, WM_CLOSE, 0, 0);
+
+cleanup:
+    ok(succeeded || non_english, "Failed to get string.\n");
+
+    return 0;
+}
+
+static void test_MessageBox_WM_COPY_Test(void)
+{
+    DWORD tid = 0;
+
+    non_english = (PRIMARYLANGID(GetUserDefaultLangID()) != LANG_ENGLISH);
+    trace("non_english %d\n", non_english);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedOK, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_OK);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedOkCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_OKCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedAbortRetryIgnore, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_ABORTRETRYIGNORE);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedYesNo, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_YESNO);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedYesNoCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_YESNOCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedRetryCancel, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_RETRYCANCEL);
+
+    CreateThread(NULL, 0, WorkerThread, &expectedCancelTryContinue, 0, &tid);
+    MessageBoxA(NULL, "Message", msgbox_title, MB_CANCELTRYCONTINUE);
+}
+
 static void test_SaveRestoreFocus(void)
 {
     HWND hDlg;
@@ -1534,9 +1716,12 @@ START_TEST(dialog)
     test_focus();
     test_GetDlgItem();
     test_GetDlgItemText();
-    test_DialogBoxParamA();
     test_DisabledDialogTest();
     test_MessageBoxFontTest();
+    test_MessageBox_WM_COPY_Test();
     test_SaveRestoreFocus();
     test_timer_message();
+
+    /* This test can make some of the above tests fail. */
+    test_DialogBoxParamA();
 }
-- 
2.10.2




More information about the wine-patches mailing list