//------------------------------------------------------------------ // // 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; } } } }