comctl32/tests: Use CRT allocation functions.
[wine.git] / dlls / comctl32 / tests / taskdialog.c
blobef60a5953bdf3ce03f5f622c9c87c059d27aeea5
1 /* Unit tests for the task dialog control.
3 * Copyright 2017 Fabian Maurer for the Wine project
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "commctrl.h"
27 #include "wine/test.h"
28 #include "v6util.h"
29 #include "msg.h"
31 #define WM_TD_CALLBACK (WM_APP) /* Custom dummy message to wrap callback notifications */
33 #define NUM_MSG_SEQUENCES 1
34 #define TASKDIALOG_SEQ_INDEX 0
36 #define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */
37 #define TEST_NUM_RADIO_BUTTONS 3
39 #define ID_START 20 /* Lower IDs might be used by the system */
40 #define ID_START_BUTTON (ID_START + 0)
41 #define ID_START_RADIO_BUTTON (ID_START + 20)
43 static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *);
44 static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *,
45 TASKDIALOG_COMMON_BUTTON_FLAGS, const WCHAR *, int *);
47 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
49 struct message_info
51 UINT message;
52 WPARAM wparam;
53 LPARAM lparam;
55 HRESULT callback_retval;
56 const struct message_info *send; /* Message to send to trigger the next callback message */
59 static const struct message_info *current_message_info;
61 /* Messages to send */
62 static const struct message_info msg_send_return[] =
64 { WM_KEYDOWN, VK_RETURN, 0 },
65 { 0 }
68 /* Messages to test against */
69 static const struct message_info msg_return_press_ok[] =
71 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
72 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
73 { 0 }
76 static const struct message_info msg_return_press_yes[] =
78 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
79 { TDN_BUTTON_CLICKED, IDYES, 0, S_OK, NULL },
80 { 0 }
83 static const struct message_info msg_return_press_no[] =
85 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
86 { TDN_BUTTON_CLICKED, IDNO, 0, S_OK, NULL },
87 { 0 }
90 static const struct message_info msg_return_press_cancel[] =
92 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
93 { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL },
94 { 0 }
97 static const struct message_info msg_return_press_retry[] =
99 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
100 { TDN_BUTTON_CLICKED, IDRETRY, 0, S_OK, NULL },
101 { 0 }
104 static const struct message_info msg_return_press_custom1[] =
106 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
107 { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL },
108 { 0 }
111 static const struct message_info msg_return_press_custom4[] =
113 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
114 { TDN_BUTTON_CLICKED, ID_START_BUTTON + 3, 0, S_OK, NULL },
115 { 0 }
118 static const struct message_info msg_return_press_custom10[] =
120 { TDN_CREATED, 0, 0, S_OK, msg_send_return },
121 { TDN_BUTTON_CLICKED, -1, 0, S_OK, NULL },
122 { 0 }
125 static const struct message_info msg_send_click_ok[] =
127 { TDM_CLICK_BUTTON, IDOK, 0 },
128 { 0 }
131 static const struct message_info msg_send_f1[] =
133 { WM_KEYF1, 0, 0, 0},
134 { 0 }
137 static const struct message_info msg_got_tdn_help[] =
139 { TDN_CREATED, 0, 0, S_OK, msg_send_f1 },
140 { TDN_HELP, 0, 0, S_OK, msg_send_click_ok },
141 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
142 { 0 }
145 /* Three radio buttons */
146 static const struct message_info msg_return_default_radio_button_1[] =
148 { TDN_CREATED, 0, 0, S_OK, NULL },
149 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok },
150 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
151 { 0 }
154 static const struct message_info msg_return_default_radio_button_2[] =
156 { TDN_CREATED, 0, 0, S_OK, NULL },
157 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_send_click_ok },
158 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
159 { 0 }
162 static const struct message_info msg_return_default_radio_button_3[] =
164 { TDN_CREATED, 0, 0, S_OK, NULL },
165 { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok },
166 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
167 { 0 }
170 static const struct message_info msg_select_first_radio_button[] =
172 { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 },
173 { 0 }
176 static const struct message_info msg_return_first_radio_button[] =
178 { TDN_CREATED, 0, 0, S_OK, NULL },
179 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_radio_button },
180 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok },
181 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
182 { 0 }
185 static const struct message_info msg_select_first_disabled_radio_button_and_press_ok[] =
187 { TDM_ENABLE_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 },
188 { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 },
189 { TDM_CLICK_BUTTON, IDOK, 0 },
190 { 0 }
193 static const struct message_info msg_return_default_radio_button_clicking_disabled[] =
195 { TDN_CREATED, 0, 0, S_OK, NULL },
196 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_disabled_radio_button_and_press_ok },
197 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL },
198 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
199 { 0 }
202 static const struct message_info msg_return_no_default_radio_button_flag[] =
204 { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
205 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL },
206 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
207 { 0 }
210 static const struct message_info msg_return_no_default_radio_button_id_and_flag[] =
212 { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
213 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
214 { 0 }
217 static const struct message_info msg_select_negative_id_radio_button[] =
219 { TDM_CLICK_RADIO_BUTTON, -2, 0 },
220 { 0 }
223 static const struct message_info msg_return_press_negative_id_radio_button[] =
225 { TDN_CREATED, 0, 0, S_OK, msg_select_negative_id_radio_button },
226 { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok },
227 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
228 { 0 }
231 static const struct message_info msg_send_all_common_button_click[] =
233 { TDM_CLICK_BUTTON, IDOK, 0 },
234 { TDM_CLICK_BUTTON, IDYES, 0 },
235 { TDM_CLICK_BUTTON, IDNO, 0 },
236 { TDM_CLICK_BUTTON, IDCANCEL, 0 },
237 { TDM_CLICK_BUTTON, IDRETRY, 0 },
238 { TDM_CLICK_BUTTON, IDCLOSE, 0 },
239 { TDM_CLICK_BUTTON, ID_START_BUTTON + 99, 0 },
240 { 0 }
243 static const struct message_info msg_press_nonexistent_buttons[] =
245 { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click },
246 { TDN_BUTTON_CLICKED, IDOK, 0, S_FALSE, NULL },
247 { TDN_BUTTON_CLICKED, IDYES, 0, S_FALSE, NULL },
248 { TDN_BUTTON_CLICKED, IDNO, 0, S_FALSE, NULL },
249 { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, NULL },
250 { TDN_BUTTON_CLICKED, IDRETRY, 0, S_FALSE, NULL },
251 { TDN_BUTTON_CLICKED, IDCLOSE, 0, S_FALSE, NULL },
252 { TDN_BUTTON_CLICKED, ID_START_BUTTON + 99, 0, S_OK, NULL },
253 { 0 }
256 static const struct message_info msg_send_all_common_button_click_with_command[] =
258 { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 },
259 { WM_COMMAND, MAKEWORD(IDYES, BN_CLICKED), 0 },
260 { WM_COMMAND, MAKEWORD(IDNO, BN_CLICKED), 0 },
261 { WM_COMMAND, MAKEWORD(IDCANCEL, BN_CLICKED), 0 },
262 { WM_COMMAND, MAKEWORD(IDRETRY, BN_CLICKED), 0 },
263 { WM_COMMAND, MAKEWORD(IDCLOSE, BN_CLICKED), 0 },
264 { WM_COMMAND, MAKEWORD(ID_START_BUTTON + 99, BN_CLICKED), 0 },
265 { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 },
266 { 0 }
269 static const struct message_info msg_press_nonexistent_buttons_with_command[] =
271 { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click_with_command },
272 { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_FALSE, NULL },
273 { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL },
274 { 0 }
277 static const struct message_info msg_send_nonexistent_radio_button_click[] =
279 { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON + 99, 0 },
280 { TDM_CLICK_BUTTON, IDOK, 0 },
281 { 0 }
284 static const struct message_info msg_press_nonexistent_radio_button[] =
286 { TDN_CREATED, 0, 0, S_OK, msg_send_nonexistent_radio_button_click },
287 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
288 { 0 }
291 static const struct message_info msg_return_default_verification_unchecked[] =
293 { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
294 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
295 { 0 }
298 static const struct message_info msg_return_default_verification_checked[] =
300 { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok },
301 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
302 { 0 }
305 static const struct message_info msg_uncheck_verification[] =
307 { TDM_CLICK_VERIFICATION, FALSE, 0 },
308 { 0 }
311 static const struct message_info msg_return_verification_unchecked[] =
313 { TDN_CREATED, 0, 0, S_OK, msg_uncheck_verification },
314 { TDN_VERIFICATION_CLICKED, FALSE, 0, S_OK, msg_send_click_ok },
315 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
316 { 0 }
319 static const struct message_info msg_check_verification[] =
321 { TDM_CLICK_VERIFICATION, TRUE, 0 },
322 { 0 }
325 static const struct message_info msg_return_verification_checked[] =
327 { TDN_CREATED, 0, 0, S_OK, msg_check_verification },
328 { TDN_VERIFICATION_CLICKED, TRUE, 0, S_OK, msg_send_click_ok },
329 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
330 { 0 }
333 static TASKDIALOGCONFIG navigated_info = {0};
335 static const struct message_info msg_send_navigate[] =
337 { TDM_NAVIGATE_PAGE, 0, (LPARAM)&navigated_info, 0},
338 { 0 }
341 static const struct message_info msg_return_navigated_page[] =
343 { TDN_CREATED, 0, 0, S_OK, NULL },
344 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_navigate },
345 { TDN_DIALOG_CONSTRUCTED, 0, 0, S_OK, NULL },
346 { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL },
347 { TDN_NAVIGATED, 0, 0, S_OK, msg_send_click_ok },
348 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
349 { 0 }
352 static const struct message_info msg_send_close[] =
354 { WM_CLOSE, 0, 0, 0},
355 { 0 }
358 static const struct message_info msg_handle_wm_close[] =
360 { TDN_CREATED, 0, 0, S_OK, msg_send_close },
361 { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, msg_send_close },
362 { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL },
363 { 0 }
366 static const struct message_info msg_send_close_then_ok[] =
368 { WM_CLOSE, 0, 0, 0},
369 { TDM_CLICK_BUTTON, IDOK, 0 },
370 { 0 }
373 static const struct message_info msg_handle_wm_close_without_cancel_button[] =
375 { TDN_CREATED, 0, 0, S_OK, msg_send_close_then_ok },
376 { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL },
377 { 0 }
380 static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg)
382 msg->message = WM_TD_CALLBACK;
383 msg->flags = sent|wparam|lparam|id;
384 msg->wParam = wParam;
385 msg->lParam = lParam;
386 msg->id = message;
387 msg->stage = 0;
390 #define run_test(info, expect_button, expect_radio_button, verification_checked, seq, context) \
391 run_test_(info, expect_button, expect_radio_button, verification_checked, seq, context, \
392 ARRAY_SIZE(seq) - 1, __FILE__, __LINE__)
394 static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button, BOOL verification_checked,
395 const struct message_info *test_messages, const char *context, int test_messages_len,
396 const char *file, int line)
398 struct message *msg, *msg_start;
399 int ret_button = 0;
400 int ret_radio = 0;
401 BOOL ret_verification = FALSE;
402 HRESULT hr;
403 int i;
405 /* Allocate messages to test against, plus 2 implicit and 1 empty */
406 msg_start = msg = calloc(test_messages_len + 3, sizeof(*msg));
408 /* Always needed, thus made implicit */
409 init_test_message(TDN_DIALOG_CONSTRUCTED, 0, 0, msg++);
410 for (i = 0; i < test_messages_len; i++)
411 init_test_message(test_messages[i].message, test_messages[i].wparam, test_messages[i].lparam, msg++);
412 /* Always needed, thus made implicit */
413 init_test_message(TDN_DESTROYED, 0, 0, msg++);
415 current_message_info = test_messages;
416 flush_sequences(sequences, NUM_MSG_SEQUENCES);
418 hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_verification);
419 ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#lx.\n", hr);
421 ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line);
422 ok_(file, line)(ret_button == expect_button,
423 "Wrong button. Expected %d, got %d\n", expect_button, ret_button);
424 ok_(file, line)(ret_radio == expect_radio_button,
425 "Wrong radio button. Expected %d, got %d\n", expect_radio_button, ret_radio);
427 free(msg_start);
430 static const LONG_PTR test_ref_data = 123456;
432 static HRESULT CALLBACK taskdialog_callback_proc(HWND hwnd, UINT notification,
433 WPARAM wParam, LPARAM lParam, LONG_PTR ref_data)
435 int msg_pos = sequences[TASKDIALOG_SEQ_INDEX]->count - 1; /* Skip implicit message */
436 const struct message_info *msg_send;
437 struct message msg;
439 ok(test_ref_data == ref_data, "Unexpected ref data %Iu.\n", ref_data);
441 init_test_message(notification, (short)wParam, lParam, &msg);
442 add_message(sequences, TASKDIALOG_SEQ_INDEX, &msg);
444 if (notification == TDN_DIALOG_CONSTRUCTED || notification == TDN_DESTROYED) /* Skip implicit messages */
445 return S_OK;
447 msg_send = current_message_info[msg_pos].send;
448 for(; msg_send && msg_send->message; msg_send++)
449 PostMessageW(hwnd, msg_send->message, msg_send->wparam, msg_send->lparam);
451 return current_message_info[msg_pos].callback_retval;
454 static void test_invalid_parameters(void)
456 TASKDIALOGCONFIG info = { 0 };
457 HRESULT hr;
459 hr = pTaskDialogIndirect(NULL, NULL, NULL, NULL);
460 ok(hr == E_INVALIDARG, "Unexpected return value %#lx.\n", hr);
462 info.cbSize = 0;
463 hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
464 ok(hr == E_INVALIDARG, "Unexpected return value %#lx.\n", hr);
466 info.cbSize = sizeof(TASKDIALOGCONFIG) - 1;
467 hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
468 ok(hr == E_INVALIDARG, "Unexpected return value %#lx.\n", hr);
470 info.cbSize = sizeof(TASKDIALOGCONFIG) + 1;
471 hr = pTaskDialogIndirect(&info, NULL, NULL, NULL);
472 ok(hr == E_INVALIDARG, "Unexpected return value %#lx.\n", hr);
475 static void test_callback(void)
477 TASKDIALOGCONFIG info = {0};
479 info.cbSize = sizeof(TASKDIALOGCONFIG);
480 info.pfCallback = taskdialog_callback_proc;
481 info.lpCallbackData = test_ref_data;
483 run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "Press VK_RETURN.");
486 static void test_buttons(void)
488 TASKDIALOGCONFIG info = {0};
489 static const DWORD command_link_flags[] = {0, TDF_USE_COMMAND_LINKS, TDF_USE_COMMAND_LINKS_NO_ICON};
490 TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS], radio_buttons[TEST_NUM_RADIO_BUTTONS];
491 /* Each button has two digits as title, plus null-terminator */
492 WCHAR button_titles[TEST_NUM_BUTTONS * 3], radio_button_titles[TEST_NUM_BUTTONS * 3];
493 int i;
495 info.cbSize = sizeof(TASKDIALOGCONFIG);
496 info.pfCallback = taskdialog_callback_proc;
497 info.lpCallbackData = test_ref_data;
499 /* Init custom buttons */
500 for (i = 0; i < TEST_NUM_BUTTONS; i++)
502 WCHAR *text = &button_titles[i * 3];
503 wsprintfW(text, L"%02d", i);
505 custom_buttons[i].pszButtonText = text;
506 custom_buttons[i].nButtonID = ID_START_BUTTON + i;
508 custom_buttons[TEST_NUM_BUTTONS - 1].nButtonID = -1;
510 /* Init radio buttons */
511 for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++)
513 WCHAR *text = &radio_button_titles[i * 3];
514 wsprintfW(text, L"%02d", i);
516 radio_buttons[i].pszButtonText = text;
517 radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i;
519 radio_buttons[TEST_NUM_RADIO_BUTTONS - 1].nButtonID = -2;
521 /* Test nDefaultButton */
523 /* Test common buttons with invalid default ID */
524 info.nDefaultButton = 0; /* Should default to first created button */
525 info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
526 | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
527 run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "default button: unset default");
528 info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON
529 | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
530 run_test(&info, IDYES, 0, FALSE, msg_return_press_yes, "default button: unset default");
531 info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
532 run_test(&info, IDNO, 0, FALSE, msg_return_press_no, "default button: unset default");
533 info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
534 run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: unset default");
535 info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON;
536 run_test(&info, IDCANCEL, 0, FALSE, msg_return_press_cancel, "default button: unset default");
538 /* Custom buttons could be command links */
539 for (i = 0; i < ARRAY_SIZE(command_link_flags); i++)
541 info.dwFlags = command_link_flags[i];
543 /* Test with all common and custom buttons and invalid default ID */
544 info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */
545 info.cButtons = TEST_NUM_BUTTONS;
546 info.pButtons = custom_buttons;
547 run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
548 "default button: invalid default, with common buttons - 1");
550 info.nDefaultButton = -1; /* Should work despite button ID -1 */
551 run_test(&info, -1, 0, FALSE, msg_return_press_custom10, "default button: invalid default, with common buttons - 2");
553 info.nDefaultButton = -2; /* Should also default to first created button */
554 run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
555 "default button: invalid default, with common buttons - 3");
557 /* Test with only custom buttons and invalid default ID */
558 info.dwCommonButtons = 0;
559 run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1,
560 "default button: invalid default, no common buttons");
562 /* Test with common and custom buttons and valid default ID */
563 info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON
564 | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON;
565 info.nDefaultButton = IDRETRY;
566 run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: valid default - 1");
568 /* Test with common and custom buttons and valid default ID */
569 info.nDefaultButton = ID_START_BUTTON + 3;
570 run_test(&info, ID_START_BUTTON + 3, 0, FALSE, msg_return_press_custom4, "default button: valid default - 2");
573 /* Test radio buttons */
574 info.nDefaultButton = 0;
575 info.cButtons = 0;
576 info.pButtons = 0;
577 info.dwCommonButtons = TDCBF_OK_BUTTON;
578 info.cRadioButtons = TEST_NUM_RADIO_BUTTONS;
579 info.pRadioButtons = radio_buttons;
581 /* Test default first radio button */
582 run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_1,
583 "default radio button: default first radio button");
585 /* Test default radio button */
586 info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
587 run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_2,
588 "default radio button: default radio button");
590 /* Test default radio button with -2 */
591 info.nDefaultRadioButton = -2;
592 run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_3,
593 "default radio button: default radio button with id -2");
595 /* Test default radio button after clicking the first, messages still work even radio button is disabled */
596 info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
597 run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_first_radio_button,
598 "default radio button: radio button after clicking");
600 /* Test radio button after disabling and clicking the first */
601 info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1;
602 run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_clicking_disabled,
603 "default radio button: disable radio button before clicking");
605 /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set, TDN_RADIO_BUTTON_CLICKED will still be received, just radio button not selected */
606 info.nDefaultRadioButton = ID_START_RADIO_BUTTON;
607 info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
608 run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_no_default_radio_button_flag,
609 "default radio button: no default radio flag");
611 /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is 0.
612 * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
613 info.nDefaultRadioButton = 0;
614 info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
615 run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
616 "default radio button: no default radio id and flag");
618 /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is invalid.
619 * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */
620 info.nDefaultRadioButton = 0xff;
621 info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
622 run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag,
623 "default radio button: no default flag, invalid id");
625 info.nDefaultRadioButton = 0;
626 info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
627 run_test(&info, IDOK, -2, FALSE, msg_return_press_negative_id_radio_button,
628 "radio button: manually click radio button with negative id");
630 /* Test sending clicks to non-existent buttons. Notification of non-existent buttons will be sent */
631 info.cButtons = TEST_NUM_BUTTONS;
632 info.pButtons = custom_buttons;
633 info.cRadioButtons = TEST_NUM_RADIO_BUTTONS;
634 info.pRadioButtons = radio_buttons;
635 info.dwCommonButtons = 0;
636 info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON;
637 run_test(&info, ID_START_BUTTON + 99, 0, FALSE, msg_press_nonexistent_buttons, "sends click to non-existent buttons");
639 /* Non-existent button clicks sent by WM_COMMAND won't generate TDN_BUTTON_CLICKED except IDOK.
640 * And will get the first existent button identifier instead of IDOK */
641 run_test(&info, ID_START_BUTTON, 0, FALSE, msg_press_nonexistent_buttons_with_command,
642 "sends click to non-existent buttons with WM_COMMAND");
644 /* Non-existent radio button won't get notifications */
645 run_test(&info, IDOK, 0, FALSE, msg_press_nonexistent_radio_button, "sends click to non-existent radio buttons");
648 static void test_help(void)
650 TASKDIALOGCONFIG info = {0};
652 info.cbSize = sizeof(TASKDIALOGCONFIG);
653 info.pfCallback = taskdialog_callback_proc;
654 info.lpCallbackData = test_ref_data;
655 info.dwCommonButtons = TDCBF_OK_BUTTON;
657 run_test(&info, IDOK, 0, FALSE, msg_got_tdn_help, "send f1");
660 struct timer_notification_data
662 DWORD last_elapsed_ms;
663 DWORD num_fired;
666 static HRESULT CALLBACK taskdialog_callback_proc_timer(HWND hwnd, UINT notification,
667 WPARAM wParam, LPARAM lParam, LONG_PTR ref_data)
669 struct timer_notification_data *data = (struct timer_notification_data *)ref_data;
671 if (notification == TDN_TIMER)
673 DWORD elapsed_ms;
674 int delta;
676 elapsed_ms = (DWORD)wParam;
678 if (data->num_fired == 3)
679 ok(data->last_elapsed_ms > elapsed_ms, "Expected reference time update.\n");
680 else
682 delta = elapsed_ms - data->last_elapsed_ms;
683 ok(delta > 0, "Expected positive time tick difference.\n");
685 data->last_elapsed_ms = elapsed_ms;
687 if (data->num_fired == 3)
688 PostMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0);
690 ++data->num_fired;
691 return data->num_fired == 3 ? S_FALSE : S_OK;
694 return S_OK;
697 static void test_timer(void)
699 struct timer_notification_data data = { 0 };
700 TASKDIALOGCONFIG info = { 0 };
702 info.cbSize = sizeof(TASKDIALOGCONFIG);
703 info.pfCallback = taskdialog_callback_proc_timer;
704 info.lpCallbackData = (LONG_PTR)&data;
705 info.dwFlags = TDF_CALLBACK_TIMER;
706 info.dwCommonButtons = TDCBF_OK_BUTTON;
708 pTaskDialogIndirect(&info, NULL, NULL, NULL);
711 static HRESULT CALLBACK taskdialog_callback_proc_progress_bar(HWND hwnd, UINT notification, WPARAM wParam,
712 LPARAM lParam, LONG_PTR ref_data)
714 unsigned long ret;
715 LONG flags = (LONG)ref_data;
716 if (notification == TDN_CREATED)
718 /* TDM_SET_PROGRESS_BAR_STATE */
719 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0);
720 ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret);
721 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_PAUSED, 0);
722 ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret);
723 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_ERROR, 0);
724 /* Progress bar has fixme on handling PBM_SETSTATE message */
725 todo_wine ok(ret == PBST_PAUSED, "Expect state: %d got state: %lx\n", PBST_PAUSED, ret);
726 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0);
727 todo_wine ok(ret == PBST_ERROR, "Expect state: %d got state: %lx\n", PBST_ERROR, ret);
729 /* TDM_SET_PROGRESS_BAR_RANGE */
730 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200));
731 ok(ret == MAKELONG(0, 100), "Expect range:%lx got:%lx\n", MAKELONG(0, 100), ret);
732 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200));
733 ok(ret == MAKELONG(0, 200), "Expect range:%lx got:%lx\n", MAKELONG(0, 200), ret);
735 /* TDM_SET_PROGRESS_BAR_POS */
736 if (flags & TDF_SHOW_MARQUEE_PROGRESS_BAR)
738 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0);
739 ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret);
740 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0);
741 ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret);
743 else
745 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0);
746 ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret);
747 ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0);
748 ok(ret == 1, "Expect position:%x got:%lx\n", 1, ret);
751 SendMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0);
754 return S_OK;
757 static void test_progress_bar(void)
759 TASKDIALOGCONFIG info = {0};
761 info.cbSize = sizeof(TASKDIALOGCONFIG);
762 info.dwFlags = TDF_SHOW_PROGRESS_BAR;
763 info.pfCallback = taskdialog_callback_proc_progress_bar;
764 info.lpCallbackData = (LONG_PTR)info.dwFlags;
765 info.dwCommonButtons = TDCBF_OK_BUTTON;
766 pTaskDialogIndirect(&info, NULL, NULL, NULL);
768 info.dwFlags = TDF_SHOW_MARQUEE_PROGRESS_BAR;
769 info.lpCallbackData = (LONG_PTR)info.dwFlags;
770 pTaskDialogIndirect(&info, NULL, NULL, NULL);
772 info.dwFlags = TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR;
773 info.lpCallbackData = (LONG_PTR)info.dwFlags;
774 pTaskDialogIndirect(&info, NULL, NULL, NULL);
777 static void test_verification_box(void)
779 TASKDIALOGCONFIG info = {0};
781 info.cbSize = sizeof(TASKDIALOGCONFIG);
782 info.pfCallback = taskdialog_callback_proc;
783 info.lpCallbackData = test_ref_data;
784 info.dwCommonButtons = TDCBF_OK_BUTTON;
786 /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
787 run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked");
789 info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
790 run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked");
792 info.pszVerificationText = L"text";
793 run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked");
795 info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
796 run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked");
798 run_test(&info, IDOK, 0, FALSE, msg_return_verification_unchecked,
799 "default verification box: default checked and then unchecked");
801 info.dwFlags = 0;
802 run_test(&info, IDOK, 0, FALSE, msg_return_verification_checked,
803 "default verification box: default unchecked and then checked");
806 static void test_navigate_page(void)
808 TASKDIALOGCONFIG info = {0};
809 TASKDIALOG_BUTTON radio_buttons[TEST_NUM_RADIO_BUTTONS];
810 WCHAR radio_button_titles[TEST_NUM_BUTTONS * 3];
811 int i;
813 /* Init radio buttons */
814 for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++)
816 WCHAR *text = &radio_button_titles[i * 3];
817 wsprintfW(text, L"%02d", i);
819 radio_buttons[i].pszButtonText = text;
820 radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i;
823 info.cbSize = sizeof(TASKDIALOGCONFIG);
824 info.pfCallback = taskdialog_callback_proc;
825 info.lpCallbackData = test_ref_data;
826 info.dwCommonButtons = TDCBF_OK_BUTTON;
827 info.cRadioButtons = TEST_NUM_RADIO_BUTTONS;
828 info.pRadioButtons = radio_buttons;
830 navigated_info = info;
831 navigated_info.pszVerificationText = L"text";
832 navigated_info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED;
834 run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: default");
836 /* TDM_NAVIGATE_PAGE doesn't check cbSize.
837 * And null taskconfig pointer crash applicatioin, thus doesn't check pointer either */
838 navigated_info.cbSize = 0;
839 run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: invalid taskconfig cbSize");
842 static void test_wm_close(void)
844 TASKDIALOGCONFIG info = {0};
846 info.cbSize = sizeof(TASKDIALOGCONFIG);
847 info.pfCallback = taskdialog_callback_proc;
848 info.lpCallbackData = test_ref_data;
850 /* WM_CLOSE can end the dialog only when a cancel button is present or dwFlags has TDF_ALLOW_DIALOG_CANCELLATION */
851 info.dwCommonButtons = TDCBF_OK_BUTTON;
852 run_test(&info, IDOK, 0, FALSE, msg_handle_wm_close_without_cancel_button, "send WM_CLOSE without cancel button");
854 info.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION;
855 run_test(&info, IDCANCEL, 0, FALSE, msg_handle_wm_close, "send WM_CLOSE with TDF_ALLOW_DIALOG_CANCELLATION");
857 info.dwFlags = 0;
858 info.dwCommonButtons = TDCBF_CANCEL_BUTTON;
859 run_test(&info, IDCANCEL, 0, FALSE, msg_handle_wm_close, "send WM_CLOSE with a cancel button");
862 START_TEST(taskdialog)
864 ULONG_PTR ctx_cookie;
865 void *ptr_ordinal;
866 HINSTANCE hinst;
867 HANDLE hCtx;
869 if (!load_v6_module(&ctx_cookie, &hCtx))
870 return;
872 /* Check if task dialogs are available */
873 hinst = LoadLibraryA("comctl32.dll");
875 pTaskDialogIndirect = (void *)GetProcAddress(hinst, "TaskDialogIndirect");
876 if (!pTaskDialogIndirect)
878 win_skip("TaskDialogIndirect not exported by name\n");
879 unload_v6_module(ctx_cookie, hCtx);
880 return;
883 pTaskDialog = (void *)GetProcAddress(hinst, "TaskDialog");
885 ptr_ordinal = GetProcAddress(hinst, (const char *)344);
886 ok(pTaskDialog == ptr_ordinal, "got wrong pointer for ordinal 344, %p expected %p\n",
887 ptr_ordinal, pTaskDialog);
889 ptr_ordinal = GetProcAddress(hinst, (const char *)345);
890 ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n",
891 ptr_ordinal, pTaskDialogIndirect);
893 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
895 test_invalid_parameters();
896 test_callback();
897 test_buttons();
898 test_help();
899 test_timer();
900 test_progress_bar();
901 test_verification_box();
902 test_navigate_page();
903 test_wm_close();
905 unload_v6_module(ctx_cookie, hCtx);