xref: /reactos/dll/win32/comctl32/taskdialog.c (revision cc439606)
1 /*
2  * Task dialog control
3  *
4  * Copyright 2017 Fabian Maurer
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  */
21 
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #define NONAMELESSUNION
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "commctrl.h"
33 #include "winerror.h"
34 #include "comctl32.h"
35 
36 #include "wine/debug.h"
37 #include "wine/list.h"
38 #include "wine/unicode.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
41 
42 #define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
43 #define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
44 #define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
45 #define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
46 
47 static const UINT DIALOG_MIN_WIDTH = 240;
48 static const UINT DIALOG_SPACING = 5;
49 static const UINT DIALOG_BUTTON_WIDTH = 50;
50 static const UINT DIALOG_BUTTON_HEIGHT = 14;
51 
52 static const UINT ID_MAIN_INSTRUCTION = 0xf000;
53 static const UINT ID_CONTENT          = 0xf001;
54 
55 struct taskdialog_control
56 {
57     struct list entry;
58     DLGITEMTEMPLATE *template;
59     unsigned int template_size;
60 };
61 
62 struct taskdialog_button_desc
63 {
64     int id;
65     const WCHAR *text;
66     unsigned int width;
67     unsigned int line;
68     HINSTANCE hinst;
69 };
70 
71 struct taskdialog_template_desc
72 {
73     const TASKDIALOGCONFIG *taskconfig;
74     unsigned int dialog_height;
75     unsigned int dialog_width;
76     struct list controls;
77     WORD control_count;
78     LONG x_baseunit;
79     LONG y_baseunit;
80     HFONT font;
81     struct taskdialog_button_desc *default_button;
82 };
83 
84 struct taskdialog_info
85 {
86     HWND hwnd;
87     PFTASKDIALOGCALLBACK callback;
88     LONG_PTR callback_data;
89 };
90 
91 static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
92 {
93     if (width)
94         *width = MulDiv(*width, 4, desc->x_baseunit);
95     if (height)
96         *height = MulDiv(*height, 8, desc->y_baseunit);
97 }
98 
99 static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
100 {
101     if (width)
102         *width = MulDiv(*width, desc->x_baseunit, 4);
103     if (height)
104         *height = MulDiv(*height, desc->y_baseunit, 8);
105 }
106 
107 static void template_write_data(char **ptr, const void *src, unsigned int size)
108 {
109     memcpy(*ptr, src, size);
110     *ptr += size;
111 }
112 
113 /* used to calculate size for the controls */
114 static void taskdialog_get_text_extent(const struct taskdialog_template_desc *desc, const WCHAR *text,
115         BOOL user_resource, SIZE *sz)
116 {
117     RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
118     const WCHAR *textW = NULL;
119     static const WCHAR nulW;
120     unsigned int length;
121     HFONT oldfont;
122     HDC hdc;
123 
124     if (IS_INTRESOURCE(text))
125     {
126         if (!(length = LoadStringW(user_resource ? desc->taskconfig->hInstance : COMCTL32_hModule,
127                 (UINT_PTR)text, (WCHAR *)&textW, 0)))
128         {
129             WARN("Failed to load text\n");
130             textW = &nulW;
131             length = 0;
132         }
133     }
134     else
135     {
136         textW = text;
137         length = strlenW(textW);
138     }
139 
140     hdc = GetDC(0);
141     oldfont = SelectObject(hdc, desc->font);
142 
143     dialogunits_to_pixels(desc, &rect.right, NULL);
144     DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK);
145     pixels_to_dialogunits(desc, &rect.right, &rect.bottom);
146 
147     SelectObject(hdc, oldfont);
148     ReleaseDC(0, hdc);
149 
150     sz->cx = rect.right - rect.left;
151     sz->cy = rect.bottom - rect.top;
152 }
153 
154 static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
155         HINSTANCE hInstance, const WCHAR *text, DWORD style, short x, short y, short cx, short cy)
156 {
157     struct taskdialog_control *control = Alloc(sizeof(*control));
158     unsigned int size, class_size, text_size;
159     DLGITEMTEMPLATE *template;
160     static const WCHAR nulW;
161     const WCHAR *textW;
162     char *ptr;
163 
164     class_size = (strlenW(class) + 1) * sizeof(WCHAR);
165 
166     if (IS_INTRESOURCE(text))
167         text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR);
168     else
169     {
170         textW = text;
171         text_size = strlenW(textW) * sizeof(WCHAR);
172     }
173 
174     size = sizeof(DLGITEMTEMPLATE);
175     size += class_size;
176     size += text_size + sizeof(WCHAR);
177     size += sizeof(WORD); /* creation data */
178 
179     control->template = template = Alloc(size);
180     control->template_size = size;
181 
182     template->style = WS_VISIBLE | style;
183     template->dwExtendedStyle = 0;
184     template->x = x;
185     template->y = y;
186     template->cx = cx;
187     template->cy = cy;
188     template->id = id;
189     ptr = (char *)(template + 1);
190     template_write_data(&ptr, class, class_size);
191     template_write_data(&ptr, textW, text_size);
192     template_write_data(&ptr, &nulW, sizeof(nulW));
193 
194     list_add_tail(&desc->controls, &control->entry);
195     desc->control_count++;
196     return ALIGNED_LENGTH(size, 3);
197 }
198 
199 static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str)
200 {
201     unsigned int size;
202     SIZE sz;
203 
204     if (!str)
205         return 0;
206 
207     taskdialog_get_text_extent(desc, str, TRUE, &sz);
208 
209     desc->dialog_height += DIALOG_SPACING;
210     size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, 0, DIALOG_SPACING,
211             desc->dialog_height, sz.cx, sz.cy);
212     desc->dialog_height += sz.cy + DIALOG_SPACING;
213     return size;
214 }
215 
216 static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
217 {
218     return taskdialog_add_static_label(desc, ID_MAIN_INSTRUCTION, desc->taskconfig->pszMainInstruction);
219 }
220 
221 static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc)
222 {
223     return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent);
224 }
225 
226 static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc,
227         int id, const WCHAR *text, BOOL custom_button)
228 {
229     SIZE sz;
230 
231     taskdialog_get_text_extent(desc, text, custom_button, &sz);
232 
233     button->id = id;
234     button->text = text;
235     button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2);
236     button->line = 0;
237     button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule;
238 
239     if (id == desc->taskconfig->nDefaultButton)
240         desc->default_button = button;
241 }
242 
243 static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons,
244     unsigned int *button_count)
245 {
246     DWORD flags = desc->taskconfig->dwCommonButtons;
247 
248 #define TASKDIALOG_INIT_COMMON_BUTTON(id) \
249     do { \
250         taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \
251     } while(0)
252 
253     if (flags & TDCBF_OK_BUTTON)
254         TASKDIALOG_INIT_COMMON_BUTTON(OK);
255     if (flags & TDCBF_YES_BUTTON)
256         TASKDIALOG_INIT_COMMON_BUTTON(YES);
257     if (flags & TDCBF_NO_BUTTON)
258         TASKDIALOG_INIT_COMMON_BUTTON(NO);
259     if (flags & TDCBF_RETRY_BUTTON)
260         TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
261     if (flags & TDCBF_CANCEL_BUTTON)
262         TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
263     if (flags & TDCBF_CLOSE_BUTTON)
264         TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
265 
266 #undef TASKDIALOG_INIT_COMMON_BUTTON
267 }
268 
269 static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc)
270 {
271     unsigned int count = 0, buttons_size, i, line_count, size = 0;
272     unsigned int location_x, *line_widths, alignment = ~0u;
273     const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
274     struct taskdialog_button_desc *buttons;
275 
276     /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
277     buttons_size = 6;
278     if (taskconfig->cButtons && taskconfig->pButtons)
279         buttons_size += taskconfig->cButtons;
280 
281     if (!(buttons = Alloc(buttons_size * sizeof(*buttons))))
282         return 0;
283 
284     /* Custom buttons */
285     if (taskconfig->cButtons && taskconfig->pButtons)
286         for (i = 0; i < taskconfig->cButtons; i++)
287             taskdialog_init_button(&buttons[count++], desc, taskconfig->pButtons[i].nButtonID,
288                     taskconfig->pButtons[i].pszButtonText, TRUE);
289 
290     /* Common buttons */
291     taskdialog_init_common_buttons(desc, buttons, &count);
292 
293     /* There must be at least one button */
294     if (count == 0)
295         taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE);
296 
297     if (!desc->default_button)
298         desc->default_button = &buttons[0];
299 
300     /* For easy handling just allocate as many lines as buttons, the worst case. */
301     line_widths = Alloc(count * sizeof(*line_widths));
302 
303     /* Separate buttons into lines */
304     location_x = DIALOG_SPACING;
305     for (i = 0, line_count = 0; i < count; i++)
306     {
307         if (location_x + buttons[i].width + DIALOG_SPACING > desc->dialog_width)
308         {
309             location_x = DIALOG_SPACING;
310             line_count++;
311         }
312 
313         buttons[i].line = line_count;
314 
315         location_x += buttons[i].width + DIALOG_SPACING;
316         line_widths[line_count] += buttons[i].width + DIALOG_SPACING;
317     }
318     line_count++;
319 
320     /* Try to balance lines so they are about the same size */
321     for (i = 1; i < line_count - 1; i++)
322     {
323         int diff_now = abs(line_widths[i] - line_widths[i - 1]);
324         unsigned int j, last_button = 0;
325         int diff_changed;
326 
327         for (j = 0; j < count; j++)
328             if (buttons[j].line == i - 1)
329                 last_button = j;
330 
331         /* Difference in length of both lines if we wrapped the last button from the last line into this one */
332         diff_changed = abs(2 * buttons[last_button].width + line_widths[i] - line_widths[i - 1]);
333 
334         if (diff_changed < diff_now)
335         {
336             buttons[last_button].line = i;
337             line_widths[i] += buttons[last_button].width;
338             line_widths[i - 1] -= buttons[last_button].width;
339         }
340     }
341 
342     /* Calculate left alignment so all lines are as far right as possible. */
343     for (i = 0; i < line_count; i++)
344     {
345         int new_alignment = desc->dialog_width - line_widths[i];
346         if (new_alignment < alignment)
347             alignment = new_alignment;
348     }
349 
350     /* Now that we got them all positioned, create all buttons */
351     location_x = alignment;
352     for (i = 0; i < count; i++)
353     {
354         DWORD style = &buttons[i] == desc->default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
355 
356         if (i > 0 && buttons[i].line != buttons[i - 1].line) /* New line */
357         {
358             location_x = alignment;
359             desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
360         }
361 
362         size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, style,
363                 location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
364 
365         location_x += buttons[i].width + DIALOG_SPACING;
366     }
367 
368     /* Add height for last row and spacing */
369     desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
370 
371     Free(line_widths);
372     Free(buttons);
373 
374     return size;
375 }
376 
377 static void taskdialog_clear_controls(struct list *controls)
378 {
379     struct taskdialog_control *control, *control2;
380 
381     LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, struct taskdialog_control, entry)
382     {
383         list_remove(&control->entry);
384         Free(control->template);
385         Free(control);
386     }
387 }
388 
389 static unsigned int taskdialog_get_reference_rect(const struct taskdialog_template_desc *desc, RECT *ret)
390 {
391     HMONITOR monitor = MonitorFromWindow(desc->taskconfig->hwndParent ? desc->taskconfig->hwndParent : GetActiveWindow(),
392             MONITOR_DEFAULTTOPRIMARY);
393     MONITORINFO info;
394 
395     info.cbSize = sizeof(info);
396     GetMonitorInfoW(monitor, &info);
397 
398     if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent)
399         GetWindowRect(desc->taskconfig->hwndParent, ret);
400     else
401         *ret = info.rcWork;
402 
403     pixels_to_dialogunits(desc, &ret->left, &ret->top);
404     pixels_to_dialogunits(desc, &ret->right, &ret->bottom);
405 
406     pixels_to_dialogunits(desc, &info.rcWork.left, &info.rcWork.top);
407     pixels_to_dialogunits(desc, &info.rcWork.right, &info.rcWork.bottom);
408     return info.rcWork.right - info.rcWork.left;
409 }
410 
411 static WCHAR *taskdialog_get_exe_name(const TASKDIALOGCONFIG *taskconfig, WCHAR *name, DWORD length)
412 {
413     DWORD len = GetModuleFileNameW(NULL, name, length);
414     if (len && len < length)
415     {
416         WCHAR *p;
417         if ((p = strrchrW(name, '/'))) name = p + 1;
418         if ((p = strrchrW(name, '\\'))) name = p + 1;
419         return name;
420     }
421     else
422         return NULL;
423 }
424 
425 static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
426 {
427     struct taskdialog_control *control, *control2;
428     unsigned int size, title_size, screen_width;
429     struct taskdialog_template_desc desc;
430     static const WORD fontsize = 0x7fff;
431     static const WCHAR emptyW[] = { 0 };
432     const WCHAR *titleW = NULL;
433     DLGTEMPLATE *template;
434     NONCLIENTMETRICSW ncm;
435     WCHAR pathW[MAX_PATH];
436     RECT ref_rect;
437     char *ptr;
438     HDC hdc;
439 
440     /* Window title */
441     if (!taskconfig->pszWindowTitle)
442         titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
443     else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
444     {
445         if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
446             titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
447     }
448     else
449         titleW = taskconfig->pszWindowTitle;
450     if (!titleW)
451         titleW = emptyW;
452     title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
453 
454     size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
455     size += title_size;
456     size += 2; /* font size */
457 
458     list_init(&desc.controls);
459     desc.taskconfig = taskconfig;
460     desc.control_count = 0;
461 
462     ncm.cbSize = sizeof(ncm);
463     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
464     desc.font = CreateFontIndirectW(&ncm.lfMessageFont);
465 
466     hdc = GetDC(0);
467     SelectObject(hdc, desc.font);
468     desc.x_baseunit = GdiGetCharDimensions(hdc, NULL, &desc.y_baseunit);
469     ReleaseDC(0, hdc);
470 
471     screen_width = taskdialog_get_reference_rect(&desc, &ref_rect);
472 
473     desc.dialog_height = 0;
474     desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
475     desc.dialog_width = min(desc.dialog_width, screen_width);
476     desc.default_button = NULL;
477 
478     size += taskdialog_add_main_instruction(&desc);
479     size += taskdialog_add_content(&desc);
480     size += taskdialog_add_buttons(&desc);
481 
482     template = Alloc(size);
483     if (!template)
484     {
485         taskdialog_clear_controls(&desc.controls);
486         DeleteObject(desc.font);
487         return NULL;
488     }
489 
490     template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
491     template->cdit = desc.control_count;
492     template->x = (ref_rect.left + ref_rect.right + desc.dialog_width) / 2;
493     template->y = (ref_rect.top + ref_rect.bottom + desc.dialog_height) / 2;
494     template->cx = desc.dialog_width;
495     template->cy = desc.dialog_height;
496 
497     ptr = (char *)(template + 1);
498     ptr += 2; /* menu */
499     ptr += 2; /* class */
500     template_write_data(&ptr, titleW, title_size);
501     template_write_data(&ptr, &fontsize, sizeof(fontsize));
502 
503     /* write control entries */
504     LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry)
505     {
506         ALIGN_POINTER(ptr, 3);
507 
508         template_write_data(&ptr, control->template, control->template_size);
509 
510         /* list item won't be needed later */
511         list_remove(&control->entry);
512         Free(control->template);
513         Free(control);
514     }
515 
516     DeleteObject(desc.font);
517     return template;
518 }
519 
520 static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
521 {
522     return dialog_info->callback ? dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam,
523             dialog_info->callback_data) : S_OK;
524 }
525 
526 static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, WORD command_id)
527 {
528     if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
529         EndDialog(dialog_info->hwnd, command_id);
530 }
531 
532 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
533 {
534     static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
535     struct taskdialog_info *dialog_info;
536 
537     TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
538 
539     if (msg != WM_INITDIALOG)
540         dialog_info = GetPropW(hwnd, taskdialog_info_propnameW);
541 
542     switch (msg)
543     {
544         case TDM_CLICK_BUTTON:
545             taskdialog_on_button_click(dialog_info, LOWORD(wParam));
546             break;
547         case WM_INITDIALOG:
548             dialog_info = (struct taskdialog_info *)lParam;
549             dialog_info->hwnd = hwnd;
550             SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
551 
552             taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
553             break;
554         case WM_SHOWWINDOW:
555             taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
556             break;
557         case WM_COMMAND:
558             if (HIWORD(wParam) == BN_CLICKED)
559             {
560                 taskdialog_on_button_click(dialog_info, LOWORD(wParam));
561                 return TRUE;
562             }
563             break;
564         case WM_DESTROY:
565             taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
566             RemovePropW(hwnd, taskdialog_info_propnameW);
567             break;
568     }
569     return FALSE;
570 }
571 
572 /***********************************************************************
573  * TaskDialogIndirect [COMCTL32.@]
574  */
575 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *button,
576                                   int *radio_button, BOOL *verification_flag_checked)
577 {
578     struct taskdialog_info dialog_info;
579     DLGTEMPLATE *template;
580     INT ret;
581 
582     TRACE("%p, %p, %p, %p\n", taskconfig, button, radio_button, verification_flag_checked);
583 
584     if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
585         return E_INVALIDARG;
586 
587     dialog_info.callback = taskconfig->pfCallback;
588     dialog_info.callback_data = taskconfig->lpCallbackData;
589 
590     template = create_taskdialog_template(taskconfig);
591     ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
592             taskdialog_proc, (LPARAM)&dialog_info);
593     Free(template);
594 
595     if (button) *button = ret;
596     if (radio_button) *radio_button = taskconfig->nDefaultButton;
597     if (verification_flag_checked) *verification_flag_checked = TRUE;
598 
599     return S_OK;
600 }
601 
602 /***********************************************************************
603  * TaskDialog [COMCTL32.@]
604  */
605 HRESULT WINAPI TaskDialog(HWND owner, HINSTANCE hinst, const WCHAR *title, const WCHAR *main_instruction,
606     const WCHAR *content, TASKDIALOG_COMMON_BUTTON_FLAGS common_buttons, const WCHAR *icon, int *button)
607 {
608     TASKDIALOGCONFIG taskconfig;
609 
610     TRACE("%p, %p, %s, %s, %s, %#x, %s, %p\n", owner, hinst, debugstr_w(title), debugstr_w(main_instruction),
611         debugstr_w(content), common_buttons, debugstr_w(icon), button);
612 
613     memset(&taskconfig, 0, sizeof(taskconfig));
614     taskconfig.cbSize = sizeof(taskconfig);
615     taskconfig.hwndParent = owner;
616     taskconfig.hInstance = hinst;
617     taskconfig.dwCommonButtons = common_buttons;
618     taskconfig.pszWindowTitle = title;
619     taskconfig.u.pszMainIcon = icon;
620     taskconfig.pszMainInstruction = main_instruction;
621     taskconfig.pszContent = content;
622     return TaskDialogIndirect(&taskconfig, button, NULL, NULL);
623 }
624