// 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.IO; using System.Text; using System.Resources; using System.Reflection; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; /// /// Receives an exception from /// /// indicating the reason a particular patch is not applicable to a product. /// /// MSP file path, XML file path, or XML blob that was passed to /// /// exception indicating the reason the patch is not applicable ///

/// If is an or subclass, then /// its and /// properties will indicate a more specific reason the patch was not applicable. ///

/// The could also be a FileNotFoundException if the /// patch string was a file path. ///

public delegate void InapplicablePatchHandler(string patch, Exception exception); /// /// Provides static methods for installing and configuring products and patches. /// public static partial class Installer { private static bool rebootRequired; private static bool rebootInitiated; private static ResourceManager errorResources; /// /// Indicates whether a system reboot is required after running an installation or configuration operation. /// public static bool RebootRequired { get { return Installer.rebootRequired; } } /// /// Indicates whether a system reboot has been initiated after running an installation or configuration operation. /// public static bool RebootInitiated { get { return Installer.rebootInitiated; } } /// /// Enables the installer's internal user interface. Then this user interface is used /// for all subsequent calls to user-interface-generating installer functions in this process. /// /// Specifies the level of complexity of the user interface /// Handle to a window, which becomes the owner of any user interface created. /// A pointer to the previous owner of the user interface is returned. /// The previous user interface level ///

/// Win32 MSI API: /// MsiSetInternalUI ///

[SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")] public static InstallUIOptions SetInternalUI(InstallUIOptions uiOptions, ref IntPtr windowHandle) { return (InstallUIOptions) NativeMethods.MsiSetInternalUI((uint) uiOptions, ref windowHandle); } /// /// Enables the installer's internal user interface. Then this user interface is used /// for all subsequent calls to user-interface-generating installer functions in this process. /// The owner of the user interface does not change. /// /// Specifies the level of complexity of the user interface /// The previous user interface level ///

/// Win32 MSI API: /// MsiSetInternalUI ///

public static InstallUIOptions SetInternalUI(InstallUIOptions uiOptions) { return (InstallUIOptions) NativeMethods.MsiSetInternalUI((uint) uiOptions, IntPtr.Zero); } /// /// Enables logging of the selected message type for all subsequent install sessions in /// the current process space. /// /// One or more mode flags specifying the type of messages to log /// Full path to the log file. A null path disables logging, /// in which case the logModes paraneter is ignored. /// an invalid log mode was specified /// This method takes effect on any new installation processes. Calling this /// method from within a custom action will not start logging for that installation. public static void EnableLog(InstallLogModes logModes, string logFile) { Installer.EnableLog(logModes, logFile, false, true); } /// /// Enables logging of the selected message type for all subsequent install sessions in /// the current process space. /// /// One or more mode flags specifying the type of messages to log /// Full path to the log file. A null path disables logging, /// in which case the logModes paraneter is ignored. /// If true, the log lines will be appended to any existing file content. /// If false, the log file will be truncated if it exists. The default is false. /// If true, the log will be flushed after every line. /// If false, the log will be flushed every 20 lines. The default is true. /// an invalid log mode was specified ///

/// This method takes effect on any new installation processes. Calling this /// method from within a custom action will not start logging for that installation. ///

/// Win32 MSI API: /// MsiEnableLog ///

public static void EnableLog(InstallLogModes logModes, string logFile, bool append, bool flushEveryLine) { uint ret = NativeMethods.MsiEnableLog((uint) logModes, logFile, (append ? (uint) 1 : 0) + (flushEveryLine ? (uint) 2 : 0)); if (ret != 0 && ret != (uint) NativeMethods.Error.FILE_INVALID) { throw InstallerException.ExceptionFromReturnCode(ret); } } /// /// increments the usage count for a particular feature and returns the installation state for /// that feature. This method should be used to indicate an application's intent to use a feature. /// /// The product code of the product. /// The feature to be used. /// Must have the value . /// The installed state of the feature. ///

/// The UseFeature method should only be used on features known to be published. The application /// should determine the status of the feature by calling either the FeatureState method or /// Features method. ///

/// Win32 MSI APIs: /// MsiUseFeature, /// MsiUseFeatureEx ///

public static InstallState UseFeature(string productCode, string feature, InstallMode installMode) { int installState = NativeMethods.MsiUseFeatureEx(productCode, feature, unchecked ((uint) installMode), 0); return (InstallState) installState; } /// /// Opens an installer package for use with functions that access the product database and install engine, /// returning an Session object. /// /// Path to the package /// Specifies whether or not the create a Session object that ignores the /// computer state and that is incapable of changing the current computer state. A value of false yields /// the normal behavior. A value of true creates a "safe" Session object that cannot change of the current /// machine state. /// A Session object allowing access to the product database and install engine /// The product could not be opened /// The installer configuration data is corrupt ///

/// Note that only one Session object can be opened by a single process. OpenPackage cannot be used in a /// custom action because the active installation is the only session allowed. ///

/// A "safe" Session object ignores the current computer state when opening the package and prevents /// changes to the current computer state. ///

/// The Session object should be d after use. /// It is best that the handle be closed manually as soon as it is no longer /// needed, as leaving lots of unused handles open can degrade performance. ///

/// Win32 MSI APIs: /// MsiOpenPackage, /// MsiOpenPackageEx ///

public static Session OpenPackage(string packagePath, bool ignoreMachineState) { int sessionHandle; uint ret = NativeMethods.MsiOpenPackageEx(packagePath, ignoreMachineState ? (uint) 1 : 0, out sessionHandle); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return new Session((IntPtr) sessionHandle, true); } /// /// Opens an installer package for use with functions that access the product database and install engine, /// returning an Session object. /// /// Database used to create the session /// Specifies whether or not the create a Session object that ignores the /// computer state and that is incapable of changing the current computer state. A value of false yields /// the normal behavior. A value of true creates a "safe" Session object that cannot change of the current /// machine state. /// A Session object allowing access to the product database and install engine /// The product could not be opened /// The installer configuration data is corrupt ///

/// Note that only one Session object can be opened by a single process. OpenPackage cannot be used in a /// custom action because the active installation is the only session allowed. ///

/// A "safe" Session object ignores the current computer state when opening the package and prevents /// changes to the current computer state. ///

/// The Session object should be d after use. /// It is best that the handle be closed manually as soon as it is no longer /// needed, as leaving lots of unused handles open can degrade performance. ///

/// Win32 MSI APIs: /// MsiOpenPackage, /// MsiOpenPackageEx ///

[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")] public static Session OpenPackage(Database database, bool ignoreMachineState) { if (database == null) { throw new ArgumentNullException("database"); } return Installer.OpenPackage( String.Format(CultureInfo.InvariantCulture, "#{0}", database.Handle), ignoreMachineState); } /// /// Opens an installer package for an installed product using the product code. /// /// Product code of the installed product /// A Session object allowing access to the product database and install engine, /// or null if the specified product is not installed. /// An unknown product was requested /// The product could not be opened /// The installer configuration data is corrupt ///

/// Note that only one Session object can be opened by a single process. OpenProduct cannot be /// used in a custom action because the active installation is the only session allowed. ///

/// The Session object should be d after use. /// It is best that the handle be closed manually as soon as it is no longer /// needed, as leaving lots of unused handles open can degrade performance. ///

/// Win32 MSI API: /// MsiOpenProduct ///

public static Session OpenProduct(string productCode) { int sessionHandle; uint ret = NativeMethods.MsiOpenProduct(productCode, out sessionHandle); if (ret != 0) { if (ret == (uint) NativeMethods.Error.UNKNOWN_PRODUCT) { return null; } else { throw InstallerException.ExceptionFromReturnCode(ret); } } return new Session((IntPtr) sessionHandle, true); } /// /// Gets the full component path, performing any necessary installation. This method prompts for source if /// necessary and increments the usage count for the feature. /// /// Product code for the product that contains the feature with the necessary component /// Feature ID of the feature with the necessary component /// Component code of the necessary component /// Installation mode; this can also include bits from /// Path to the component ///

/// Win32 MSI API: /// MsiProvideComponent ///

public static string ProvideComponent(string product, string feature, string component, InstallMode installMode) { StringBuilder pathBuf = new StringBuilder(512); uint pathBufSize = (uint) pathBuf.Capacity; uint ret = NativeMethods.MsiProvideComponent(product, feature, component, unchecked((uint)installMode), pathBuf, ref pathBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { pathBuf.Capacity = (int) ++pathBufSize; ret = NativeMethods.MsiProvideComponent(product, feature, component, unchecked((uint)installMode), pathBuf, ref pathBufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return pathBuf.ToString(); } /// /// Gets the full component path for a qualified component that is published by a product and /// performs any necessary installation. This method prompts for source if necessary and increments /// the usage count for the feature. /// /// Specifies the component ID for the requested component. This may not be the /// GUID for the component itself but rather a server that provides the correct functionality, as in the /// ComponentId column of the PublishComponent table. /// Specifies a qualifier into a list of advertising components (from PublishComponent Table). /// Installation mode; this can also include bits from /// Optional; specifies the product to match that has published the qualified component. /// Path to the component ///

/// Win32 MSI APIs: /// MsiProvideQualifiedComponent /// MsiProvideQualifiedComponentEx ///

public static string ProvideQualifiedComponent(string component, string qualifier, InstallMode installMode, string product) { StringBuilder pathBuf = new StringBuilder(512); uint pathBufSize = (uint) pathBuf.Capacity; uint ret = NativeMethods.MsiProvideQualifiedComponentEx(component, qualifier, unchecked((uint)installMode), product, 0, 0, pathBuf, ref pathBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { pathBuf.Capacity = (int) ++pathBufSize; ret = NativeMethods.MsiProvideQualifiedComponentEx(component, qualifier, unchecked((uint)installMode), product, 0, 0, pathBuf, ref pathBufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return pathBuf.ToString(); } /// /// Gets the full path to a Windows Installer component containing an assembly. This method prompts for a source and /// increments the usage count for the feature. /// /// Assembly name /// Set to null for global assemblies. For private assemblies, set to the full path of the /// application configuration file (.cfg file) or executable file (.exe) of the application to which the assembly /// has been made private. /// Installation mode; this can also include bits from /// True if this is a Win32 assembly, false if it is a .NET assembly /// Path to the assembly ///

/// Win32 MSI API: /// MsiProvideAssembly ///

public static string ProvideAssembly(string assemblyName, string appContext, InstallMode installMode, bool isWin32Assembly) { StringBuilder pathBuf = new StringBuilder(512); uint pathBufSize = (uint) pathBuf.Capacity; uint ret = NativeMethods.MsiProvideAssembly(assemblyName, appContext, unchecked ((uint) installMode), (isWin32Assembly ? (uint) 1 : 0), pathBuf, ref pathBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { pathBuf.Capacity = (int) ++pathBufSize; ret = NativeMethods.MsiProvideAssembly(assemblyName, appContext, unchecked ((uint) installMode), (isWin32Assembly ? (uint) 1 : 0), pathBuf, ref pathBufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return pathBuf.ToString(); } /// /// Installs files that are unexpectedly missing. /// /// Product code for the product that owns the component to be installed /// Component to be installed /// Specifies the way the component should be installed. /// the user exited the installation ///

/// Win32 MSI API: /// MsiInstallMissingComponent ///

public static void InstallMissingComponent(string product, string component, InstallState installState) { uint ret = NativeMethods.MsiInstallMissingComponent(product, component, (int) installState); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } /// /// Installs files that are unexpectedly missing. /// /// Product code for the product that owns the file to be installed /// File to be installed /// the user exited the installation ///

/// Win32 MSI API: /// MsiInstallMissingFile ///

public static void InstallMissingFile(string product, string file) { uint ret = NativeMethods.MsiInstallMissingFile(product, file); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } /// /// Reinstalls a feature. /// /// Product code for the product containing the feature to be reinstalled /// Feature to be reinstalled /// Reinstall modes /// the user exited the installation ///

/// Win32 MSI API: /// MsiReinstallFeature ///

public static void ReinstallFeature(string product, string feature, ReinstallModes reinstallModes) { uint ret = NativeMethods.MsiReinstallFeature(product, feature, (uint) reinstallModes); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } /// /// Reinstalls a product. /// /// Product code for the product to be reinstalled /// Reinstall modes /// the user exited the installation ///

/// Win32 MSI API: /// MsiReinstallProduct ///

public static void ReinstallProduct(string product, ReinstallModes reinstallModes) { uint ret = NativeMethods.MsiReinstallProduct(product, (uint) reinstallModes); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } /// /// Opens an installer package and initializes an install session. /// /// path to the patch package /// command line property settings /// There was an error installing the product ///

/// To completely remove a product, set REMOVE=ALL in . ///

/// This method displays the user interface with the current settings and /// log mode. You can change user interface settings with the /// and functions. You can set the log mode with the /// function. ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI API: /// MsiInstallProduct ///

public static void InstallProduct(string packagePath, string commandLine) { uint ret = NativeMethods.MsiInstallProduct(packagePath, commandLine); Installer.CheckInstallResult(ret); } /// /// Installs or uninstalls a product. /// /// Product code of the product to be configured. /// Specifies the default installation configuration of the /// product. The parameter is ignored and all features /// are installed if the parameter is set to any other /// value than . This parameter must be either 0 /// (install using authored feature levels), 65535 (install all features), or a value /// between 0 and 65535 to install a subset of available features. /// Specifies the installation state for the product. /// Specifies the command line property settings. This should /// be a list of the format Property=Setting Property=Setting. /// There was an error configuring the product ///

/// This method displays the user interface with the current settings and /// log mode. You can change user interface settings with the /// and functions. You can set the log mode with the /// function. ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI APIs: /// MsiConfigureProduct, /// MsiConfigureProductEx ///

public static void ConfigureProduct(string productCode, int installLevel, InstallState installState, string commandLine) { uint ret = NativeMethods.MsiConfigureProductEx(productCode, installLevel, (int) installState, commandLine); Installer.CheckInstallResult(ret); } /// /// Configures the installed state for a product feature. /// /// Product code of the product to be configured. /// Specifies the feature ID for the feature to be configured. /// Specifies the installation state for the feature. /// There was an error configuring the feature ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI API: /// MsiConfigureFeature ///

public static void ConfigureFeature(string productCode, string feature, InstallState installState) { uint ret = NativeMethods.MsiConfigureFeature(productCode, feature, (int) installState); Installer.CheckInstallResult(ret); } /// /// For each product listed by the patch package as eligible to receive the patch, ApplyPatch invokes /// an installation and sets the PATCH property to the path of the patch package. /// /// path to the patch package /// optional command line property settings /// There was an error applying the patch ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI API: /// MsiApplyPatch ///

public static void ApplyPatch(string patchPackage, string commandLine) { Installer.ApplyPatch(patchPackage, null, InstallType.Default, commandLine); } /// /// For each product listed by the patch package as eligible to receive the patch, ApplyPatch invokes /// an installation and sets the PATCH property to the path of the patch package. /// /// path to the patch package /// path to the product to be patched, if installType /// is set to /// type of installation to patch /// optional command line property settings /// There was an error applying the patch ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI API: /// MsiApplyPatch ///

public static void ApplyPatch(string patchPackage, string installPackage, InstallType installType, string commandLine) { uint ret = NativeMethods.MsiApplyPatch(patchPackage, installPackage, (int) installType, commandLine); Installer.CheckInstallResult(ret); } /// /// Removes one or more patches from a single product. To remove a patch from /// multiple products, RemovePatches must be called for each product. /// /// List of patches to remove. Each patch can be specified by the GUID /// of the patch or the full path to the patch package. /// The ProductCode (GUID) of the product from which the patches /// are removed. This parameter cannot be null. /// optional command line property settings /// There was an error removing the patches ///

/// The and properties should be /// tested after calling this method. ///

/// Win32 MSI API: /// MsiRemovePatches ///

public static void RemovePatches(IList patches, string productCode, string commandLine) { if (patches == null || patches.Count == 0) { throw new ArgumentNullException("patches"); } if (productCode == null) { throw new ArgumentNullException("productCode"); } StringBuilder patchList = new StringBuilder(); foreach (string patch in patches) { if (patch != null) { if (patchList.Length != 0) { patchList.Append(';'); } patchList.Append(patch); } } if (patchList.Length == 0) { throw new ArgumentNullException("patches"); } uint ret = NativeMethods.MsiRemovePatches(patchList.ToString(), productCode, (int) InstallType.SingleInstance, commandLine); Installer.CheckInstallResult(ret); } /// /// Determines which patches apply to a specified product MSI and in what sequence. /// /// Full path to an MSI file that is the target product /// for the set of patches. /// An array of strings specifying the patches to be checked. Each item /// may be the path to an MSP file, the path an XML file, or just an XML blob. /// Callback to be invoked for each inapplicable patch, reporting the /// reason the patch is not applicable. This value may be left null if that information is not /// desired. /// An array of selected patch strings from , indicating /// the set of applicable patches. The items are re-ordered to be in the best sequence. ///

/// If an item in is a file path but does not end in .MSP or .XML, /// it is assumed to be an MSP file. ///

/// As this overload uses InstallContext.None, it does not consider the current state of /// the system. ///

/// Win32 MSI API: /// MsiDetermineApplicablePatches ///

public static IList DetermineApplicablePatches( string productPackage, string[] patches, InapplicablePatchHandler errorHandler) { return DetermineApplicablePatches(productPackage, patches, errorHandler, null, UserContexts.None); } /// /// Determines which patches apply to a specified product and in what sequence. If /// the product is installed, this method accounts for patches that have already been applied to /// the product and accounts for obsolete and superceded patches. /// /// The product that is the target for the set of patches. This may be /// either a ProductCode (GUID) of a product that is currently installed, or the path to a an /// MSI file. /// An array of strings specifying the patches to be checked. Each item /// may be the path to an MSP file, the path an XML file, or just an XML blob. /// Callback to be invoked for each inapplicable patch, reporting the /// reason the patch is not applicable. This value may be left null if that information is not /// desired. /// Specifies a security identifier (SID) of a user. This parameter restricts /// the context of enumeration for this user account. This parameter cannot be the special SID /// strings s-1-1-0 (everyone) or s-1-5-18 (local system). If is set to /// or , then /// must be null. For the current user context, /// can be null and can be set to /// or . /// Restricts the enumeration to per-user-unmanaged, per-user-managed, /// or per-machine context, or (if referring to an MSI) to no system context at all. This /// parameter can be , , /// , or . /// An array of selected patch strings from , indicating /// the set of applicable patches. The items are re-ordered to be in the best sequence. ///

/// If an item in is a file path but does not end in .MSP or .XML, /// it is assumed to be an MSP file. ///

/// Passing an InstallContext of None only analyzes the MSI file; it does not consider the /// current state of the system. You cannot use InstallContext.None with a ProductCode GUID. ///

/// Win32 MSI APIs: /// MsiDetermineApplicablePatches /// MsiDeterminePatchSequence ///

public static IList DetermineApplicablePatches( string product, string[] patches, InapplicablePatchHandler errorHandler, string userSid, UserContexts context) { if (String.IsNullOrEmpty(product)) { throw new ArgumentNullException("product"); } if (patches == null) { throw new ArgumentNullException("patches"); } NativeMethods.MsiPatchSequenceData[] sequenceData = new NativeMethods.MsiPatchSequenceData[patches.Length]; for (int i = 0; i < patches.Length; i++) { if (String.IsNullOrEmpty(patches[i])) { throw new ArgumentNullException("patches[" + i + "]"); } sequenceData[i].szPatchData = patches[i]; sequenceData[i].ePatchDataType = GetPatchStringDataType(patches[i]); sequenceData[i].dwOrder = -1; sequenceData[i].dwStatus = 0; } uint ret; if (context == UserContexts.None) { ret = NativeMethods.MsiDetermineApplicablePatches(product, (uint) sequenceData.Length, sequenceData); } else { ret = NativeMethods.MsiDeterminePatchSequence(product, userSid, context, (uint) sequenceData.Length, sequenceData); } if (errorHandler != null) { for (int i = 0; i < sequenceData.Length; i++) { if (sequenceData[i].dwOrder < 0 && sequenceData[i].dwStatus != 0) { errorHandler(sequenceData[i].szPatchData, InstallerException.ExceptionFromReturnCode(sequenceData[i].dwStatus)); } } } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } IList patchSeq = new List(patches.Length); for (int i = 0; i < sequenceData.Length; i++) { for (int j = 0; j < sequenceData.Length; j++) { if (sequenceData[j].dwOrder == i) { patchSeq.Add(sequenceData[j].szPatchData); } } } return patchSeq; } /// /// Applies one or more patches to products that are eligible to receive the patch. /// For each product listed by the patch package as eligible to receive the patch, ApplyPatch invokes /// an installation and sets the PATCH property to the path of the patch package. /// /// The set of patch packages to be applied. /// Each item is the full path to an MSP file. /// Provides the ProductCode of the product being patched. If this parameter /// is null, the patches are applied to all products that are eligible to receive these patches. /// optional command line property settings ///

/// Win32 MSI API: /// MsiApplyMultiplePatches ///

public static void ApplyMultiplePatches( IList patchPackages, string productCode, string commandLine) { if (patchPackages == null || patchPackages.Count == 0) { throw new ArgumentNullException("patchPackages"); } StringBuilder patchList = new StringBuilder(); foreach (string patch in patchPackages) { if (patch != null) { if (patchList.Length != 0) { patchList.Append(';'); } patchList.Append(patch); } } if (patchList.Length == 0) { throw new ArgumentNullException("patchPackages"); } uint ret = NativeMethods.MsiApplyMultiplePatches(patchList.ToString(), productCode, commandLine); Installer.CheckInstallResult(ret); } /// /// Extracts information from a patch that can be used to determine whether the patch /// applies on a target system. The method returns an XML string that can be provided to /// /// instead of the full patch file. /// /// Full path to the patch being queried. /// XML string containing patch data. ///

/// Win32 MSI API: /// MsiExtractPatchXMLData ///

public static string ExtractPatchXmlData(string patchPath) { StringBuilder buf = new StringBuilder(""); uint bufSize = 0; uint ret = NativeMethods.MsiExtractPatchXMLData(patchPath, 0, buf, ref bufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { buf.Capacity = (int) ++bufSize; ret = NativeMethods.MsiExtractPatchXMLData(patchPath, 0, buf, ref bufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return buf.ToString(); } /// /// [MSI 3.1] Migrates a user's application configuration data to a new SID. /// /// Previous user SID that data is to be migrated from /// New user SID that data is to be migrated to ///

/// Win32 MSI API: /// MsiNotifySidChange ///

public static void NotifySidChange(string oldSid, string newSid) { uint ret = NativeMethods.MsiNotifySidChange(oldSid, newSid); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } private static void CheckInstallResult(uint ret) { switch (ret) { case (uint) NativeMethods.Error.SUCCESS: break; case (uint) NativeMethods.Error.SUCCESS_REBOOT_REQUIRED: Installer.rebootRequired = true; break; case (uint) NativeMethods.Error.SUCCESS_REBOOT_INITIATED: Installer.rebootInitiated = true; break; default: throw InstallerException.ExceptionFromReturnCode(ret); } } private static int GetPatchStringDataType(string patchData) { if (patchData.IndexOf("<", StringComparison.Ordinal) >= 0 && patchData.IndexOf(">", StringComparison.Ordinal) >= 0) { return 2; // XML blob } else if (String.Compare(Path.GetExtension(patchData), ".xml", StringComparison.OrdinalIgnoreCase) == 0) { return 1; // XML file path } else { return 0; // MSP file path } } } }