Modernize Win32 Message Box with Task Dialog

When I was developing NanaZip, I plan to modernize the implementation of Self Extracting Executables. Because Self Extracting Executables need to support older version of Windows, I plan to use Task Dialog to achieve the goal.

But I also think that I can use the same way from YY-Thunks, to make a wrapper for modernizing Win32 Message Box with Task Dialog for the existing Win32 apps without huge source code modification.

YY-Thunks uses the MSVC specific features to hack the specified IAT item of the target binary for redirecting to the needed fallback implementation. I think it’s an elegant way to modernize the existing projects.

I just made a simple implementation and provide in this article and hope it can be some help for developers.

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <CommCtrl.h>
#pragma comment(lib,"comctl32.lib")

EXTERN_C int WINAPI ModernMessageBoxW(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType)
{
if (uType != (uType & (MB_ICONMASK | MB_TYPEMASK)))
{
return ::MessageBoxW(hWnd, lpText, lpCaption, uType);
}

TASKDIALOGCONFIG TaskDialogConfig = { 0 };

TaskDialogConfig.cbSize = sizeof(TASKDIALOGCONFIG);
TaskDialogConfig.hwndParent = hWnd;
TaskDialogConfig.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION;
TaskDialogConfig.pszWindowTitle = lpCaption;
TaskDialogConfig.pszMainInstruction = lpText;

switch (uType & MB_TYPEMASK)
{
case MB_OK:
TaskDialogConfig.dwCommonButtons =
TDCBF_OK_BUTTON;
break;
case MB_OKCANCEL:
TaskDialogConfig.dwCommonButtons =
TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;
break;
case MB_YESNOCANCEL:
TaskDialogConfig.dwCommonButtons =
TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON;
break;
case MB_YESNO:
TaskDialogConfig.dwCommonButtons =
TDCBF_YES_BUTTON | TDCBF_NO_BUTTON;
break;
case MB_RETRYCANCEL:
TaskDialogConfig.dwCommonButtons =
TDCBF_RETRY_BUTTON | TDCBF_CANCEL_BUTTON;
break;
default:
return ::MessageBoxW(hWnd, lpText, lpCaption, uType);
}

switch (uType & MB_ICONMASK)
{
case MB_ICONHAND:
TaskDialogConfig.pszMainIcon = TD_ERROR_ICON;
break;
case MB_ICONQUESTION:
TaskDialogConfig.dwFlags |= TDF_USE_HICON_MAIN;
TaskDialogConfig.hMainIcon = ::LoadIconW(nullptr, IDI_QUESTION);
break;
case MB_ICONEXCLAMATION:
TaskDialogConfig.pszMainIcon = TD_WARNING_ICON;
break;
case MB_ICONASTERISK:
TaskDialogConfig.pszMainIcon = TD_INFORMATION_ICON;
break;
default:
break;
}

int ButtonID = 0;

HRESULT hr = ::TaskDialogIndirect(
&TaskDialogConfig,
&ButtonID,
nullptr,
nullptr);

if (ButtonID == 0)
{
::SetLastError(hr);
}

return ButtonID;
}

#if defined(_M_IX86)
#pragma warning(suppress:4483)
extern "C" __declspec(selectany) void const* const __identifier("_imp__MessageBoxW@16") = reinterpret_cast<void const*>(::ModernMessageBoxW);
#else
extern "C" __declspec(selectany) void const* const __imp_MessageBoxW = reinterpret_cast<void const*>(::ModernMessageBoxW);
#endif