From: Joachim Priesner Subject: [PATCH 2/3] comctl32: Basic implementation of TaskDialogIndirect (try 2) Message-Id: <201410021320.20569.joachim.priesner@web.de> Date: Thu, 2 Oct 2014 13:20:19 +0200 This is part of an effort to replace the current TaskDialogIndirect stub (which uses MessageBoxW) with an own implementation. In this patch, the actual basic implementation of TaskDialogIndirect is provided, which for now displays the main instruction and content text, as well as the icon. Try 2: Incorporated fixes for the things pointed out by Nikolay Sivov (thanks!). Also removed the sunken line between task dialog content and buttons as it is not present in Windows 7 with classic style (which is basically the style Wine is using). Tested on openSuse 13.1. --- dlls/comctl32/Makefile.in | 2 +- dlls/comctl32/comctl32.h | 7 + dlls/comctl32/comctl32.rc | 15 ++ dlls/comctl32/taskdialog.c | 494 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 486 insertions(+), 32 deletions(-) diff --git a/dlls/comctl32/Makefile.in b/dlls/comctl32/Makefile.in index 9718f4b..728b85c 100644 --- a/dlls/comctl32/Makefile.in +++ b/dlls/comctl32/Makefile.in @@ -1,7 +1,7 @@ EXTRADEFS = -D_COMCTL32_ MODULE = comctl32.dll IMPORTLIB = comctl32 -IMPORTS = uuid user32 gdi32 advapi32 +IMPORTS = uuid user32 gdi32 advapi32 shlwapi DELAYIMPORTS = winmm uxtheme C_SRCS = \ diff --git a/dlls/comctl32/comctl32.h b/dlls/comctl32/comctl32.h index b9b0574..2bc3507 100644 --- a/dlls/comctl32/comctl32.h +++ b/dlls/comctl32/comctl32.h @@ -65,6 +65,13 @@ extern HBRUSH COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN; #define IDS_SEPARATOR 1024 +/* Task dialog */ +#define IDD_TASKDIALOG 510 + +#define IDC_MAIN_ICON 511 +#define IDC_MAIN_INSTRUCTION 512 +#define IDC_CONTENT 513 + /* Toolbar imagelist bitmaps */ #define IDB_STD_SMALL 120 #define IDB_STD_LARGE 121 diff --git a/dlls/comctl32/comctl32.rc b/dlls/comctl32/comctl32.rc index 87e2fd1..0450a36 100644 --- a/dlls/comctl32/comctl32.rc +++ b/dlls/comctl32/comctl32.rc @@ -93,6 +93,21 @@ BEGIN LISTBOX IDC_TOOLBARBTN_LBOX, 194,17,120,100,LBS_NOTIFY | LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_DISABLENOSCROLL | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP END +IDD_TASKDIALOG DIALOG 100, 80, 216, 168 +STYLE DS_MODALFRAME | DS_NOIDLEMSG | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +BEGIN + ICON "", IDC_MAIN_ICON, 8, 20, 16, 16, WS_CHILD | WS_VISIBLE + LTEXT "", IDC_MAIN_INSTRUCTION, 32, 4, 176, 48, WS_CHILD | WS_VISIBLE | WS_GROUP | SS_NOPREFIX + LTEXT "", IDC_CONTENT, 32, 4, 176, 48, WS_CHILD | WS_VISIBLE | WS_GROUP | SS_NOPREFIX + PUSHBUTTON "OK", IDOK, 16, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&Yes", IDYES, 306, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&No", IDNO, 364, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 74, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&Retry", IDRETRY, 190, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&Close", IDCLOSE, 132, 56, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #define WINE_FILEDESCRIPTION_STR "Wine Common Controls" diff --git a/dlls/comctl32/taskdialog.c b/dlls/comctl32/taskdialog.c index 760a984..38b70e5 100644 --- a/dlls/comctl32/taskdialog.c +++ b/dlls/comctl32/taskdialog.c @@ -17,46 +17,478 @@ */ #include "comctl32.h" -#include "winuser.h" +#include "shlwapi.h" + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winternl.h" +#include "dlgs.h" #include "wine/debug.h" +#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(commctrl); +#define TASKDIALOG_SPACING 8 + +#define IMAGERES_QUESTION_ICON MAKEINTRESOURCEW(0x63) +#define IMAGERES_ERROR_ICON MAKEINTRESOURCEW(0x62) +#define IMAGERES_WARNING_ICON MAKEINTRESOURCEW(0x54) +#define IMAGERES_INFORMATION_ICON MAKEINTRESOURCEW(0x51) + +/* Retrieves an icon for the task dialog from the specified instance and icon identifier. */ +HICON TASKDIALOG_GetIcon(HINSTANCE instance, PCWSTR icon) +{ + /* If instance is NULL and icon is not one if the TD_*_ICON constants, the icon is taken + * from the system's image resources (imageres.dll). This is documented in the MSDN + * documentation for the TDM_UPDATE_ICON message. Until imageres.dll is implemented, + * hard-code some of the offsets for standard icons in imageres.dll so at least some + * of the icons are displayed correctly. */ + if (icon == TD_ERROR_ICON || (instance == NULL && icon == IMAGERES_ERROR_ICON)) + return LoadIconW(0, (LPWSTR)IDI_ERROR); + else if (icon == TD_WARNING_ICON || (instance == NULL && icon == IMAGERES_WARNING_ICON)) + return LoadIconW(0, (LPWSTR)IDI_WARNING); + else if (icon == TD_INFORMATION_ICON || (instance == NULL && icon == IMAGERES_INFORMATION_ICON)) + return LoadIconW(0, (LPWSTR)IDI_INFORMATION); + else if (icon == TD_SHIELD_ICON) + return LoadIconW(0, (LPWSTR)IDI_SHIELD); + else if (instance == NULL && icon == IMAGERES_QUESTION_ICON) + return LoadIconW(0, (LPWSTR)IDI_QUESTION); + else if (instance) + return LoadIconW(instance, icon); + return 0; +} + +/* + * TaskDialog layout (not everything implemented yet): + * + * +------------------------------------------------------------+ + * | Window title (fallback: executable file name) X | + * +------------------------------------------------------------+ + * | _THE Main instruction | + * | MAIN | + * | ICON Content | + * | | + * | Expanded information if TDF_EXPAND_FOOTER AREA | + * | is not set. | + * | | + * | [Progress Bar or Marquee Progress Bar_____________] | + * | | + * | (*) Radio button 1 | + * | ( ) Radio button 2 | + * | | + * | => User buttons if TDF_USE_COMMAND_LINKS or | + * | TDF_USE_COMMAND_LINKS_NOICON is set | + * | | + * | => In this case, the user buttons don't appear | + * | below. | + * | | + * +============================================================+ + * | (V) Expand/collapse [User buttons] [Common buttons] | + * | [ ] Verification checkbox | + * +============================================================+ + * | ICON Footer text | + * | which can span multiple lines. | + * +============================================================+ + * | Expanded information if TDF_EXPAND_FOOTER_AREA is set | + * +------------------------------------------------------------+ + */ +static void TASKDIALOG_OnInit(HWND hwnd, TASKDIALOGCONFIG *config) +{ + HFONT hNormalFont, hMainInstructionFont; + LOGFONTW mainInstructionFontAttributes = {0}; + HWND hItem, hDefaultButton = 0; + HICON hIconMain = 0; + HDC hdc; + BOOL hasButton = 0; + int i, nButtons, currentX, currentY; + int buttonWidth, buttonAreaWidth, buttonHeight; + int windowClientWidth, windowClientHeight, windowLeft, windowTop; + int windowBorderHeight, windowBorderWidth; + int mainIconHeight, mainIconLeft, mainIconWidth; + int mainAreaTextLeft, mainAreaTextWidth; + int contentTextWidth, contentTextHeight; + int mainInstructionWidth, mainInstructionHeight; + HMONITOR monitor = 0; + MONITORINFO mon_info; + LPCWSTR lpszContent, lpszMainInstruction; + WCHAR *windowTitleBuffer = NULL, *contentTextBuffer = NULL, *mainInstructionBuffer = NULL; + const WCHAR *ptr; + RECT rect; + static const int commonButtons[6] = { IDOK, IDYES, IDNO, IDCANCEL, IDRETRY, IDCLOSE }; + + /* Get the text to display. */ + TRACE("pszWindowTitle=%s\n", debugstr_w(config->pszWindowTitle)); + if (IS_INTRESOURCE(config->pszWindowTitle)) + { + UINT len = LoadStringW(config->hInstance, LOWORD(config->pszWindowTitle), (LPWSTR)&ptr, 0); + if (len) + { + windowTitleBuffer = Alloc((len + 1) * sizeof(WCHAR)); + if (!windowTitleBuffer) + { + EndDialog(hwnd, E_OUTOFMEMORY); + return; + } + memcpy(windowTitleBuffer, ptr, len * sizeof(WCHAR)); + windowTitleBuffer[len] = 0; + SetWindowTextW(hwnd, windowTitleBuffer); + } + /* If the supplied window title is invalid, display the executable file name instead. */ + else + { + windowTitleBuffer = Alloc((MAX_PATH + 1) * sizeof(WCHAR)); + if (!windowTitleBuffer) + { + EndDialog(hwnd, E_OUTOFMEMORY); + return; + } + GetModuleFileNameW(NULL, windowTitleBuffer, MAX_PATH); + windowTitleBuffer[MAX_PATH] = 0; + SetWindowTextW(hwnd, PathFindFileNameW(windowTitleBuffer)); + } + Free(windowTitleBuffer); + windowTitleBuffer = NULL; + } + else SetWindowTextW(hwnd, config->pszWindowTitle); + + TRACE("pszMainInstruction=%s\n", debugstr_w(config->pszMainInstruction)); + if (IS_INTRESOURCE(config->pszMainInstruction)) + { + UINT len = LoadStringW(config->hInstance, LOWORD(config->pszMainInstruction), (LPWSTR)&ptr, 0); + mainInstructionBuffer = Alloc((len + 1) * sizeof(WCHAR)); + if (!mainInstructionBuffer) + { + EndDialog(hwnd, E_OUTOFMEMORY); + return; + } + memcpy(mainInstructionBuffer, ptr, len * sizeof(WCHAR)); + mainInstructionBuffer[len] = 0; + lpszMainInstruction = mainInstructionBuffer; + } + else lpszMainInstruction = config->pszMainInstruction; + SetWindowTextW(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), lpszMainInstruction); + + TRACE("pszContent=%s\n", debugstr_w(config->pszContent)); + if (IS_INTRESOURCE(config->pszContent)) + { + UINT len = LoadStringW(config->hInstance, LOWORD(config->pszContent), (LPWSTR)&ptr, 0); + contentTextBuffer = Alloc((len + 1) * sizeof(WCHAR)); + if (!contentTextBuffer) + { + EndDialog(hwnd, E_OUTOFMEMORY); + return; + } + memcpy(contentTextBuffer, ptr, len * sizeof(WCHAR)); + contentTextBuffer[len] = 0; + lpszContent = contentTextBuffer; + } + else lpszContent = config->pszContent; + SetWindowTextW(GetDlgItem(hwnd, IDC_CONTENT), lpszContent); + + if (config->pszFooter) + { + FIXME("pszFooter=%s\n", debugstr_w(config->pszFooter)); + } + if (config->pszVerificationText) + { + FIXME("pszVerificationText=%s\n", debugstr_w(config->pszVerificationText)); + } + if (config->pszExpandedInformation) + { + FIXME("pszExpandedInformation=%s\n", debugstr_w(config->pszExpandedInformation)); + } + if (config->pszExpandedControlText) + { + FIXME("pszExpandedControlText=%s\n", debugstr_w(config->pszExpandedControlText)); + } + if (config->pszCollapsedControlText) + { + FIXME("pszCollapsedControlText=%s\n", debugstr_w(config->pszCollapsedControlText)); + } + + /* Destroy unused common buttons. */ + TRACE("dwCommonButtons=%x\n", config->dwCommonButtons); + if (!(config->dwCommonButtons & TDCBF_YES_BUTTON)) + DestroyWindow(GetDlgItem(hwnd, IDYES)); + else hasButton = 1; + if (!(config->dwCommonButtons & TDCBF_NO_BUTTON)) + DestroyWindow(GetDlgItem(hwnd, IDNO)); + else hasButton = 1; + if (!(config->dwCommonButtons & TDCBF_CANCEL_BUTTON)) + DestroyWindow(GetDlgItem(hwnd, IDCANCEL)); + else hasButton = 1; + if (!(config->dwCommonButtons & TDCBF_RETRY_BUTTON)) + DestroyWindow(GetDlgItem(hwnd, IDRETRY)); + else hasButton = 1; + if (!(config->dwCommonButtons & TDCBF_CLOSE_BUTTON)) + DestroyWindow(GetDlgItem(hwnd, IDCLOSE)); + else hasButton = 1; + + /* Set user-defined buttons. */ + if (config->cButtons) + FIXME("Custom task dialog buttons not implemented.\n"); + if (config->cRadioButtons) + FIXME("Task dialog radio buttons not implemented.\n"); + + /* If no other buttons are specified, the OK button remains. */ + if (!(config->dwCommonButtons & TDCBF_OK_BUTTON) && hasButton) + DestroyWindow(GetDlgItem(hwnd, IDOK)); + + /* Get the main icon. */ + if (config->dwFlags & TDF_USE_HICON_MAIN) + hIconMain = config->hMainIcon; + else + hIconMain = TASKDIALOG_GetIcon(config->hInstance, config->pszMainIcon); + + /* Position everything. */ + GetWindowRect(hwnd, &rect); + windowBorderHeight = rect.bottom - rect.top; + windowBorderWidth = rect.right - rect.left; + GetClientRect(hwnd, &rect); + windowBorderHeight -= rect.bottom - rect.top; + windowBorderWidth -= rect.right - rect.left; + + /* Get the main icon size. */ + if (hIconMain) { + SendDlgItemMessageW(hwnd, IDC_MAIN_ICON, STM_SETICON, (WPARAM)hIconMain, 0); + GetWindowRect(GetDlgItem(hwnd, IDC_MAIN_ICON), &rect); + MapWindowPoints(0, hwnd, (LPPOINT)&rect, 2); + mainIconHeight = rect.bottom - rect.top; + mainIconLeft = TASKDIALOG_SPACING; + mainIconWidth = rect.right - rect.left; + } else { + mainIconHeight = 0; + mainIconLeft = 0; + mainIconWidth = 0; + } + + /* Set a bold font for the main instruction. */ + hdc = GetDC(hwnd); + hNormalFont = SelectObject(hdc, (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0)); + + GetObjectW(hNormalFont, sizeof(mainInstructionFontAttributes), &mainInstructionFontAttributes); + mainInstructionFontAttributes.lfWeight = FW_BOLD; + hMainInstructionFont = CreateFontIndirectW(&mainInstructionFontAttributes); + SendMessageW(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), WM_SETFONT, (WPARAM)hMainInstructionFont, 0); + + /* Get the number of visible buttons and their size. */ + buttonHeight = buttonWidth = 1; + nButtons = 0; + for (i = 0; i < (sizeof(commonButtons) / sizeof(commonButtons[0])); i++) + { + hItem = GetDlgItem(hwnd, commonButtons[i]); + if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE) + { + WCHAR buttonText[1024]; + int w, h; + nButtons++; + if (GetWindowTextW(hItem, buttonText, 1024)) + { + DrawTextW(hdc, buttonText, -1, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT); + h = rect.bottom - rect.top; + w = rect.right - rect.left; + if (h > buttonHeight) buttonHeight = h; + if (w > buttonWidth) buttonWidth = w ; + } + } + } + + /* Give the buttons some white space. */ + buttonHeight *= 2; + buttonWidth = max(buttonWidth * 2, buttonHeight); + buttonAreaWidth = (buttonWidth + TASKDIALOG_SPACING) * nButtons - TASKDIALOG_SPACING; + + mainAreaTextLeft = TASKDIALOG_SPACING; + if (mainIconWidth) mainAreaTextLeft += mainIconWidth + TASKDIALOG_SPACING; + + /* Get the size of the texts in the main area. + * Minimum text width corresponds to space for the buttons. */ + GetClientRect(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), &rect); + rect.top = rect.left = rect.bottom = 0; + rect.right = max(rect.right, buttonAreaWidth); + SelectObject(hdc, hMainInstructionFont); + DrawTextW(hdc, lpszMainInstruction, -1, &rect, + DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT); + mainInstructionWidth = rect.right; + mainInstructionHeight = rect.bottom; + + GetClientRect(GetDlgItem(hwnd, IDC_CONTENT), &rect); + rect.top = rect.left = rect.bottom = 0; + rect.right = max(rect.right, buttonAreaWidth); + SelectObject(hdc, hNormalFont); + DrawTextW(hdc, lpszContent, -1, &rect, + DT_LEFT | DT_EXPANDTABS | DT_WORDBREAK | DT_CALCRECT); + contentTextWidth = rect.right; + contentTextHeight = rect.bottom; + + ReleaseDC(hwnd, hdc); + + mainAreaTextWidth = max(mainInstructionWidth, contentTextWidth); + windowClientWidth = max(TASKDIALOG_SPACING + buttonAreaWidth, + mainAreaTextLeft + mainAreaTextWidth) + TASKDIALOG_SPACING; + if (config->cxWidth) + FIXME("Ignoring config->cxWidth.\n"); + + /* Position everything from top to bottom. */ + currentY = TASKDIALOG_SPACING; + + /* Position the main icon. */ + if (hIconMain) + SetWindowPos(GetDlgItem(hwnd, IDC_MAIN_ICON), 0, mainIconLeft, currentY, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + else DestroyWindow(GetDlgItem(hwnd, IDC_MAIN_ICON)); + + /* Position the texts in the main area. */ + if (mainInstructionHeight) + { + SetWindowPos(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION), 0, mainAreaTextLeft, currentY, + mainInstructionWidth, mainInstructionHeight, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + currentY += mainInstructionHeight + TASKDIALOG_SPACING; + } + else DestroyWindow(GetDlgItem(hwnd, IDC_MAIN_INSTRUCTION)); + + if (contentTextHeight) + { + SetWindowPos(GetDlgItem(hwnd, IDC_CONTENT), 0, mainAreaTextLeft, currentY, + contentTextWidth, contentTextHeight, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + currentY += contentTextHeight + TASKDIALOG_SPACING; + } + else DestroyWindow(GetDlgItem(hwnd, IDC_CONTENT)); + + if (mainIconHeight) + currentY = max(currentY, TASKDIALOG_SPACING + mainIconHeight + TASKDIALOG_SPACING); + + /* Position the buttons: right-aligned */ + currentX = windowClientWidth - TASKDIALOG_SPACING - buttonAreaWidth; + for (i = 0; i < (sizeof(commonButtons) / sizeof(commonButtons[0])); i++) + { + hItem = GetDlgItem(hwnd, commonButtons[i]); + if (GetWindowLongW(hItem, GWL_STYLE) & WS_VISIBLE) + { + if (hDefaultButton == 0 || config->nDefaultButton == commonButtons[i]) + hDefaultButton = hItem; + SetWindowPos(hItem, 0, currentX, currentY, buttonWidth, buttonHeight, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + currentX += buttonWidth + TASKDIALOG_SPACING; + } + } + + if (hDefaultButton) + { + SetFocus(hDefaultButton); + SendMessageW(hDefaultButton, BM_SETSTYLE, BS_DEFPUSHBUTTON, TRUE); + } + + currentY += buttonHeight + TASKDIALOG_SPACING; + windowClientHeight = currentY; + + /* Query parent window/desktop size and center window. */ + monitor = MonitorFromWindow(config->hwndParent ? config->hwndParent : GetActiveWindow(), + MONITOR_DEFAULTTOPRIMARY); + mon_info.cbSize = sizeof(mon_info); + GetMonitorInfoW(monitor, &mon_info); + windowLeft = (mon_info.rcWork.left + mon_info.rcWork.right + - (windowClientWidth + windowBorderWidth)) / 2; + windowTop = (mon_info.rcWork.top + mon_info.rcWork.bottom + - (windowClientHeight + windowBorderHeight)) / 2; + SetWindowPos(hwnd, 0, windowLeft, windowTop, + windowClientWidth + windowBorderWidth, windowClientHeight + windowBorderHeight, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW); + + DeleteObject(hMainInstructionFont); + Free(contentTextBuffer); + Free(mainInstructionBuffer); +} + + +/************************************************************************** + * TASKDIALOG_DlgProc + * + * Dialog procedure for task dialogs. + */ +static INT_PTR CALLBACK TASKDIALOG_DlgProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + switch(message) { + case WM_INITDIALOG: + { + TASKDIALOGCONFIG *config = (TASKDIALOGCONFIG*)lParam; + TASKDIALOG_OnInit(hwnd, config); + break; + } + + case WM_COMMAND: + { + if (LOWORD(wParam) == IDCANCEL) + /* IDCANCEL button must be present or TDF_ALLOW_DIALOG_CANCELLATION flag set. */ + FIXME("Not checking if dialog cancellation is allowed\n"); + EndDialog(hwnd, wParam); + break; + } + + default: + /* Ignore all the other messages. */ + TRACE("Message number 0x%04x is being ignored.\n", message); + break; + } + return 0; +} + /*********************************************************************** * TaskDialogIndirect [COMCTL32.@] */ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked) { - UINT uType = 0; - INT ret; - FIXME("%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; + LPVOID template; + HRSRC hRes; + INT_PTR ret; + + if (!pTaskConfig) { + TRACE("pTaskConfig = NULL\n"); + return E_INVALIDARG; + } + + TRACE("hWndParent=%p, hInstance=%p, dwCommonButtons=%x, pnButton=%p\n", + pTaskConfig->hwndParent, pTaskConfig->hInstance, pTaskConfig->dwCommonButtons, + pnButton); + + if (!(hRes = FindResourceExW(COMCTL32_hModule, (LPWSTR)RT_DIALOG, + MAKEINTRESOURCEW(IDD_TASKDIALOG), LANG_NEUTRAL))) + { + ERR("Cannot find TASKDIALOG resource\n"); + return E_FAIL; + } + if (!(template = LoadResource(COMCTL32_hModule, hRes))) + { + ERR("Cannot load TASKDIALOG resource\n"); + return E_FAIL; + } + + ret = DialogBoxIndirectParamW(pTaskConfig->hInstance, template, + pTaskConfig->hwndParent, TASKDIALOG_DlgProc, (LPARAM)pTaskConfig); + if (pnRadioButton) + { + FIXME("Task dialog radio buttons not implemented.\n"); + *pnRadioButton = pTaskConfig->nDefaultButton; + } + if (pfVerificationFlagChecked) + { + FIXME("Task dialog verification check box not implemented.\n"); + *pfVerificationFlagChecked = TRUE; + } + if (ret > 0) + { + if (pnButton) *pnButton = ret; + return S_OK; + } 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; - return S_OK; + { + if (pnButton) *pnButton = 0; + /* DialogBoxIndirect returns -1 on failure. */ + return ret == -1 ? E_FAIL : ret; + } } -- 1.8.4.5