[v4 2/5] comctl32: Add basic implementation for task dialogs

Fabian Maurer dark.shadow4 at web.de
Wed Mar 15 15:13:20 CDT 2017


The dialog doesn't show text and can basically do nothing,
but it sets the foundation.

v2: Skip test on windows when taskdialogs aren't available
v3: Rewrite to implement Nikolay Sivov's suggestions
v4: Rewrite to fix a bunch of issues and make patchset smaller

Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
 dlls/comctl32/taskdialog.c | 192 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 163 insertions(+), 29 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index c26d53bac9..0d0535ea2b 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -28,9 +28,134 @@
 #include "commctrl.h"
 #include "winerror.h"
 #include "comctl32.h"
+
+#include "wine/list.h"
 #include "wine/debug.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
+WINE_DEFAULT_DEBUG_CHANNEL(taskdlg);
+
+typedef struct
+{
+    DLGITEMTEMPLATE template;
+    const WCHAR *class; /* For simplicity, we don't use ordinals but only the class name */
+    const WCHAR *text;
+    WORD creation_data;
+
+    /* not part of actual template */
+    UINT text_size;  /* Length in bytes including null-terminator */
+    UINT class_size; /* Length in bytes including null-terminator */
+    struct list entry;
+} control_info;
+
+typedef struct
+{
+    DLGTEMPLATE template;
+    WORD menu;
+    WORD class;
+    const WCHAR *title;
+
+    /* Not part of actual template */
+    UINT title_size; /* Length in bytes including null-terminator */
+} dialog_header;
+
+#define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target = (void *)((char *)target + size);
+#define ALIGN_POINTER(ptr, boundary) ptr = (void *)(((UINT_PTR)(ptr) + (boundary)) & ~(boundary))
+
+/* Functions for turning our dialog structures into a usable dialog template
+ * We don't load the dialog template from a resource, we instead create it in memory
+ * This way we can easily handle variable control numbers */
+DLGTEMPLATE* dialog_template_create(dialog_header header, struct list *controls)
+{
+    control_info *control;
+    DLGTEMPLATE *template_data_start;
+    void *template_data;
+    int data_size = 0;
+
+    data_size += sizeof(DLGTEMPLATE);
+    data_size += sizeof(WORD) * 2 + header.title_size;
+    LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+    {
+        data_size += sizeof(DWORD); /* Each item is aligned on DWORD boundary, allocate a bit of padding */
+        data_size += sizeof(DLGITEMTEMPLATE);
+        data_size += control->text_size;
+        data_size += control->class_size;
+        data_size += sizeof(WORD);
+    }
+
+    template_data_start = Alloc(data_size);
+    template_data = template_data_start;
+
+    /* Copy header data */
+    MEMCPY_MOVEPTR(template_data, &header.template, sizeof(DLGTEMPLATE));
+    MEMCPY_MOVEPTR(template_data, &header.menu, sizeof(WORD));
+    MEMCPY_MOVEPTR(template_data, &header.class, sizeof(WORD));
+    MEMCPY_MOVEPTR(template_data, header.title, header.title_size);
+
+    /* Copy dialog member data */
+    LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+    {
+        /* Align on DWORD boundary for each new control */
+        ALIGN_POINTER(template_data, 3);
+
+        MEMCPY_MOVEPTR(template_data, &control->template, sizeof(DLGITEMTEMPLATE));
+        MEMCPY_MOVEPTR(template_data, control->class, control->class_size);
+        MEMCPY_MOVEPTR(template_data, control->text, control->text_size);
+        MEMCPY_MOVEPTR(template_data, &control->creation_data, sizeof(WORD));
+    }
+
+    return template_data_start;
+}
+
+/* Functions to handle the dialog control list */
+
+static void controls_destroy(struct list *controls)
+{
+    control_info *control, *control2;
+    LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, control_info, entry)
+    {
+        list_remove(&control->entry);
+        Free(control);
+    }
+}
+
+/* Adds a control for the TaskDialog into our list */
+static void controls_add(struct list *controls, WORD id, const WCHAR *class, const WCHAR *text,
+                       DWORD style, short x, short y, short cx, short cy)
+{
+    control_info *data = Alloc(sizeof(control_info));
+
+    data->template.x = x;
+    data->template.y = y;
+    data->template.cx = cx;
+    data->template.cy = cy;
+    data->template.id = id;
+    data->template.style = style;
+
+    data->class = class;
+    data->class_size = (lstrlenW(class) + 1) * sizeof(WCHAR);
+    data->text = text;
+    data->text_size = (lstrlenW(text) + 1) * sizeof(WCHAR);
+
+    list_add_tail(controls, &data->entry);
+}
+
+static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    TRACE("hwndDlg=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwndDlg, uMsg, wParam, lParam);
+
+    switch (uMsg)
+    {
+        case WM_COMMAND:
+            if (HIWORD(wParam) == BN_CLICKED)
+            {
+                WORD command_id = LOWORD(wParam);
+                EndDialog(hwndDlg, command_id);
+                return TRUE;
+            }
+            break;
+    }
+    return FALSE;
+}
 
 /***********************************************************************
  * TaskDialogIndirect [COMCTL32.@]
@@ -38,35 +163,44 @@ WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
 HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton,
                                   int *pnRadioButton, BOOL *pfVerificationFlagChecked)
 {
-    UINT uType = 0;
-    INT  ret;
+    static const WCHAR empty_string[] = {0};
+    static const WCHAR text_ok[] = {'O','K',0};
+    DLGTEMPLATE *template_data;
+    dialog_header header = {0};
+    struct list controls;
+
     TRACE("%p, %p, %p, %p\n", pTaskConfig, pnButton, pnRadioButton, pfVerificationFlagChecked);
 
-    if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_YESNOCANCEL;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_YES_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_NO_BUTTON)
-        uType |= MB_YESNO;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_RETRY_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_RETRYCANCEL;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON &&
-        pTaskConfig->dwCommonButtons & TDCBF_CANCEL_BUTTON)
-        uType |= MB_OKCANCEL;
-    else
-    if (pTaskConfig->dwCommonButtons & TDCBF_OK_BUTTON)
-        uType |= MB_OK;
-    ret = MessageBoxW(pTaskConfig->hwndParent, pTaskConfig->pszMainInstruction,
-                      pTaskConfig->pszWindowTitle, uType);
-    FIXME("dwCommonButtons=%x uType=%x ret=%x\n", pTaskConfig->dwCommonButtons, uType, ret);
-
-    if (pnButton) *pnButton = ret;
-    if (pnRadioButton) *pnRadioButton = pTaskConfig->nDefaultButton;
-    if (pfVerificationFlagChecked) *pfVerificationFlagChecked = TRUE;
+    if (!pTaskConfig || pTaskConfig->cbSize != sizeof(TASKDIALOGCONFIG))
+        return E_INVALIDARG;
+
+    list_init(&controls);
+
+    /* Start creating controls */
+
+    controls_add(&controls, IDOK, WC_BUTTONW, text_ok, WS_CHILD | WS_VISIBLE, 105, 85, 40, 10);
+
+    header.title = pTaskConfig->pszWindowTitle;
+    if (!header.title)
+        header.title = empty_string; /* FIXME: Set to exe path instead */
+    header.title_size = (lstrlenW(header.title) + 1) * sizeof(WCHAR);
+
+    header.template.style = DS_MODALFRAME | WS_CAPTION | WS_VISIBLE;
+    header.template.cdit = list_count(&controls);
+    header.template.x = 10;
+    header.template.y = 10;
+    header.template.cx = 150;
+    header.template.cy = 100;
+
+    /* Turn template information into a dialog template to display it */
+    template_data = dialog_template_create(header, &controls);
+
+    DialogBoxIndirectParamW(pTaskConfig->hInstance, template_data, pTaskConfig->hwndParent, DialogProc, 0);
+
+    /* Cleanup */
+
+    Free(template_data);
+    controls_destroy(&controls);
+
     return S_OK;
 }
-- 
2.12.0




More information about the wine-patches mailing list