[PATCH 1/2] comctl32/taskdialog: Initial support for callback procedure

Nikolay Sivov nsivov at codeweavers.com
Tue Aug 29 16:45:00 CDT 2017


From: Fabian Maurer <dark.shadow4 at web.de>

Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---

Simplified tests further, minor formatting changes.

 dlls/comctl32/taskdialog.c       |  55 ++++++++++++++++++--
 dlls/comctl32/tests/taskdialog.c | 106 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 158 insertions(+), 3 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index a14a2230a9..2558174fa3 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -80,6 +80,13 @@ struct taskdialog_button_desc
     HINSTANCE hinst;
 };
 
+struct taskdialog_info
+{
+    HWND hwnd;
+    PFTASKDIALOGCALLBACK callback;
+    LONG_PTR callback_data;
+};
+
 static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
 {
     if (width)
@@ -482,20 +489,54 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi
     return template;
 }
 
+static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+{
+    return dialog_info->callback ? dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam,
+            dialog_info->callback_data) : S_OK;
+}
+
+static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, WORD command_id)
+{
+    if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
+        EndDialog(dialog_info->hwnd, command_id);
+}
+
 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
+    static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
+    struct taskdialog_info *dialog_info;
+
     TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
 
+    if (msg != WM_INITDIALOG)
+        dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
+
     switch (msg)
     {
+        case TDM_CLICK_BUTTON:
+            taskdialog_on_button_click(dialog_info, LOWORD(wParam));
+            break;
+        case WM_INITDIALOG:
+            dialog_info = (struct taskdialog_info *)lParam;
+            dialog_info->hwnd = hwnd;
+            SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
+
+            taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
+            break;
+        case WM_SHOWWINDOW:
+            taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
+            break;
         case WM_COMMAND:
             if (HIWORD(wParam) == BN_CLICKED)
             {
-                WORD command_id = LOWORD(wParam);
-                EndDialog(hwnd, command_id);
+                taskdialog_on_button_click(dialog_info, LOWORD(wParam));
                 return TRUE;
             }
             break;
+        case WM_DESTROY:
+            taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
+            RemovePropW(hwnd, taskdialog_info_propnameW);
+            break;
     }
     return FALSE;
 }
@@ -506,13 +547,21 @@ static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
                                   int *radio_button, BOOL *verification_flag_checked)
 {
+    struct taskdialog_info dialog_info;
     DLGTEMPLATE *template;
     INT ret;
 
     TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
 
+    if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
+        return E_INVALIDARG;
+
+    dialog_info.callback = taskconfig->pfCallback;
+    dialog_info.callback_data = taskconfig->lpCallbackData;
+
     template = create_taskdialog_template(taskconfig);
-    ret = DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent, taskdialog_proc, 0);
+    ret = DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
+            taskdialog_proc, (LPARAM)&dialog_info);
     Free(template);
 
     if (button) *button = ret;
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index c937127897..dcadd30f3c 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -24,13 +24,114 @@
 #include "winuser.h"
 #include "commctrl.h"
 
+#include "wine/list.h"
 #include "wine/test.h"
 #include "v6util.h"
+#include "msg.h"
+
+#define WM_TD_CALLBACK WM_APP /* Custom dummy message to wrap callback notifications */
+
+#define NUM_MSG_SEQUENCES     1
+#define TASKDIALOG_SEQ_INDEX  0
 
 static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *);
 static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *,
         TASKDIALOG_COMMON_BUTTON_FLAGS, const WCHAR *, int *);
 
+static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
+
+static HRESULT CALLBACK taskdialog_callback_proc(HWND hwnd, UINT notification, WPARAM wParam,
+        LPARAM lParam, LONG_PTR refdata)
+{
+    struct message *ctl = (struct message *)refdata;
+    struct message msg;
+
+    ok(refdata != 0, "Unexpected refdata %lx.\n", refdata);
+
+    msg.message = WM_TD_CALLBACK;
+    msg.id = notification;
+    msg.flags = wparam|lparam|id;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+
+    add_message(sequences, TASKDIALOG_SEQ_INDEX, &msg);
+
+    if (notification == TDN_DIALOG_CONSTRUCTED || notification == TDN_DESTROYED)
+        return S_OK;
+
+    while (ctl->message)
+    {
+        PostMessageW(hwnd, ctl->message, ctl->wParam, ctl->lParam);
+        ctl++;
+    }
+
+    return S_OK;
+}
+
+#define run_test(expected_button, messages, seq, context) \
+    run_test_(expected_button, messages, seq, context, __LINE__)
+
+static void run_test_(int expected_button, const struct message *control_messages, const struct message *seq,
+        const char* context, int line)
+{
+    TASKDIALOGCONFIG info = { 0 };
+    int button = 0;
+    HRESULT hr;
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    info.cbSize = sizeof(info);
+    info.pfCallback = taskdialog_callback_proc;
+    info.lpCallbackData = (LONG_PTR)control_messages;
+    hr = pTaskDialogIndirect(&info, &button, NULL, NULL);
+    ok_(__FILE__, line)(hr == S_OK, "Unexpected return value, hr %#x.\n", hr);
+
+    ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, seq, context, FALSE, __FILE__, line);
+
+    ok_(__FILE__, line)(button == expected_button, "Unexpected button %d, expected %d.\n", button, expected_button);
+}
+
+static void test_invalid_parameters(void)
+{
+    TASKDIALOGCONFIG info = { 0 };
+    HRESULT hr;
+
+    hr = pTaskDialogIndirect(NULL, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr);
+
+    info.cbSize = 0;
+    hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr);
+
+    info.cbSize = sizeof(TASKDIALOGCONFIG) - 1;
+    hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr);
+
+    info.cbSize = sizeof(TASKDIALOGCONFIG) + 1;
+    hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+    ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr);
+}
+
+static const struct message callback_msg_seq[] =
+{
+    { TDM_CLICK_BUTTON, 0, IDOK },
+    { 0 }
+};
+
+static const struct message callback_seq[] =
+{
+    { WM_TD_CALLBACK, id|wparam|lparam, 0, 0, TDN_DIALOG_CONSTRUCTED },
+    { WM_TD_CALLBACK, id|wparam|lparam, 0, 0, TDN_CREATED },
+    { WM_TD_CALLBACK, id|wparam|lparam, IDOK, 0, TDN_BUTTON_CLICKED },
+    { WM_TD_CALLBACK, id|wparam|lparam, 0, 0, TDN_DESTROYED },
+    { 0 }
+};
+
+static void test_callback(void)
+{
+    run_test(IDOK, callback_msg_seq, callback_seq, "Callback test 1");
+}
+
 START_TEST(taskdialog)
 {
     ULONG_PTR ctx_cookie;
@@ -62,5 +163,10 @@ START_TEST(taskdialog)
     ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n",
                                             ptr_ordinal, pTaskDialogIndirect);
 
+    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    test_invalid_parameters();
+    test_callback();
+
     unload_v6_module(ctx_cookie, hCtx);
 }
-- 
2.14.1




More information about the wine-patches mailing list