// 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.Configuration; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Security; using System.Text; /// /// Managed-code portion of the embedded UI proxy. /// internal static class EmbeddedUIProxy { private static IEmbeddedUI uiInstance; private static string uiClass; private static bool DebugBreakEnabled(string method) { return CustomActionProxy.DebugBreakEnabled(new string[] { method, EmbeddedUIProxy.uiClass + "." + method } ); } /// /// Initializes managed embedded UI by loading the UI class and invoking its Initialize method. /// /// Integer handle to the installer session. /// Name of the class that implements the embedded UI. This must /// be of the form: "AssemblyName!Namespace.Class" /// On entry, contains the current UI level for the installation. After this /// method returns, the installer resets the UI level to the returned value of this parameter. /// 0 if the embedded UI was successfully loaded and initialized, /// ERROR_INSTALL_USEREXIT if the user canceled the installation during initialization, /// or ERROR_INSTALL_FAILURE if the embedded UI could not be initialized. /// /// Due to interop limitations, the successful resulting UILevel is actually returned /// as the high-word of the return value instead of via a ref parameter. /// [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public static int Initialize(int sessionHandle, string uiClass, int internalUILevel) { Session session = null; try { session = new Session((IntPtr) sessionHandle, false); if (String.IsNullOrEmpty(uiClass)) { throw new ArgumentNullException("uiClass"); } EmbeddedUIProxy.uiInstance = EmbeddedUIProxy.InstantiateUI(session, uiClass); } catch (Exception ex) { if (session != null) { try { session.Log("Exception while loading embedded UI:"); session.Log(ex.ToString()); } catch (Exception) { } } } if (EmbeddedUIProxy.uiInstance == null) { return (int) ActionResult.Failure; } try { string resourcePath = Path.GetDirectoryName(EmbeddedUIProxy.uiInstance.GetType().Assembly.Location); InstallUIOptions uiOptions = (InstallUIOptions) internalUILevel; if (EmbeddedUIProxy.DebugBreakEnabled("Initialize")) { System.Diagnostics.Debugger.Launch(); } if (EmbeddedUIProxy.uiInstance.Initialize(session, resourcePath, ref uiOptions)) { // The embedded UI initialized and the installation should continue // with internal UI reset according to options. return ((int) uiOptions) << 16; } else { // The embedded UI did not initialize but the installation should still continue // with internal UI reset according to options. return (int) uiOptions; } } catch (InstallCanceledException) { // The installation was canceled by the user. return (int) ActionResult.UserExit; } catch (Exception ex) { // An unhandled exception causes the installation to fail immediately. session.Log("Exception thrown by embedded UI initialization:"); session.Log(ex.ToString()); return (int) ActionResult.Failure; } } /// /// Passes a progress message to the UI class. /// /// Installer message type and message box options. /// Handle to a record containing message data. /// Return value returned by the UI class. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public static int ProcessMessage(int messageType, int recordHandle) { if (EmbeddedUIProxy.uiInstance != null) { 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) { if (EmbeddedUIProxy.DebugBreakEnabled("ProcessMessage")) { System.Diagnostics.Debugger.Launch(); } return (int) EmbeddedUIProxy.uiInstance.ProcessMessage( (InstallMessage) msgType, msgRec, (MessageButtons) buttons, (MessageIcon) icon, (MessageDefaultButton) defButton); } } catch (Exception) { // Ignore it... just hope future messages will not throw exceptions. } } return 0; } /// /// Passes a shutdown message to the UI class. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] public static void Shutdown() { if (EmbeddedUIProxy.uiInstance != null) { try { if (EmbeddedUIProxy.DebugBreakEnabled("Shutdown")) { System.Diagnostics.Debugger.Launch(); } EmbeddedUIProxy.uiInstance.Shutdown(); } catch (Exception) { // Nothing to do at this point... the installation is done anyway. } EmbeddedUIProxy.uiInstance = null; } } /// /// Instantiates a UI class from a given assembly and class name. /// /// Installer session, for logging. /// Name of the class that implements the embedded UI. This must /// be of the form: "AssemblyName!Namespace.Class" /// Interface on the UI class for handling UI messages. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] private static IEmbeddedUI InstantiateUI(Session session, string uiClass) { int assemblySplit = uiClass.IndexOf('!'); if (assemblySplit < 0) { session.Log("Error: invalid embedded UI assembly and class:" + uiClass); return null; } string assemblyName = uiClass.Substring(0, assemblySplit); EmbeddedUIProxy.uiClass = uiClass.Substring(assemblySplit + 1); Assembly uiAssembly; try { uiAssembly = AppDomain.CurrentDomain.Load(assemblyName); // This calls out to CustomActionProxy.DebugBreakEnabled() directly instead // of calling EmbeddedUIProxy.DebugBreakEnabled() because we don't compose a // class.method name for this breakpoint. if (CustomActionProxy.DebugBreakEnabled(new string[] { "EmbeddedUI" })) { System.Diagnostics.Debugger.Launch(); } return (IEmbeddedUI) uiAssembly.CreateInstance(EmbeddedUIProxy.uiClass); } catch (Exception ex) { session.Log("Error: could not load embedded UI class " + EmbeddedUIProxy.uiClass + " from assembly: " + assemblyName); session.Log(ex.ToString()); return null; } } } }