//------------------------------------------------------------------
//
// A P/Invoke wrapper for TaskDialog. Usability was given preference to perf and size.
//
//
//
//------------------------------------------------------------------
namespace ProcessHacker.Components
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
///
/// The signature of the callback that recieves notificaitons from the Task Dialog.
///
/// The active task dialog which has methods that can be performed on an active Task Dialog.
/// The notification arguments including the type of notification and information for the notification.
/// The value set on TaskDialog.CallbackData
/// Return value meaning varies depending on the Notification member of args.
public delegate bool TaskDialogCallback(ActiveTaskDialog taskDialog, TaskDialogNotificationArgs args, object callbackData);
///
/// The TaskDialog common button flags used to specify the builtin bottons to show in the TaskDialog.
///
[Flags]
public enum TaskDialogCommonButtons
{
///
/// No common buttons.
///
None = 0,
///
/// OK common button. If selected Task Dialog will return DialogResult.OK.
///
Ok = 0x0001,
///
/// Yes common button. If selected Task Dialog will return DialogResult.Yes.
///
Yes = 0x0002,
///
/// No common button. If selected Task Dialog will return DialogResult.No.
///
No = 0x0004,
///
/// Cancel common button. If selected Task Dialog will return DialogResult.Cancel.
/// If this button is specified, the dialog box will respond to typical cancel actions (Alt-F4 and Escape).
///
Cancel = 0x0008,
///
/// Retry common button. If selected Task Dialog will return DialogResult.Retry.
///
Retry = 0x0010,
///
/// Close common button. If selected Task Dialog will return this value.
///
Close = 0x0020,
}
///
/// The System icons the TaskDialog supports.
///
public enum TaskDialogIcon : uint
{
///
/// No Icon.
///
None = 0,
///
/// System warning icon.
///
Warning = 0xFFFF, // MAKEINTRESOURCEW(-1)
///
/// System Error icon.
///
Error = 0xFFFE, // MAKEINTRESOURCEW(-2)
///
/// System Information icon.
///
Information = 0xFFFD, // MAKEINTRESOURCEW(-3)
///
/// Shield icon.
///
Shield = 0xFFFC, // MAKEINTRESOURCEW(-4)
//These are undocumented "Special Styled" TaskDialog Windows
SecurityStop = UInt16.MaxValue - 1,
SecurityInformation = UInt16.MaxValue - 2,
SecurityShield = UInt16.MaxValue - 3,
SecurityShieldBlue = UInt16.MaxValue - 4,
SecurityWarning = UInt16.MaxValue - 5,
SecurityError = UInt16.MaxValue - 6,
SecuritySuccess = UInt16.MaxValue - 7,
SecurityShieldGray = UInt16.MaxValue - 8,
ASecurityWarning = UInt16.MaxValue,
//Other undocumented Icons
DefragWithShield = 195,
VideoIconWithoutVideo = 193,
WorldIconWithCable = 179,
MagnifyingGlass = 177,
FolderwithTwoArrowsPointingInwards = 175,
AppInstallUninstallIcon = 161,
AppearanceIcon = 151,
PerformanceMonitorIcon = 150,
MyComputerIconWithTick = 149,
ComputerJumpToComputerIcon = 147,
MonitorIconWithMagnifingGlass = 145,
InternetWorldWithClockIcon = 144,
BriefcaseWithUsericon = 130,
AppPageWithTicks = 121,
NetworkingIcon = 120,
CogWithTicks = 114,
DegragIcon = 111,
ShowDesktopIcon = 110,
ExclamationMarkShield = 107,
GreenTickShield = 106,
RedXShield = 105,
QuestionIcon = 104,
MonitorIcon = 101,
RunBoxIcon = 100,
CirleQuestion = 99,
CircleX = 98,
GreySmallX = 97,
FlashChip = 96,
TXTandBRIcon = 94,
BigRedX = 89,
AppInstallIcon = 87,
ExIcon = 84,
KeyIcon = 82,
InfoIcon = 81,
Startmenu = 80,
SharedIcon = 79,
WindowsDrive = 37,
CircleandTick = 24,
Network = 25,
RecycleBin = 55,
Padlock = 59,
DisplayLookingIcon = 65,
Picture = 70,
HDDQuestion = 75,
}
///
/// Task Dialog callback notifications.
///
public enum TaskDialogNotification
{
///
/// Sent by the Task Dialog once the dialog has been created and before it is displayed.
/// The value returned by the callback is ignored.
///
Created = 0,
//// Spec is not clear what this is so not supporting it.
/////
///// Sent by the Task Dialog when a navigation has occurred.
///// The value returned by the callback is ignored.
/////
// Navigated = 1,
///
/// Sent by the Task Dialog when the user selects a button or command link in the task dialog.
/// The button ID corresponding to the button selected will be available in the
/// TaskDialogNotificationArgs. To prevent the Task Dialog from closing, the application must
/// return true, otherwise the Task Dialog will be closed and the button ID returned to via
/// the original application call.
///
ButtonClicked = 2, // wParam = Button ID
///
/// Sent by the Task Dialog when the user clicks on a hyperlink in the Task Dialog’s content.
/// The string containing the HREF of the hyperlink will be available in the
/// TaskDialogNotificationArgs. To prevent the TaskDialog from shell executing the hyperlink,
/// the application must return TRUE, otherwise ShellExecute will be called.
///
HyperlinkClicked = 3, // lParam = (LPCWSTR)pszHREF
///
/// Sent by the Task Dialog approximately every 200 milliseconds when TaskDialog.CallbackTimer
/// has been set to true. The number of milliseconds since the dialog was created or the
/// notification returned true is available on the TaskDialogNotificationArgs. To reset
/// the tickcount, the application must return true, otherwise the tickcount will continue to
/// increment.
///
Timer = 4, // wParam = Milliseconds since dialog created or timer reset
///
/// Sent by the Task Dialog when it is destroyed and its window handle no longer valid.
/// The value returned by the callback is ignored.
///
Destroyed = 5,
///
/// Sent by the Task Dialog when the user selects a radio button in the task dialog.
/// The button ID corresponding to the button selected will be available in the
/// TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
///
RadioButtonClicked = 6, // wParam = Radio Button ID
///
/// Sent by the Task Dialog once the dialog has been constructed and before it is displayed.
/// The value returned by the callback is ignored.
///
DialogConstructed = 7,
///
/// Sent by the Task Dialog when the user checks or unchecks the verification checkbox.
/// The verificationFlagChecked value is available on the TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
///
VerificationClicked = 8, // wParam = 1 if checkbox checked, 0 if not, lParam is unused and always 0
///
/// Sent by the Task Dialog when the user presses F1 on the keyboard while the dialog has focus.
/// The value returned by the callback is ignored.
///
Help = 9,
///
/// Sent by the task dialog when the user clicks on the dialog's expando button.
/// The expanded value is available on the TaskDialogNotificationArgs.
/// The value returned by the callback is ignored.
///
ExpandoButtonClicked = 10 // wParam = 0 (dialog is now collapsed), wParam != 0 (dialog is now expanded)
}
///
/// Progress bar state.
///
public enum ProgressBarState
{
///
/// Normal.
///
Normal = 1,
///
/// Error state.
///
Error = 2,
///
/// Paused state.
///
Paused = 3
}
///
/// A custom button for the TaskDialog.
///
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
public struct TaskDialogButton
{
///
/// The ID of the button. This value is returned by TaskDialog.Show when the button is clicked.
///
private int buttonId;
///
/// The string that appears on the button.
///
[MarshalAs(UnmanagedType.LPWStr)]
private string buttonText;
///
/// Initialize the custom button.
///
/// The ID of the button. This value is returned by TaskDialog.Show when
/// the button is clicked. Typically this will be a value in the DialogResult enum.
/// The string that appears on the button.
public TaskDialogButton(int id, string text)
{
this.buttonId = id;
this.buttonText = text;
}
///
/// The ID of the button. This value is returned by TaskDialog.Show when the button is clicked.
///
public int ButtonId
{
get { return this.buttonId; }
set { this.buttonId = value; }
}
///
/// The string that appears on the button.
///
public string ButtonText
{
get { return this.buttonText; }
set { this.buttonText = value; }
}
}
///
/// A Task Dialog. This is like a MessageBox but with many more features. TaskDialog requires Windows Longhorn or later.
///
public class TaskDialog
{
///
/// The string to be used for the dialog box title. If this parameter is NULL, the filename of the executable program is used.
///
private string windowTitle;
///
/// The string to be used for the main instruction.
///
private string mainInstruction;
///
/// The string to be used for the dialog’s primary content. If the EnableHyperlinks member is true,
/// then this string may contain hyperlinks in the form: Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
private string content;
///
/// Specifies the push buttons displayed in the dialog box. This parameter may be a combination of flags.
/// If no common buttons are specified and no custom buttons are specified using the Buttons member, the
/// dialog box will contain the OK button by default.
///
private TaskDialogCommonButtons commonButtons;
///
/// Specifies a built in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon is null then no main icon will be displayed.
///
private TaskDialogIcon mainIcon;
///
/// Specifies a custom in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon member is null then no main icon will be displayed.
///
private Icon customMainIcon;
///
/// Specifies a built in icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
///
private TaskDialogIcon footerIcon;
///
/// Specifies a custom icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
///
private Icon customFooterIcon;
///
/// Specifies the custom push buttons to display in the dialog. Use CommonButtons member for
/// common buttons; OK, Yes, No, Retry and Cancel, and Buttons when you want different text
/// on the push buttons.
///
private TaskDialogButton[] buttons;
///
/// Specifies the radio buttons to display in the dialog.
///
private TaskDialogButton[] radioButtons;
///
/// The flags passed to TaskDialogIndirect.
///
private UnsafeNativeMethods.TASKDIALOG_FLAGS flags;
///
/// Indicates the default button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the Buttons array,
/// or one a DialogResult value that corresponds to a buttons specified in the CommonButtons Member.
/// If this member is zero or its value does not correspond to any button ID in the dialog,
/// then the first button in the dialog will be the default.
///
private int defaultButton;
///
/// Indicates the default radio button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the RadioButtons array.
/// If this member is zero or its value does not correspond to any radio button ID in the dialog,
/// then the first button in RadioButtons will be the default.
/// The property NoDefaultRadioButton can be set to have no default.
///
private int defaultRadioButton;
///
/// The string to be used to label the verification checkbox. If this member is null, the
/// verification checkbox is not displayed in the dialog box.
///
private string verificationText;
///
/// The string to be used for displaying additional information. The additional information is
/// displayed either immediately below the content or below the footer text depending on whether
/// the ExpandFooterArea member is true. If the EnableHyperlinks member is true, then this string
/// may contain hyperlinks in the form: Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
private string expandedInformation;
///
/// The string to be used to label the button for collapsing the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the CollapsedControlText is specified, then the CollapsedControlText value will be
/// used for this member as well.
///
private string expandedControlText;
///
/// The string to be used to label the button for expanding the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the ExpandedControlText is specified, then the ExpandedControlText value will be
/// used for this member as well.
///
private string collapsedControlText;
///
/// The string to be used in the footer area of the dialog box. If the EnableHyperlinks member
/// is true, then this string may contain hyperlinks in the form:
/// Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
private string footer;
///
/// The callback that receives messages from the Task Dialog when various events occur.
///
private TaskDialogCallback callback;
///
/// Reference that is passed to the callback.
///
private object callbackData;
///
/// Specifies the width of the Task Dialog’s client area in DLU’s. If 0, Task Dialog will calculate the ideal width.
///
private uint width;
///
/// Creates a default Task Dialog.
///
public TaskDialog()
{
this.Reset();
}
///
/// The string to be used for the dialog box title. If this parameter is NULL, the filename of the executable program is used.
///
public string WindowTitle
{
get { return this.windowTitle; }
set { this.windowTitle = value; }
}
///
/// The string to be used for the main instruction.
///
public string MainInstruction
{
get { return this.mainInstruction; }
set { this.mainInstruction = value; }
}
///
/// The string to be used for the dialog’s primary content. If the EnableHyperlinks member is true,
/// then this string may contain hyperlinks in the form: Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
public string Content
{
get { return this.content; }
set { this.content = value; }
}
///
/// Specifies the push buttons displayed in the dialog box. This parameter may be a combination of flags.
/// If no common buttons are specified and no custom buttons are specified using the Buttons member, the
/// dialog box will contain the OK button by default.
///
public TaskDialogCommonButtons CommonButtons
{
get { return this.commonButtons; }
set { this.commonButtons = value; }
}
///
/// Specifies a built in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon is null then no main icon will be displayed.
///
public TaskDialogIcon MainIcon
{
get { return this.mainIcon; }
set { this.mainIcon = value; }
}
///
/// Specifies a custom in icon for the main icon in the dialog. If this is set to none
/// and the CustomMainIcon member is null then no main icon will be displayed.
///
public Icon CustomMainIcon
{
get { return this.customMainIcon; }
set { this.customMainIcon = value; }
}
///
/// Specifies a built in icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
///
public TaskDialogIcon FooterIcon
{
get { return this.footerIcon; }
set { this.footerIcon = value; }
}
///
/// Specifies a custom icon for the icon to be displayed in the footer area of the
/// dialog box. If this is set to none and the CustomFooterIcon member is null then no
/// footer icon will be displayed.
///
public Icon CustomFooterIcon
{
get { return this.customFooterIcon; }
set { this.customFooterIcon = value; }
}
///
/// Specifies the custom push buttons to display in the dialog. Use CommonButtons member for
/// common buttons; OK, Yes, No, Retry and Cancel, and Buttons when you want different text
/// on the push buttons.
///
public TaskDialogButton[] Buttons
{
get
{
return this.buttons;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.buttons = value;
}
}
///
/// Specifies the radio buttons to display in the dialog.
///
public TaskDialogButton[] RadioButtons
{
get
{
return this.radioButtons;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.radioButtons = value;
}
}
///
/// Enables hyperlink processing for the strings specified in the Content, ExpandedInformation
/// and FooterText members. When enabled, these members may be strings that contain hyperlinks
/// in the form: Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
/// Note: Task Dialog will not actually execute any hyperlinks. Hyperlink execution must be handled
/// in the callback function specified by Callback member.
///
public bool EnableHyperlinks
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS, value); }
}
///
/// Indicates that the dialog should be able to be closed using Alt-F4, Escape and the title bar’s
/// close button even if no cancel button is specified in either the CommonButtons or Buttons members.
///
public bool AllowDialogCancellation
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION, value); }
}
///
/// Indicates that the buttons specified in the Buttons member should be displayed as command links
/// (using a standard task dialog glyph) instead of push buttons. When using command links, all
/// characters up to the first new line character in the ButtonText member (of the TaskDialogButton
/// structure) will be treated as the command link’s main text, and the remainder will be treated
/// as the command link’s note. This flag is ignored if the Buttons member has no entires.
///
public bool UseCommandLinks
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS, value); }
}
///
/// Indicates that the buttons specified in the Buttons member should be displayed as command links
/// (without a glyph) instead of push buttons. When using command links, all characters up to the
/// first new line character in the ButtonText member (of the TaskDialogButton structure) will be
/// treated as the command link’s main text, and the remainder will be treated as the command link’s
/// note. This flag is ignored if the Buttons member has no entires.
///
public bool UseCommandLinksNoIcon
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS_NO_ICON, value); }
}
///
/// Indicates that the string specified by the ExpandedInformation member should be displayed at the
/// bottom of the dialog’s footer area instead of immediately after the dialog’s content. This flag
/// is ignored if the ExpandedInformation member is null.
///
public bool ExpandFooterArea
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPAND_FOOTER_AREA, value); }
}
///
/// Indicates that the string specified by the ExpandedInformation member should be displayed
/// when the dialog is initially displayed. This flag is ignored if the ExpandedInformation member
/// is null.
///
public bool ExpandedByDefault
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_EXPANDED_BY_DEFAULT, value); }
}
///
/// Indicates that the verification checkbox in the dialog should be checked when the dialog is
/// initially displayed. This flag is ignored if the VerificationText parameter is null.
///
public bool VerificationFlagChecked
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED, value); }
}
///
/// Indicates that a Progress Bar should be displayed.
///
public bool ShowProgressBar
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR, value); }
}
///
/// Indicates that an Marquee Progress Bar should be displayed.
///
public bool ShowMarqueeProgressBar
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR, value); }
}
///
/// Indicates that the TaskDialog’s callback should be called approximately every 200 milliseconds.
///
public bool CallbackTimer
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER, value); }
}
///
/// Indicates that the TaskDialog should be positioned (centered) relative to the owner window
/// passed when calling Show. If not set (or no owner window is passed), the TaskDialog is
/// positioned (centered) relative to the monitor.
///
public bool PositionRelativeToWindow
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_POSITION_RELATIVE_TO_WINDOW) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_POSITION_RELATIVE_TO_WINDOW, value); }
}
///
/// Indicates that the TaskDialog should have right to left layout.
///
public bool RightToLeftLayout
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_RTL_LAYOUT) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_RTL_LAYOUT, value); }
}
///
/// Indicates that the TaskDialog should have no default radio button.
///
public bool NoDefaultRadioButton
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_NO_DEFAULT_RADIO_BUTTON, value); }
}
///
/// Indicates that the TaskDialog can be minimised. Works only if there if parent window is null. Will enable cancellation also.
///
public bool CanBeMinimized
{
get { return (this.flags & UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED) != 0; }
set { this.SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_CAN_BE_MINIMIZED, value); }
}
///
/// Indicates the default button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the Buttons array,
/// or one a DialogResult value that corresponds to a buttons specified in the CommonButtons Member.
/// If this member is zero or its value does not correspond to any button ID in the dialog,
/// then the first button in the dialog will be the default.
///
public int DefaultButton
{
get { return this.defaultButton; }
set { this.defaultButton = value; }
}
///
/// Indicates the default radio button for the dialog. This may be any of the values specified
/// in ButtonId members of one of the TaskDialogButton structures in the RadioButtons array.
/// If this member is zero or its value does not correspond to any radio button ID in the dialog,
/// then the first button in RadioButtons will be the default.
/// The property NoDefaultRadioButton can be set to have no default.
///
public int DefaultRadioButton
{
get { return this.defaultRadioButton; }
set { this.defaultRadioButton = value; }
}
///
/// The string to be used to label the verification checkbox. If this member is null, the
/// verification checkbox is not displayed in the dialog box.
///
public string VerificationText
{
get { return this.verificationText; }
set { this.verificationText = value; }
}
///
/// The string to be used for displaying additional information. The additional information is
/// displayed either immediately below the content or below the footer text depending on whether
/// the ExpandFooterArea member is true. If the EnameHyperlinks member is true, then this string
/// may contain hyperlinks in the form: Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
public string ExpandedInformation
{
get { return this.expandedInformation; }
set { this.expandedInformation = value; }
}
///
/// The string to be used to label the button for collapsing the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the CollapsedControlText is specified, then the CollapsedControlText value will be
/// used for this member as well.
///
public string ExpandedControlText
{
get { return this.expandedControlText; }
set { this.expandedControlText = value; }
}
///
/// The string to be used to label the button for expanding the expanded information. This
/// member is ignored when the ExpandedInformation member is null. If this member is null
/// and the ExpandedControlText is specified, then the ExpandedControlText value will be
/// used for this member as well.
///
public string CollapsedControlText
{
get { return this.collapsedControlText; }
set { this.collapsedControlText = value; }
}
///
/// The string to be used in the footer area of the dialog box. If the EnableHyperlinks member
/// is true, then this string may contain hyperlinks in the form:
/// Hyperlink Text.
/// WARNING: Enabling hyperlinks when using content from an unsafe source may cause security vulnerabilities.
///
public string Footer
{
get { return this.footer; }
set { this.footer = value; }
}
///
/// width of the Task Dialog's client area in DLU's. If 0, Task Dialog will calculate the ideal width.
///
public uint Width
{
get { return this.width; }
set { this.width = value; }
}
///
/// The callback that receives messages from the Task Dialog when various events occur.
///
public TaskDialogCallback Callback
{
get { return this.callback; }
set { this.callback = value; }
}
///
/// Reference that is passed to the callback.
///
public object CallbackData
{
get { return this.callbackData; }
set { this.callbackData = value; }
}
///
/// Resets the Task Dialog to the state when first constructed, all properties set to their default value.
///
public void Reset()
{
this.windowTitle = null;
this.mainInstruction = null;
this.content = null;
this.commonButtons = 0;
this.mainIcon = TaskDialogIcon.None;
this.customMainIcon = null;
this.footerIcon = TaskDialogIcon.None;
this.customFooterIcon = null;
this.buttons = new TaskDialogButton[0];
this.radioButtons = new TaskDialogButton[0];
this.flags = 0;
this.defaultButton = 0;
this.defaultRadioButton = 0;
this.verificationText = null;
this.expandedInformation = null;
this.expandedControlText = null;
this.collapsedControlText = null;
this.footer = null;
this.callback = null;
this.callbackData = null;
this.width = 0;
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show()
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show(IntPtr.Zero, out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IWin32Window owner)
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IntPtr hwndOwner)
{
bool verificationFlagChecked;
int radioButtonResult;
return this.Show(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// Returns true if the verification checkbox was checked when the dialog
/// was dismissed.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IWin32Window owner, out bool verificationFlagChecked)
{
int radioButtonResult;
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// Returns true if the verification checkbox was checked when the dialog
/// was dismissed.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IntPtr hwndOwner, out bool verificationFlagChecked)
{
// We have to call a private version or PreSharp gets upset about a unsafe
// block in a public method. (PreSharp error 56505)
int radioButtonResult;
return this.PrivateShow(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// Returns true if the verification checkbox was checked when the dialog
/// was dismissed.
/// The radio botton selected by the user.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IWin32Window owner, out bool verificationFlagChecked, out int radioButtonResult)
{
return this.Show((owner == null ? IntPtr.Zero : owner.Handle), out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// Returns true if the verification checkbox was checked when the dialog
/// was dismissed.
/// The radio botton selected by the user.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
public int Show(IntPtr hwndOwner, out bool verificationFlagChecked, out int radioButtonResult)
{
// We have to call a private version or PreSharp gets upset about a unsafe
// block in a public method. (PreSharp error 56505)
return this.PrivateShow(hwndOwner, out verificationFlagChecked, out radioButtonResult);
}
///
/// Creates, displays, and operates a task dialog. The task dialog contains application-defined messages, title,
/// verification check box, command links and push buttons, plus any combination of predefined icons and push buttons
/// as specified on the other members of the class before calling Show.
///
/// Owner window the task Dialog will modal to.
/// Returns true if the verification checkbox was checked when the dialog
/// was dismissed.
/// The radio botton selected by the user.
/// The result of the dialog, either a DialogResult value for common push buttons set in the CommonButtons
/// member or the ButtonID from a TaskDialogButton structure set on the Buttons member.
private int PrivateShow(IntPtr hwndOwner, out bool verificationFlagChecked, out int radioButtonResult)
{
verificationFlagChecked = false;
radioButtonResult = 0;
int result = 0;
UnsafeNativeMethods.TASKDIALOGCONFIG config = new UnsafeNativeMethods.TASKDIALOGCONFIG();
try
{
config.cbSize = (uint)Marshal.SizeOf(typeof(UnsafeNativeMethods.TASKDIALOGCONFIG));
config.hwndParent = hwndOwner;
config.dwFlags = this.flags;
config.dwCommonButtons = this.commonButtons;
if (!string.IsNullOrEmpty(this.windowTitle))
{
config.pszWindowTitle = this.windowTitle;
}
config.MainIcon = (IntPtr)this.mainIcon;
if (this.customMainIcon != null)
{
config.dwFlags |= UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_MAIN;
config.MainIcon = this.customMainIcon.Handle;
}
if (!string.IsNullOrEmpty(this.mainInstruction))
{
config.pszMainInstruction = this.mainInstruction;
}
if (!string.IsNullOrEmpty(this.content))
{
config.pszContent = this.content;
}
TaskDialogButton[] customButtons = this.buttons;
if (customButtons.Length > 0)
{
// Hand marshal the buttons array.
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
config.pButtons = Marshal.AllocHGlobal(elementSize * (int)customButtons.Length);
for (int i = 0; i < customButtons.Length; i++)
{
unsafe // Unsafe because of pointer arithmatic.
{
byte* p = (byte*)config.pButtons;
Marshal.StructureToPtr(customButtons[i], (IntPtr)(p + (elementSize * i)), false);
}
config.cButtons++;
}
}
TaskDialogButton[] customRadioButtons = this.radioButtons;
if (customRadioButtons.Length > 0)
{
// Hand marshal the buttons array.
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
config.pRadioButtons = Marshal.AllocHGlobal(elementSize * (int)customRadioButtons.Length);
for (int i = 0; i < customRadioButtons.Length; i++)
{
unsafe // Unsafe because of pointer arithmatic.
{
byte* p = (byte*)config.pRadioButtons;
Marshal.StructureToPtr(customRadioButtons[i], (IntPtr)(p + (elementSize * i)), false);
}
config.cRadioButtons++;
}
}
config.nDefaultButton = this.defaultButton;
config.nDefaultRadioButton = this.defaultRadioButton;
if (!string.IsNullOrEmpty(this.verificationText))
{
config.pszVerificationText = this.verificationText;
}
if (!string.IsNullOrEmpty(this.expandedInformation))
{
config.pszExpandedInformation = this.expandedInformation;
}
if (!string.IsNullOrEmpty(this.expandedControlText))
{
config.pszExpandedControlText = this.expandedControlText;
}
if (!string.IsNullOrEmpty(this.collapsedControlText))
{
config.pszCollapsedControlText = this.CollapsedControlText;
}
config.FooterIcon = (IntPtr)this.footerIcon;
if (this.customFooterIcon != null)
{
config.dwFlags |= UnsafeNativeMethods.TASKDIALOG_FLAGS.TDF_USE_HICON_FOOTER;
config.FooterIcon = this.customFooterIcon.Handle;
}
if (!string.IsNullOrEmpty(this.footer))
{
config.pszFooter = this.footer;
}
// If our user has asked for a callback then we need to ask for one to
// translate to the friendly version.
if (this.callback != null)
{
config.pfCallback = new UnsafeNativeMethods.TaskDialogCallback(this.PrivateCallback);
}
////config.lpCallbackData = this.callbackData; // How do you do this? Need to pin the ref?
config.cxWidth = this.width;
// The call all this mucking about is here for.
UnsafeNativeMethods.TaskDialogIndirect(ref config, out result, out radioButtonResult, out verificationFlagChecked);
}
finally
{
// Free the unmanged memory needed for the button arrays.
// There is the possiblity of leaking memory if the app-domain is destroyed in a non clean way
// and the hosting OS process is kept alive but fixing this would require using hardening techniques
// that are not required for the users of this class.
if (config.pButtons != IntPtr.Zero)
{
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
for (int i = 0; i < config.cButtons; i++)
{
unsafe
{
byte* p = (byte*)config.pButtons;
Marshal.DestroyStructure((IntPtr)(p + (elementSize * i)), typeof(TaskDialogButton));
}
}
Marshal.FreeHGlobal(config.pButtons);
}
if (config.pRadioButtons != IntPtr.Zero)
{
int elementSize = Marshal.SizeOf(typeof(TaskDialogButton));
for (int i = 0; i < config.cRadioButtons; i++)
{
unsafe
{
byte* p = (byte*)config.pRadioButtons;
Marshal.DestroyStructure((IntPtr)(p + (elementSize * i)), typeof(TaskDialogButton));
}
}
Marshal.FreeHGlobal(config.pRadioButtons);
}
}
return result;
}
///
/// The callback from the native Task Dialog. This prepares the friendlier arguments and calls the simplier callback.
///
/// The window handle of the Task Dialog that is active.
/// The notification. A TaskDialogNotification value.
/// Specifies additional noitification information. The contents of this parameter depends on the value of the msg parameter.
/// Specifies additional noitification information. The contents of this parameter depends on the value of the msg parameter.
/// Specifies the application-defined value given in the call to TaskDialogIndirect.
/// A HRESULT. It's not clear in the spec what a failed result will do.
private int PrivateCallback([In] IntPtr hwnd, [In] uint msg, [In] UIntPtr wparam, [In] IntPtr lparam, [In] IntPtr refData)
{
TaskDialogCallback callback = this.callback;
if (callback != null)
{
// Prepare arguments for the callback to the user we are insulating from Interop casting sillyness.
// Future: Consider reusing a single ActiveTaskDialog object and mark it as destroyed on the destry notification.
ActiveTaskDialog activeDialog = new ActiveTaskDialog(hwnd);
TaskDialogNotificationArgs args = new TaskDialogNotificationArgs();
args.Notification = (TaskDialogNotification)msg;
switch (args.Notification)
{
case TaskDialogNotification.ButtonClicked:
case TaskDialogNotification.RadioButtonClicked:
args.ButtonId = (int)wparam;
break;
case TaskDialogNotification.HyperlinkClicked:
args.Hyperlink = Marshal.PtrToStringUni(lparam);
break;
case TaskDialogNotification.Timer:
args.TimerTickCount = (uint)wparam;
break;
case TaskDialogNotification.VerificationClicked:
args.VerificationFlagChecked = (wparam != UIntPtr.Zero);
break;
case TaskDialogNotification.ExpandoButtonClicked:
args.Expanded = (wparam != UIntPtr.Zero);
break;
}
return (callback(activeDialog, args, this.callbackData) ? 1 : 0);
}
return 0; // false;
}
///
/// Helper function to set or clear a bit in the flags field.
///
/// The Flag bit to set or clear.
/// True to set, false to clear the bit in the flags field.
private void SetFlag(UnsafeNativeMethods.TASKDIALOG_FLAGS flag, bool value)
{
if (value)
{
this.flags |= flag;
}
else
{
this.flags &= ~flag;
}
}
}
}