[PATCH v2] comctl32/taskdialog: Add callback and tests
Fabian Maurer
dark.shadow4 at web.de
Mon Aug 28 17:27:41 CDT 2017
v2: Implemented suggestions by Nikolay Sivov and simplify patch
Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
dlls/comctl32/taskdialog.c | 58 +++++++++++++-
dlls/comctl32/tests/taskdialog.c | 161 +++++++++++++++++++++++++++++++++++++++
2 files changed, 216 insertions(+), 3 deletions(-)
diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index a14a2230a9..a4922b2f2e 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,58 @@ static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfi
return template;
}
+static HRESULT callback(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+{
+ if (dialog_info->callback)
+ return dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam, dialog_info->callback_data);
+ return S_OK;
+}
+
+static void click_button(struct taskdialog_info *dialog_info, WORD command_id)
+{
+ HRESULT ret_callback;
+
+ ret_callback = callback(dialog_info, TDN_BUTTON_CLICKED, command_id, 0);
+ if (ret_callback == 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 WM_INITDIALOG:
+ dialog_info = (struct taskdialog_info *)lParam;
+ dialog_info->hwnd = hwnd;
+ SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
+
+ callback(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
+ break;
+ case WM_SHOWWINDOW:
+ callback(dialog_info, TDN_CREATED, 0, 0);
+ break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
WORD command_id = LOWORD(wParam);
- EndDialog(hwnd, command_id);
+ click_button(dialog_info, command_id);
return TRUE;
}
break;
+ case WM_DESTROY:
+ callback(dialog_info, TDN_DESTROYED, 0, 0);
+ RemovePropW(hwnd, taskdialog_info_propnameW);
+ break;
}
return FALSE;
}
@@ -507,17 +552,24 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
int *radio_button, BOOL *verification_flag_checked)
{
DLGTEMPLATE *template;
+ struct taskdialog_info dialog_info;
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;
if (radio_button) *radio_button = taskconfig->nDefaultButton;
- if (verification_flag_checked) *verification_flag_checked = TRUE;
+ if (verification_flag_checked) *verification_flag_checked = FALSE;
return S_OK;
}
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index c937127897..03db766759 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -24,13 +24,170 @@
#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];
+
+/* Message lists to test against */
+
+struct message_send_info
+{
+ UINT send_message;
+ WPARAM send_wparam;
+ LPARAM send_lparam;
+};
+
+struct message_info
+{
+ UINT recv_message; /* Message the callback receives */
+ WPARAM recv_wparam;
+ LPARAM recv_lparam;
+
+ HRESULT ret; /* Value the callback should return */
+
+ struct message_send_info send[9]; /* Message to send to trigger the next callback message */
+};
+
+static const struct message_info *current_message_info;
+
+static const struct message_info mes_return_press_ok[] = {
+ { TDN_CREATED, 0, 0, S_OK, {
+ { WM_KEYDOWN, VK_RETURN, 0 },
+ { 0 }}},
+ { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, {{ 0 }}},
+ { 0 }
+};
+
+/* Create a message to test against */
+static struct message create_test_message(UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct message mes;
+
+ mes.message = WM_TD_CALLBACK;
+ mes.id = message;
+ mes.flags = sent|wparam|lparam|id;
+ mes.wParam = wParam;
+ mes.lParam = lParam;
+
+ return mes;
+}
+
+#define run_test(info, expect_button, expect_radio, expect_checkbox, seq, context) \
+ run_test_(info, expect_button, expect_radio, expect_checkbox, seq, context, \
+ sizeof(seq)/sizeof(seq[0]) - 1 , __FILE__, __LINE__)
+
+void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio, BOOL expect_checkbox,
+ const struct message_info *test_messages, const char* context,
+ int test_messages_len, const char *file, int line)
+{
+ HRESULT ret;
+ int ret_button = 0;
+ int ret_radio = 0;
+ BOOL ret_checkbox = 0;
+
+ struct message *mes, *mes_start;
+ int i;
+
+ /* Allocate messages to test against, plus 2 implicit and 1 empty */
+ mes_start = mes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct message) * (test_messages_len + 3));
+
+ *mes++ = create_test_message(TDN_DIALOG_CONSTRUCTED, 0, 0); /* Always needed, thus made implicit */
+ for(i = 0; i < test_messages_len; i++)
+ {
+ *mes++ = create_test_message(test_messages[i].recv_message,
+ test_messages[i].recv_wparam, test_messages[i].recv_lparam);
+ }
+ *mes++ = create_test_message(TDN_DESTROYED, 0, 0); /* Always needed, thus made implicit */
+
+ current_message_info = test_messages;
+ flush_sequences(sequences, NUM_MSG_SEQUENCES);
+ ret = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_checkbox);
+
+ ok_( file, line)(ret == S_OK, "Expected S_OK, got %x\n", ret);
+ ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, mes_start, context, FALSE, file, line);
+ ok_( file, line)(ret_button == expect_button,
+ "Wrong button. Expected %d, got %d\n", expect_button, ret_button);
+ ok_( file, line)(ret_radio == expect_radio,
+ "Wrong radio button. Expected %d, got %d\n", expect_radio, ret_radio);
+ ok_( file, line)(ret_checkbox == expect_checkbox,
+ "Wrong checkbox state. Expected %d, got %d\n", expect_checkbox, ret_checkbox);
+
+ HeapFree(GetProcessHeap(), 0, mes_start);
+}
+
+static LONG_PTR backup_ref_data; /* Copy of dwRefData to test against */
+
+static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd, UINT uNotification, WPARAM wParam,
+ LPARAM lParam, LONG_PTR dwRefData)
+{
+ struct message msg;
+
+ ok(backup_ref_data == dwRefData, "dwRefData is wrong, expected %lu, got %lu\n", backup_ref_data, dwRefData);
+
+ msg = create_test_message(uNotification, wParam, lParam);
+ add_message(sequences, TASKDIALOG_SEQ_INDEX, &msg);
+
+ if(uNotification != TDN_DIALOG_CONSTRUCTED && uNotification != TDN_DESTROYED) /* Skip implicit messages */
+ {
+ int mes_pos = sequences[TASKDIALOG_SEQ_INDEX]->count - 2; /* Skip implicit message and the current one */
+ const struct message_send_info *msg_send = current_message_info[mes_pos].send;
+ while(msg_send->send_message)
+ {
+ PostMessageW(hwnd, msg_send->send_message, msg_send->send_wparam, msg_send->send_lparam);
+
+ msg_send++;
+ }
+
+ return current_message_info[mes_pos].ret;
+ }
+
+ return S_OK;
+}
+
+static void test_invalid_parameters(void)
+{
+ TASKDIALOGCONFIG info = {0};
+ HRESULT ret;
+
+ ret = pTaskDialogIndirect(NULL, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+ info.cbSize = 0;
+ ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+ info.cbSize = sizeof(TASKDIALOGCONFIG) - 1;
+ ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+ info.cbSize = sizeof(TASKDIALOGCONFIG) + 1;
+ ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+ ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+}
+
+static void test_callback(void)
+{
+ TASKDIALOGCONFIG info = {0};
+
+ info.cbSize = sizeof(TASKDIALOGCONFIG);
+ info.pfCallback = TaskDialogCallbackProc;
+ info.lpCallbackData = backup_ref_data = 0x12345678; /* Set data for callback tests */
+
+ run_test(&info, IDOK, 0, FALSE, mes_return_press_ok, "Simple callback test just sending return");
+}
+
START_TEST(taskdialog)
{
ULONG_PTR ctx_cookie;
@@ -62,5 +219,9 @@ 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