Tuesday, September 26, 2006 11:33 AM bart

Using Windows Vista's TaskDialog API in managed code (C#)

Introduction

Windows Vista comes with a new (comctl32 v6) API called TaskDialog. In marketing speak, this API should be seen as a part of the "clarity" pillar of Windows Vista's mission statement. Where classic dialogs à la MessageBox are the old-fashioned way to interact with end-users, TaskDialogs offer a lot more functionality. In this first post on TaskDialog, I'm showing you how to create a MessageBox-equivalent TaskDialog in C# using interop.

Where do we want to go?

In the end we want to be capable to do a similar thing as the MessageBox.Show method in Windows Forms:

TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Yes | TaskDialogButtons.No | TaskDialogButtons.Cancel, TaskDialogIcon.Warning);

One first remark is the number of arguments (I'll supply overloads further on). Next to a title ("My application") and a text ("The quick brown fox jumps over the lazy dog.") there's a so-called instruction too ("Can I ask you a question?"). Wait a minute to see where this pops up in the displayed dialog. But first ... the code.

Let the fun begin: interop coding time

The TaskDialog signature can be found in CommCtrl.h in the Windows SDK folder (%programfiles%\Microsoft SDKs\Windows\v6.0\Include):

WINCOMMCTRLAPI HRESULT WINAPI TaskDialog(__in_opt HWND hwndParent, __in_opt HINSTANCE hInstance, __in_opt PCWSTR pszWindowTitle, __in_opt PCWSTR pszMainInstruction, __in_opt PCWSTR pszContent, TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons, __in_opt PCWSTR pszIcon, __out_opt int *pnButton);

Or in a more clean fashion:

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

This translates to the following in C#:

using System.Runtime.InteropServices;

public
class TaskDialog
{
   [
DllImport("comctl32.dll", CharSet = CharSet
.Unicode,  EntryPoint="TaskDialog")]
  
static extern int _TaskDialog(IntPtr hWndParent, IntPtr hInstance, String pszWindowTitle, String pszMainInstruction, String pszContent, int dwCommonButtons, IntPtr pszIcon, out int
pnButton);
}

Note: The name of the method was changed to _TaskDialog in order to make it compile, because the class itself is called TaskDialog too. Notice the parameter EntryPoint to the DllImportAttribute to make this name change work correctly.

The typical mapping of HWND to IntPtr should be no surprise. The same for HINSTANCE (which we won't use; it can be used to refer to a resource module to extract an icon, see pszIcon parameter). PCWSTRs map to System.String. The only surprise might be the mapping of PCWSTR pszIcon on IntPtr pszIcon. The reason for this is the definition of the icons in the CommCtrl.h file:

#define TD_WARNING_ICON         MAKEINTRESOURCEW(-1)
#define TD_ERROR_ICON           MAKEINTRESOURCEW(-2)
#define TD_INFORMATION_ICON     MAKEINTRESOURCEW(-3)
#define TD_SHIELD_ICON          MAKEINTRESOURCEW(-4)

MAKEINTRESOURCEW is a macro defined in WinUser.h:

#define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))

It's basically just a mapping to a pointer, therefore we use IntPtr.

Enough on this plumbing for now; time to do some useful work. We'll declare modal and non-modal overloads for a static Show method, just like the ones available on MessageBox. However, we'll start by defining the helper method that calls the imported TaskDialog method:

private static TaskDialogResult ShowInternal(IntPtr owner, string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon icon)
{
   int
p;
   if (_TaskDialog(owner, IntPtr.Zero, caption, instruction, text, (int)buttons, new IntPtr((int)icon), out
p) != 0)
      throw new InvalidOperationException("Something weird has happened."
);

   switch
(p)
   {
      case 1: return TaskDialogResult
.OK;
      case 2: return TaskDialogResult
.Cancel;
      case 4: return TaskDialogResult
.Retry;
      case 6: return TaskDialogResult
.Yes;
      case 7: return TaskDialogResult
.No;
      case 8: return TaskDialogResult
.Close;
      default: return TaskDialogResult
.None;
   }
}

In analogy to MessageBox, we've defined three helper enums: TaskDialogButtons, TaskDialogIcon and TaskDialogResult. Here are the definitions:

[Flags]
public enum
TaskDialogButtons
{
   OK = 0x0001,
   Cancel = 0x0008,
   Yes = 0x0002,
   No = 0x0004,
   Retry = 0x0010,
   Close = 0x0020
}

public enum
TaskDialogIcon
{
   Information =
UInt16
.MaxValue - 2,
   Warning =
UInt16
.MaxValue,
   Stop =
UInt16
.MaxValue - 1,
   Question = 0,

   SecurityWarning = UInt16.MaxValue - 5,
   SecurityError =
UInt16
.MaxValue - 6,
   SecuritySuccess =
UInt16
.MaxValue - 7,
   SecurityShield =
UInt16
.MaxValue - 3,
   SecurityShieldBlue =
UInt16
.MaxValue - 4,
   SecurityShieldGray =
UInt16
.MaxValue - 8
}

public enum
TaskDialogResult
{
   None,
   OK,
   Cancel,
   Yes,
   No,
   Retry,
   Close
}

Don't worry about the enum's values which are interop-related. For the buttons, you can consult the CommCtrl.h header file for instance:

enum _TASKDIALOG_COMMON_BUTTON_FLAGS
{
    TDCBF_OK_BUTTON            = 0x0001, // selected control return value IDOK
    TDCBF_YES_BUTTON           = 0x0002, // selected control return value IDYES
    TDCBF_NO_BUTTON            = 0x0004, // selected control return value IDNO
    TDCBF_CANCEL_BUTTON        = 0x0008, // selected control return value IDCANCEL
    TDCBF_RETRY_BUTTON         = 0x0010, // selected control return value IDRETRY
    TDCBF_CLOSE_BUTTON         = 0x0020  // selected control return value IDCLOSE
};
typedef int TASKDIALOG_COMMON_BUTTON_FLAGS;           // Note: _TASKDIALOG_COMMON_BUTTON_FLAGS is an int

The icons are slightly more cmplex and were extracted based on a few defines as well as some experiments:

#define TD_WARNING_ICON         MAKEINTRESOURCEW(-1)
#define TD_ERROR_ICON           MAKEINTRESOURCEW(-2)
#define TD_INFORMATION_ICON     MAKEINTRESOURCEW(-3)
#define TD_SHIELD_ICON          MAKEINTRESOURCEW(-4)

What about the various overloads? Here these are:

public static TaskDialogResult Show(IWin32Window owner, string text)
{
   return Show(owner, text, null, null, TaskDialogButtons
.OK);
}

public static TaskDialogResult Show(IWin32Window owner, string text, string
instruction)
{
   return Show(owner, text, instruction, null, TaskDialogButtons
.OK, 0);
}

public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string
caption)
{
   return Show(owner, text, instruction, caption, TaskDialogButtons
.OK, 0);
}

public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string caption, TaskDialogButtons
buttons)
{
   return
Show(owner, text, instruction, caption, buttons, 0);
}

public static TaskDialogResult Show(IWin32Window owner, string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon
icon)
{
   return
ShowInternal(owner.Handle, text, instruction, caption, buttons, icon);
}

public static TaskDialogResult Show(string text)
{
   return Show(text, null, null, TaskDialogButtons
.OK);
}

public static TaskDialogResult Show(string text, string
instruction)
{
   return Show(text, instruction, null, TaskDialogButtons
.OK, 0);
}

public static TaskDialogResult Show(string text, string instruction, string
caption)
{
   return Show(text, instruction, caption, TaskDialogButtons
.OK, 0);
}

public static TaskDialogResult Show(string text, string instruction, string caption, TaskDialogButtons
buttons)
{
   return
Show(text, instruction, caption, buttons, 0);
}

public static TaskDialogResult Show(string text, string instruction, string caption, TaskDialogButtons buttons, TaskDialogIcon
icon)
{
  
return ShowInternal(IntPtr
.Zero, text, instruction, caption, buttons, icon);
}

A few examples

So, what did we create? A few examples to illustrate:


TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Yes | TaskDialogButtons.No | TaskDialogButtons.Cancel, TaskDialogIcon.Warning);


TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application"TaskDialogButtons.OK, TaskDialogIcon.Information);


TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.OK | TaskDialogButtons.Cancel, TaskDialogIcon.SecuritySuccess);


TaskDialog.Show(this, "The quick brown fox jumps over the lazy dog.", "Can I ask you a question?", "My application", TaskDialogButtons.Retry | TaskDialogButtons.Cancel, TaskDialogIcon.Stop);

Download the code

Over here. So, I'd say: time to bring clarity to your customers' live. Stay tuned for more Vista fun!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# A graphical MessageBox confirmation cmdlet in PowerShell

Thursday, October 05, 2006 12:32 PM by B# .NET Blog

Introduction
I've been posting about Windows PowerShell programmability quite a bit lately. A few interesting...

# Wilm’s FuzzyLogic » Windows Vista Taskdialogs

Monday, December 04, 2006 7:37 AM by Wilm’s FuzzyLogic » Windows Vista Taskdialogs

# Del.icio.us Links for 5/25/2007

Friday, May 25, 2007 11:31 AM by Frank La Vigne

# Vista-style dialog using TaskDialog (with Windows XP emulation!) « {Programming} & Life

Pingback from  Vista-style dialog using TaskDialog (with Windows XP emulation!) « {Programming} & Life

# Unable to find an entry point name ‘TaskDialogIndirect’ in DLL ‘comctl32.dll’ « Managed world

Pingback from  Unable to find an entry point name ‘TaskDialogIndirect’ in DLL ‘comctl32.dll’ « Managed world

# TaskDialog hidden icons. « Managed world

Sunday, June 28, 2009 2:45 PM by TaskDialog hidden icons. « Managed world

Pingback from  TaskDialog hidden icons. « Managed world

# TaskDialog hidden icons. « Sharp World

Sunday, September 13, 2009 12:25 PM by TaskDialog hidden icons. « Sharp World

Pingback from  TaskDialog hidden icons. «  Sharp World

#

Monday, September 14, 2009 3:38 PM by TrackBack

Pingback from  

# Vista-style dialog using TaskDialog (with Windows XP emulation!) | {Programming} & Life

Pingback from  Vista-style dialog using TaskDialog (with Windows XP emulation!) | {Programming} & Life

# Canine cm plex | Trd4runner

Tuesday, July 19, 2011 2:11 PM by Canine cm plex | Trd4runner

Pingback from  Canine cm plex | Trd4runner

# gentlemen club las vegas

Friday, October 03, 2014 7:50 PM by gentlemen club las vegas

Using Windows Vista's TaskDialog API in managed code (C#) - B# .NET Blog