Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WTL

Vista Goodies in C++: Showing Friendly Messages with Task Dialogs

4.87/5 (21 votes)
29 Dec 20067 min read 1   763  
Using the Vista TaskDialog API as a replacement for MessageBox

Contents

Introduction

In this Vista Goodies article, I will cover the new TaskDialog() API, which is designed to be a more user-friendly replacement for MessageBox(). TaskDialog() not only shows a nicer-looking UI, but it's also easier for the developer since it handles details like automatic loading of string resources.

This article is written for the RTM version of Vista, using Visual Studio 2005, WTL 7.5, and the Windows SDK. See the introduction in the first Vista Goodies article for more information on where you can download those components.

While it's always been easy to show simple messages using MessageBox(), it can be cumbersome to use at time, especially if your app is written with internationalization in mind and your UI strings are in a resource DLL: MessageBox() has no provision for loading string resources, unlike other APIs like DialogBox() which can be passed a resource ID. There is also the drawback that a message box can show only predefined sets of buttons; if you wanted to have a Retry button but not an Ignore button, you'd be out of luck.

While these shortcomings were addressed by MessageBoxIndirect(), that API still has the restriction on which buttons can be displayed. TaskDialog() solves that problem and provides a much easier-to-use and more flexible API.

The TaskDialog API

The TaskDialog() prototype is:

HRESULT TaskDialog ( HWND hWndParent, HINSTANCE hInstance,
                     PCWSTR pszWindowTitle, PCWSTR pszMainInstruction,
                     PCWSTR pszContent,
                     TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
                     PCWSTR pszIcon, int *pnButton );

The parameters are:

hWndParent
The window that will be the task dialog's parent. As with MessageBox(), this window is disabled while the task dialog is displayed. Pass NULL if you don't want the task dialog to have a parent (see this blog post by Raymond Chen on why passing GetDesktopWindow() is wrong).
hInstance
If any of the following parameters are the ID of a string or icon resource, set this parameter to the HINSTANCE of the module that contains the resources.
pszWindowTitle
The string to show in the task dialog's caption. This can either be a zero-terminated Unicode string or a string resource ID. When passing a resource ID, use the MAKEINTRESOURCE macro on the ID. (If you are building for the ANSI character set, use the Unicode macro MAKEINTRESOURCEW explicitly instead of MAKEINTRESOURCE.)
pszMainInstruction
The string to show at the top of the task dialog, beside the icon. This text is shown in a larger font and in a different color, making it appear like a heading in a document. As with pszWindowTitle, this parameter can be a C-style string or a string resource ID.
pszContent
The string to show in the body of the task dialog. This parameter can also be a C-style string or a string resource ID.
dwCommonButtons
A set of flags indicating which buttons to show in the dialog. To have multiple buttons, use the logical-or operator to combine the flags for the buttons you want. The possible flags are: TDCBF_OK_BUTTON, TDCBF_YES_BUTTON, TDCBF_NO_BUTTON, TDCBF_CANCEL_BUTTON, TDCBF_RETRY_BUTTON, TDCBF_CLOSE_BUTTON.

There is no restriction on which buttons can appear together; you can set as many flags in this parameter as you wish. If you pass 0, the default behavior is to just have an OK button. Note that the dialog will not be closeable using the Esc or Alt+F4 keys unless TDCBF_CANCEL_BUTTON is passed.

pszIcon
The resource ID of an icon to show in the task dialog. As with the string parameters, you must use the MAKEINTRESOURCE or MAKEINTRESOURCEW macro on the ID. You can also pass one of these predefined values to use a system icon: TD_ERROR_ICON, TD_WARNING_ICON, TD_INFORMATION_ICON, TD_SHIELD_ICON. TD_SHIELD_ICON displays the UAC shield (Image 1) and should only be used to indicate a situation where the user will need to elevate to perform an administrative task. You can also pass NULL if you don't want any icon at all.
pnButton
A pointer to an int that is set to one of the following values to indicate which button the user clicks: IDOK, IDYES, IDNO, IDCANCEL, IDRETRY, IDCLOSE. If you pass TDCBF_CANCEL_BUTTON in dwCommonButtons, then *pnButton will be set to IDCANCEL if the user presses Esc or Alt+F4. If TaskDialog() fails, the value will be set to 0. You can pass NULL for this parameter if you don't need to know which button was clicked.

The return value from TaskDialog() is an HRESULT that indicates whether the function succeeds. It can fail if it is unable to load a resource, or in low-memory conditions.

If pszWindowTitle is NULL, the executable's filename is used for the caption text. pszMainInstruction or pszContent may be NULL if you don't want to have that piece of text in the dialog. Also, pszMainInstruction and pszContent can be multi-line strings; put a newline character ('\n') in the string to indicate a line break.

Note that the string parameters are always Unicode strings. As with many APIs added in XP and Vista, there is no version that accepts ANSI strings.

Examples using TaskDialog

Using TaskDialog with C-Style Strings

Let's start with an example that just uses hard-coded strings. The context in which we're showing this message is that our app has determined that an update is available, and we are asking the user if he wants to get the update now.

void CTheApp::OnUpdateAvailable()
{
HRESULT hr;
int nClickedButton;
LPCWSTR szTitle = L"Mike's AntiFluff Scanner",
  szHeader = L"An update for Mike's AntiFluff Scanner is available",
  szBodyText = L"Version 2007.1 of Mike's AntiFluff Scanner has been released." \
               L"Do you want to download the update now?";
 
  hr = TaskDialog ( m_hWnd, NULL,
                    szTitle, szHeader, szBodyText,
                    TDCBF_YES_BUTTON|TDCBF_NO_BUTTON,
                    NULL, &nClickedButton );
 
  if ( SUCCEEDED(hr) && IDYES == nClickedButton )
    {
    // download the update...
    }
}

Here's the task dialog, showing the strings and buttons that we passed in:

Image 2

We can also add an information icon with the pszIcon parameter:

hr = TaskDialog ( m_hWnd, NULL,
                  szTitle, szHeader, szBodyText,
                  TDCBF_YES_BUTTON|TDCBF_NO_BUTTON,
                  TD_INFORMATION_ICON, &nClickedButton );

Image 3

Using TaskDialog with Resources

Now let's see how to use TaskDialog() when the strings and icon are in a resource DLL. We will need the HINSTANCE of the DLL that contains the resources. For this example, let's assume that the DLL has already been loaded, so all we need to do here is retrieve that handle. The way of doing this varies among class libraries; here are the functions you can call in ATL, WTL, and MFC:

  • ATL in VC 6 and WTL using a global CAppModule variable: _Module.GetResourceInstance()
  • ATL in VC 7 and later: _AtlBaseModule.GetResourceInstance()
  • MFC in VC 6: AfxGetResourceHandle()
  • MFC in VC 7 and later: AfxGetResourceHandle() and AfxFindResourceHandle()

First, let's replace the hard-coded strings with resource IDs:

HINSTANCE hinst = _Module.GetResourceInstance();  // this is the WTL method
 
  hr = TaskDialog ( m_hWnd, hinst,
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_TITLE),
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_HEADER),
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_BODY),
                    TDCBF_YES_BUTTON|TDCBF_NO_BUTTON,
                    TD_INFORMATION_ICON, &nClickedButton );

And finally, let's add our own icon to the dialog:

HINSTANCE hinst = _Module.GetResourceInstance();  // this is the WTL method
 
  hr = TaskDialog ( m_hWnd, hinst,
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_TITLE),
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_HEADER),
                    MAKEINTRESOURCE(IDS_SAMPLEDLG_BODY),
                    TDCBF_YES_BUTTON|TDCBF_NO_BUTTON,
                    MAKEINTRESOURCE(IDI_TD_SAMPLE_ICON),
                    &nClickedButton );

Here's the final dialog with our custom icon:

Image 4

That's all there is to it! While TaskDialog() is a snap to use, the dialog is still not much more complex than a message box. In the next article, I'll cover TaskDialogIndirect(), which has a lot more power and many more UI features.

Using the Sample App

The demo project for this article is a task dialog builder. You can enter the text you want to show in the caption, header, and body areas, along with the buttons and an optional icon:

Image 5

The app then calls TaskDialog() with those parameters, so you can see the dialog in action:

Image 6

Further Reading

Copyright and License

This article is copyrighted material, ©2006 by Michael Dunn. I realize this isn't going to stop people from copying it all around the 'net, but I have to say it anyway. If you are interested in doing a translation of this article, please email me to let me know. I don't foresee denying anyone permission to do a translation, I would just like to be aware of the translation so I can post a link to it here.

The demo code that accompanies this article is released to the public domain. I release it this way so that the code can benefit everyone. (I don't make the article itself public domain because having the article available only on CodeProject helps both my own visibility and the CodeProject site.) If you use the demo code in your own application, an email letting me know would be appreciated (just to satisfy my curiosity about whether folks are benefiting from my code) but is not required. Attribution in your own source code is also appreciated but not required.

Revision History

  • December 12, 2006: Article first published.
  • December 26, 2006: No content changes, just updated series navigation links.

Series navigation: « Using the New Vista File Dialogs | Using TaskDialogIndirect to Build Dialogs that Get User Input »

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here