[03/18] comctl32: Added basic implementation for task dialogs and add tests

Fabian Maurer dark.shadow4 at web.de
Fri Feb 24 14:03:57 CST 2017


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

Signed-off-by: Fabian Maurer <dark.shadow4 at web.de>
---
 dlls/comctl32/taskdialog.c       | 212 +++++++++++++++++++++++++++++++++------
 dlls/comctl32/tests/taskdialog.c |  40 +++++++-
 2 files changed, 223 insertions(+), 29 deletions(-)

diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c
index c26d53bac9..d31e7d0b95 100644
--- a/dlls/comctl32/taskdialog.c
+++ b/dlls/comctl32/taskdialog.c
@@ -28,45 +28,201 @@
 #include "commctrl.h"
 #include "winerror.h"
 #include "comctl32.h"
+
+#include "wine/list.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(commctrl);
 
+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 titleSize; /* Length in bytes including null-terminator */
+}dialog_header;
+
+#define MEMCPY_MOVEPTR(target, source, size) memcpy(target, source, size); target += size;
+#define STR_SIZE(str) ((lstrlenW(str) + 1) * sizeof(WCHAR))
+
+static void* align_word(void *ptr)
+{
+    return (void *)(((UINT_PTR)ptr + 1 ) & ~1);
+}
+
+static void* align_dword(void *ptr)
+{
+    return (void *)(((UINT_PTR)ptr + 3 ) & ~3);
+}
+
+/* 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 */
+LPDLGTEMPLATEW dialog_template_create(dialog_header header, struct list *controls)
+{
+    control_info *control;
+    void *template_data_start;
+    char *template_data;
+    int data_size = sizeof(WORD); /* Alignment at WORD boundaries is needed for strings, allocate a bit of padding */
+
+    data_size += sizeof(DLGTEMPLATE);
+    data_size += sizeof(WORD)*2 + header.titleSize;
+    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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data_size);
+    template_data = template_data_start;
+
+    /* Align on WORD boundary for the strings */
+    template_data = align_word(template_data);
+
+    /* 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.titleSize);
+
+    /* Copy dialog member data */
+    LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+    {
+        /* Align on DWORD boundary for each new control */
+        template_data = align_dword(template_data);
+
+        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;
+}
+
+static void dialog_template_destroy(LPDLGTEMPLATEW template_data)
+{
+    HeapFree(GetProcessHeap(), 0, template_data);
+}
+
+/* Functions to handle the dialog control list */
+
+static void controls_init(struct list **controls)
+{
+    *controls = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct list));
+    list_init(*controls);
+}
+
+static void controls_destroy(struct list *controls)
+{
+    control_info *control;
+    LIST_FOR_EACH_ENTRY(control, controls, control_info, entry)
+    {
+        HeapFree(GetProcessHeap(), 0, control);
+    }
+    HeapFree(GetProcessHeap(), 0, controls);
+}
+
+/* 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 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 = STR_SIZE(class);
+    data->text = text;
+    data->text_size = STR_SIZE(text);
+
+    list_add_tail(controls, &data->entry);
+}
+
+static INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (uMsg)
+    {
+        case WM_COMMAND:
+            if(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
+            {
+                EndDialog(hwndDlg, 0);
+                return TRUE;
+            }
+            break;
+    }
+    return FALSE;
+}
+
 /***********************************************************************
  * TaskDialogIndirect [COMCTL32.@]
  */
 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 class_button[] = {'B','u','t','t','o','n',0};
+    static const WCHAR text_ok[] = {'O','K',0};
+    LPDLGTEMPLATEW 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;
+
+    controls_init(&controls);
+
+    /* Start creating controls */
+
+    controls_add(controls, IDOK, class_button, 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.titleSize = STR_SIZE(header.title);
+
+    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 */
+
+    dialog_template_destroy(template_data);
+    controls_destroy(controls);
+
     return S_OK;
 }
diff --git a/dlls/comctl32/tests/taskdialog.c b/dlls/comctl32/tests/taskdialog.c
index 332fa23db0..54c75ed061 100644
--- a/dlls/comctl32/tests/taskdialog.c
+++ b/dlls/comctl32/tests/taskdialog.c
@@ -29,6 +29,40 @@
 
 static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *);
 
+static HRESULT CALLBACK TaskDialogCallbackProc(HWND hwnd, UINT uNotification, WPARAM wParam,
+                                               LPARAM lParam, LONG_PTR dwRefData)
+{
+    if(uNotification == TDN_CREATED)
+    {
+        PostMessageW(hwnd, WM_KEYDOWN, VK_RETURN, 0);
+    }
+    return S_OK;
+}
+
+static void test_TaskDialogIndirect(void)
+{
+    TASKDIALOGCONFIG info = {0};
+    HRESULT ret;
+
+    ret = pTaskDialogIndirect(NULL, NULL, NULL, NULL);
+    ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+    ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+    ok(ret == E_INVALIDARG, "Expected E_INVALIDARG, got %x\n", ret);
+
+    info.cbSize = sizeof(TASKDIALOGCONFIG);
+    info.pfCallback = TaskDialogCallbackProc;
+
+    /* Skip this test on wine, because it doesn't really fail,
+     * it would displays a dialog that doesn't automatically close */
+    if (strcmp(winetest_platform, "wine"))
+    {
+        ret = pTaskDialogIndirect(&info, NULL, NULL, NULL);
+        ok(ret == S_OK, "Expected S_OK, got %x\n", ret);
+    }
+
+}
+
 START_TEST(taskdialog)
 {
     ULONG_PTR ctx_cookie;
@@ -46,7 +80,11 @@ START_TEST(taskdialog)
     ok(pTaskDialogIndirect != NULL, "TaskDialogIndirect not exported by name.\n");
 
     ptr_ordinal = GetProcAddress(hinst, (const CHAR*)345);
-    ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n", ptr_ordinal, pTaskDialogIndirect);
+    ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n",
+                                            ptr_ordinal, pTaskDialogIndirect);
+
+    if(pTaskDialogIndirect)
+        test_TaskDialogIndirect();
 
     unload_v6_module(ctx_cookie, hCtx);
 }
-- 
2.11.1




More information about the wine-patches mailing list