From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- .../ExternalUIHandler.cs | 223 +++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs') diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs new file mode 100644 index 00000000..08f00867 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ExternalUIHandler.cs @@ -0,0 +1,223 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Dtf.WindowsInstaller +{ + using System; + using System.Collections; + using System.Runtime.InteropServices; + using System.Diagnostics.CodeAnalysis; + + /// + /// Defines a callback function that the installer calls for progress notification and error messages. + /// + public delegate MessageResult ExternalUIHandler( + InstallMessage messageType, + string message, + MessageButtons buttons, + MessageIcon icon, + MessageDefaultButton defaultButton); + + /// + /// [MSI 3.1] Defines a callback function that the installer calls for record-based progress notification and error messages. + /// + public delegate MessageResult ExternalUIRecordHandler( + InstallMessage messageType, + Record messageRecord, + MessageButtons buttons, + MessageIcon icon, + MessageDefaultButton defaultButton); + + internal delegate int NativeExternalUIHandler(IntPtr context, int messageType, [MarshalAs(UnmanagedType.LPWStr)] string message); + + internal delegate int NativeExternalUIRecordHandler(IntPtr context, int messageType, int recordHandle); + + internal class ExternalUIProxy + { + private ExternalUIHandler handler; + + internal ExternalUIProxy(ExternalUIHandler handler) + { + this.handler = handler; + } + + public ExternalUIHandler Handler + { + get { return this.handler; } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public int ProxyHandler(IntPtr contextPtr, int messageType, [MarshalAs(UnmanagedType.LPWStr)] string message) + { + try + { + int msgType = messageType & 0x7F000000; + int buttons = messageType & 0x0000000F; + int icon = messageType & 0x000000F0; + int defButton = messageType & 0x00000F00; + + return (int) this.handler( + (InstallMessage) msgType, + message, + (MessageButtons) buttons, + (MessageIcon) icon, + (MessageDefaultButton) defButton); + } + catch + { + return (int) MessageResult.Error; + } + } + } + + internal class ExternalUIRecordProxy + { + private ExternalUIRecordHandler handler; + + internal ExternalUIRecordProxy(ExternalUIRecordHandler handler) + { + this.handler = handler; + } + + public ExternalUIRecordHandler Handler + { + get { return this.handler; } + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + public int ProxyHandler(IntPtr contextPtr, int messageType, int recordHandle) + { + try + { + int msgType = messageType & 0x7F000000; + int buttons = messageType & 0x0000000F; + int icon = messageType & 0x000000F0; + int defButton = messageType & 0x00000F00; + + Record msgRec = (recordHandle != 0 ? Record.FromHandle((IntPtr) recordHandle, false) : null); + using (msgRec) + { + return (int) this.handler( + (InstallMessage) msgType, + msgRec, + (MessageButtons) buttons, + (MessageIcon) icon, + (MessageDefaultButton) defButton); + } + } + catch + { + return (int) MessageResult.Error; + } + } + } + + public static partial class Installer + { + private static IList externalUIHandlers = ArrayList.Synchronized(new ArrayList()); + + /// + /// Enables an external user-interface handler. This external UI handler is called before the + /// normal internal user-interface handler. The external UI handler has the option to suppress + /// the internal UI by returning a non-zero value to indicate that it has handled the messages. + /// + /// A callback delegate that handles the UI messages + /// Specifies which messages to handle using the external message handler. + /// If the external handler returns a non-zero result, then that message will not be sent to the UI, + /// instead the message will be logged if logging has been enabled. + /// The previously set external handler, or null if there was no previously set handler + ///

+ /// To restore the previous UI handler, a second call is made to SetExternalUI using the + /// ExternalUIHandler returned by the first call to SetExternalUI and specifying + /// as the message filter. + ///

+ /// The external user interface handler does not have full control over the external user + /// interface unless is called with the uiLevel parameter set to + /// . If SetInternalUI is not called, the internal user + /// interface level defaults to . As a result, any message not + /// handled by the external user interface handler is handled by Windows Installer. The initial + /// "Preparing to install..." dialog always appears even if the external user interface + /// handler handles all messages. + ///

+ /// SetExternalUI should only be called from a bootstrapping application. You cannot call + /// it from a custom action + ///

+ /// Win32 MSI API: + /// MsiSetExternalUI + ///

+ public static ExternalUIHandler SetExternalUI(ExternalUIHandler uiHandler, InstallLogModes messageFilter) + { + NativeExternalUIHandler nativeHandler = null; + if (uiHandler != null) + { + nativeHandler = new ExternalUIProxy(uiHandler).ProxyHandler; + Installer.externalUIHandlers.Add(nativeHandler); + } + NativeExternalUIHandler oldNativeHandler = NativeMethods.MsiSetExternalUI(nativeHandler, (uint) messageFilter, IntPtr.Zero); + if (oldNativeHandler != null && oldNativeHandler.Target is ExternalUIProxy) + { + Installer.externalUIHandlers.Remove(oldNativeHandler); + return ((ExternalUIProxy) oldNativeHandler.Target).Handler; + } + else + { + return null; + } + } + + /// + /// [MSI 3.1] Enables a record-based external user-interface handler. This external UI handler is called + /// before the normal internal user-interface handler. The external UI handler has the option to suppress + /// the internal UI by returning a non-zero value to indicate that it has handled the messages. + /// + /// A callback delegate that handles the UI messages + /// Specifies which messages to handle using the external message handler. + /// If the external handler returns a non-zero result, then that message will not be sent to the UI, + /// instead the message will be logged if logging has been enabled. + /// The previously set external handler, or null if there was no previously set handler + ///

+ /// To restore the previous UI handler, a second call is made to SetExternalUI using the + /// ExternalUIHandler returned by the first call to SetExternalUI and specifying + /// as the message filter. + ///

+ /// The external user interface handler does not have full control over the external user + /// interface unless is called with the uiLevel parameter set to + /// . If SetInternalUI is not called, the internal user + /// interface level defaults to . As a result, any message not + /// handled by the external user interface handler is handled by Windows Installer. The initial + /// "Preparing to install..." dialog always appears even if the external user interface + /// handler handles all messages. + ///

+ /// SetExternalUI should only be called from a bootstrapping application. You cannot call + /// it from a custom action + ///

+ /// Win32 MSI API: + /// MsiSetExternalUIRecord + ///

+ public static ExternalUIRecordHandler SetExternalUI(ExternalUIRecordHandler uiHandler, InstallLogModes messageFilter) + { + NativeExternalUIRecordHandler nativeHandler = null; + if (uiHandler != null) + { + nativeHandler = new ExternalUIRecordProxy(uiHandler).ProxyHandler; + Installer.externalUIHandlers.Add(nativeHandler); + } + NativeExternalUIRecordHandler oldNativeHandler; + uint ret = NativeMethods.MsiSetExternalUIRecord(nativeHandler, (uint) messageFilter, IntPtr.Zero, out oldNativeHandler); + if (ret != 0) + { + Installer.externalUIHandlers.Remove(nativeHandler); + throw InstallerException.ExceptionFromReturnCode(ret); + } + + if (oldNativeHandler != null && oldNativeHandler.Target is ExternalUIRecordProxy) + { + Installer.externalUIHandlers.Remove(oldNativeHandler); + return ((ExternalUIRecordProxy) oldNativeHandler.Target).Handler; + } + else + { + return null; + } + } + } +} -- cgit v1.2.3-55-g6feb