Installer.cs :  » Installers-Generators » WiX » Microsoft » Deployment » WindowsInstaller » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Installers Generators » WiX 
WiX » Microsoft » Deployment » WindowsInstaller » Installer.cs
//---------------------------------------------------------------------
// <copyright file="Installer.cs" company="Microsoft">
//    Copyright (c) Microsoft Corporation.  All rights reserved.
//    
//    The use and distribution terms for this software are covered by the
//    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
//    which can be found in the file CPL.TXT at the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by
//    the terms of this license.
//    
//    You must not remove this notice, or any other, from this software.
// </copyright>
// <summary>
// Part of the Deployment Tools Foundation project.
// </summary>
//---------------------------------------------------------------------

namespace Microsoft.Deployment.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;

/// <summary>
/// Receives an exception from
/// <see cref="Installer.DetermineApplicablePatches(string,string[],InapplicablePatchHandler,string,UserContexts)"/>
/// indicating the reason a particular patch is not applicable to a product.
/// </summary>
/// <param name="patch">MSP file path, XML file path, or XML blob that was passed to 
/// <see cref="Installer.DetermineApplicablePatches(string,string[],InapplicablePatchHandler,string,UserContexts)"/></param>
/// <param name="exception">exception indicating the reason the patch is not applicable</param>
/// <remarks><p>
/// If <paramref name="exception"/> is an <see cref="InstallerException"/> or subclass, then
/// its <see cref="InstallerException.ErrorCode"/> and <see cref="InstallerException.Message"/>
/// properties will indicate a more specific reason the patch was not applicable.
/// </p><p>
/// The <paramref name="exception"/> could also be a FileNotFoundException if the
/// patch string was a file path.
/// </p></remarks>
public delegate void InapplicablePatchHandler(string patch, Exception exception);

/// <summary>
/// Provides static methods for installing and configuring products and patches.
/// </summary>
public static partial class Installer
{
    private static bool rebootRequired;
    private static bool rebootInitiated;
    private static ResourceManager errorResources;

    /// <summary>
    /// Indicates whether a system reboot is required after running an installation or configuration operation.
    /// </summary>
    public static bool RebootRequired
    {
        get
        {
            return Installer.rebootRequired;
        }
    }

    /// <summary>
    /// Indicates whether a system reboot has been initiated after running an installation or configuration operation.
    /// </summary>
    public static bool RebootInitiated
    {
        get
        {
            return Installer.rebootInitiated;
        }
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="uiOptions">Specifies the level of complexity of the user interface</param>
    /// <param name="windowHandle">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.</param>
    /// <returns>The previous user interface level</returns>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetinternalui.asp">MsiSetInternalUI</a>
    /// </p></remarks>
    [SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference")]
    public static InstallUIOptions SetInternalUI(InstallUIOptions uiOptions, ref IntPtr windowHandle)
    {
        return (InstallUIOptions) NativeMethods.MsiSetInternalUI((uint) uiOptions, ref windowHandle);
    }
    
    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="uiOptions">Specifies the level of complexity of the user interface</param>
    /// <returns>The previous user interface level</returns>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisetinternalui.asp">MsiSetInternalUI</a>
    /// </p></remarks>
    public static InstallUIOptions SetInternalUI(InstallUIOptions uiOptions)
    {
        return (InstallUIOptions) NativeMethods.MsiSetInternalUI((uint) uiOptions, IntPtr.Zero);
    }

    /// <summary>
    /// Enables logging of the selected message type for all subsequent install sessions in
    /// the current process space.
    /// </summary>
    /// <param name="logModes">One or more mode flags specifying the type of messages to log</param>
    /// <param name="logFile">Full path to the log file.  A null path disables logging,
    /// in which case the logModes paraneter is ignored.</param>
    /// <exception cref="ArgumentException">an invalid log mode was specified</exception>
    /// <remarks>This method takes effect on any new installation processes.  Calling this
    /// method from within a custom action will not start logging for that installation.</remarks>
    public static void EnableLog(InstallLogModes logModes, string logFile)
    {
        Installer.EnableLog(logModes, logFile, false, true);
    }

    /// <summary>
    /// Enables logging of the selected message type for all subsequent install sessions in
    /// the current process space.
    /// </summary>
    /// <param name="logModes">One or more mode flags specifying the type of messages to log</param>
    /// <param name="logFile">Full path to the log file.  A null path disables logging,
    /// in which case the logModes paraneter is ignored.</param>
    /// <param name="append">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.</param>
    /// <param name="flushEveryLine">If true, the log will be flushed after every line.
    /// If false, the log will be flushed every 20 lines.  The default is true.</param>
    /// <exception cref="ArgumentException">an invalid log mode was specified</exception>
    /// <remarks><p>
    /// This method takes effect on any new installation processes.  Calling this
    /// method from within a custom action will not start logging for that installation.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienablelog.asp">MsiEnableLog</a>
    /// </p></remarks>
    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);
        }
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="productCode">The product code of the product.</param>
    /// <param name="feature">The feature to be used.</param>
    /// <param name="installMode">Must have the value <see cref="InstallMode.NoDetection"/>.</param>
    /// <returns>The installed state of the feature.</returns>
    /// <remarks><p>
    /// 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.
    /// </p><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiusefeature.asp">MsiUseFeature</a>,
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiusefeatureex.asp">MsiUseFeatureEx</a>
    /// </p></remarks>
    public static InstallState UseFeature(string productCode, string feature, InstallMode installMode)
    {
        int installState = NativeMethods.MsiUseFeatureEx(productCode, feature, unchecked ((uint) installMode), 0);
        return (InstallState) installState;
    }

    /// <summary>
    /// Opens an installer package for use with functions that access the product database and install engine,
    /// returning an Session object.
    /// </summary>
    /// <param name="packagePath">Path to the package</param>
    /// <param name="ignoreMachineState">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.</param>
    /// <returns>A Session object allowing access to the product database and install engine</returns>
    /// <exception cref="InstallerException">The product could not be opened</exception>
    /// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
    /// <remarks><p>
    /// 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.
    /// </p><p>
    /// A "safe" Session object ignores the current computer state when opening the package and prevents
    /// changes to the current computer state.
    /// </p><p>
    /// The Session object should be <see cref="InstallerHandle.Close"/>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.
    /// </p><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopenpackage.asp">MsiOpenPackage</a>,
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopenpackageex.asp">MsiOpenPackageEx</a>
    /// </p></remarks>
    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);
    }

    /// <summary>
    /// Opens an installer package for use with functions that access the product database and install engine,
    /// returning an Session object.
    /// </summary>
    /// <param name="database">Database used to create the session</param>
    /// <param name="ignoreMachineState">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.</param>
    /// <returns>A Session object allowing access to the product database and install engine</returns>
    /// <exception cref="InstallerException">The product could not be opened</exception>
    /// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
    /// <remarks><p>
    /// 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.
    /// </p><p>
    /// A "safe" Session object ignores the current computer state when opening the package and prevents
    /// changes to the current computer state.
    /// </p><p>
    /// The Session object should be <see cref="InstallerHandle.Close"/>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.
    /// </p><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopenpackage.asp">MsiOpenPackage</a>,
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopenpackageex.asp">MsiOpenPackageEx</a>
    /// </p></remarks>
    [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);
    }

    /// <summary>
    /// Opens an installer package for an installed product using the product code.
    /// </summary>
    /// <param name="productCode">Product code of the installed product</param>
    /// <returns>A Session object allowing access to the product database and install engine,
    /// or null if the specified product is not installed.</returns>
    /// <exception cref="ArgumentException">An unknown product was requested</exception>
    /// <exception cref="InstallerException">The product could not be opened</exception>
    /// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
    /// <remarks><p>
    /// 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.
    /// </p><p>
    /// The Session object should be <see cref="InstallerHandle.Close"/>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.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopenproduct.asp">MsiOpenProduct</a>
    /// </p></remarks>
    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);
    }

    /// <summary>
    /// Gets the full component path, performing any necessary installation. This method prompts for source if
    /// necessary and increments the usage count for the feature.
    /// </summary>
    /// <param name="product">Product code for the product that contains the feature with the necessary component</param>
    /// <param name="feature">Feature ID of the feature with the necessary component</param>
    /// <param name="component">Component code of the necessary component</param>
    /// <param name="installMode">Installation mode; this can also include bits from <see cref="ReinstallModes"/></param>
    /// <returns>Path to the component</returns>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiprovidecomponent.asp">MsiProvideComponent</a>
    /// </p></remarks>
    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, (uint) installMode, pathBuf, ref pathBufSize);
        if (ret == (uint) NativeMethods.Error.MORE_DATA)
        {
            pathBuf.Capacity = (int) ++pathBufSize;
            ret = NativeMethods.MsiProvideComponent(product, feature, component, (uint) installMode, pathBuf, ref pathBufSize);
        }

        if (ret != 0)
        {
            throw InstallerException.ExceptionFromReturnCode(ret);
        }
        return pathBuf.ToString();
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="component">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.</param>
    /// <param name="qualifier">Specifies a qualifier into a list of advertising components (from PublishComponent Table).</param>
    /// <param name="installMode">Installation mode; this can also include bits from <see cref="ReinstallModes"/></param>
    /// <param name="product">Optional; specifies the product to match that has published the qualified component.</param>
    /// <returns>Path to the component</returns>
    /// <remarks><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiprovidequalifiedcomponent.asp">MsiProvideQualifiedComponent</a>
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiprovidequalifiedcomponentex.asp">MsiProvideQualifiedComponentEx</a>
    /// </p></remarks>
    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, (uint) installMode, product, 0, 0, pathBuf, ref pathBufSize);
        if (ret == (uint) NativeMethods.Error.MORE_DATA)
        {
            pathBuf.Capacity = (int) ++pathBufSize;
            ret = NativeMethods.MsiProvideQualifiedComponentEx(component, qualifier, (uint) installMode, product, 0, 0, pathBuf, ref pathBufSize);
        }

        if (ret != 0)
        {
            throw InstallerException.ExceptionFromReturnCode(ret);
        }
        return pathBuf.ToString();
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="assemblyName">Assembly name</param>
    /// <param name="appContext">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.</param>
    /// <param name="installMode">Installation mode; this can also include bits from <see cref="ReinstallModes"/></param>
    /// <param name="isWin32Assembly">True if this is a Win32 assembly, false if it is a .NET assembly</param>
    /// <returns>Path to the assembly</returns>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiprovideassembly.asp">MsiProvideAssembly</a>
    /// </p></remarks>
    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, (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, (uint) installMode, (isWin32Assembly ? (uint) 1 : 0), pathBuf, ref pathBufSize);
        }

        if (ret != 0)
        {
            throw InstallerException.ExceptionFromReturnCode(ret);
        }
        return pathBuf.ToString();
    }

    /// <summary>
    /// Installs files that are unexpectedly missing.
    /// </summary>
    /// <param name="product">Product code for the product that owns the component to be installed</param>
    /// <param name="component">Component to be installed</param>
    /// <param name="installState">Specifies the way the component should be installed.</param>
    /// <exception cref="InstallCanceledException">the user exited the installation</exception>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiinstallmissingcomponent.asp">MsiInstallMissingComponent</a>
    /// </p></remarks>
    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);
        }
    }

    /// <summary>
    /// Installs files that are unexpectedly missing.
    /// </summary>
    /// <param name="product">Product code for the product that owns the file to be installed</param>
    /// <param name="file">File to be installed</param>
    /// <exception cref="InstallCanceledException">the user exited the installation</exception>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiinstallmissingfile.asp">MsiInstallMissingFile</a>
    /// </p></remarks>
    public static void InstallMissingFile(string product, string file)
    {
        uint ret = NativeMethods.MsiInstallMissingFile(product, file);
        if (ret != 0)
        {
            throw InstallerException.ExceptionFromReturnCode(ret);
        }
    }

    /// <summary>
    /// Reinstalls a feature.
    /// </summary>
    /// <param name="product">Product code for the product containing the feature to be reinstalled</param>
    /// <param name="feature">Feature to be reinstalled</param>
    /// <param name="reinstallModes">Reinstall modes</param>
    /// <exception cref="InstallCanceledException">the user exited the installation</exception>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msireinstallfeature.asp">MsiReinstallFeature</a>
    /// </p></remarks>
    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);
        }
    }

    /// <summary>
    /// Reinstalls a product.
    /// </summary>
    /// <param name="product">Product code for the product to be reinstalled</param>
    /// <param name="reinstallModes">Reinstall modes</param>
    /// <exception cref="InstallCanceledException">the user exited the installation</exception>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msireinstallproduct.asp">MsiReinstallProduct</a>
    /// </p></remarks>
    public static void ReinstallProduct(string product, ReinstallModes reinstallModes)
    {
        uint ret = NativeMethods.MsiReinstallProduct(product, (uint) reinstallModes);
        if (ret != 0)
        {
            throw InstallerException.ExceptionFromReturnCode(ret);
        }
    }

    /// <summary>
    /// Opens an installer package and initializes an install session.
    /// </summary>
    /// <param name="packagePath">path to the patch package</param>
    /// <param name="commandLine">command line property settings</param>
    /// <exception cref="InstallerException">There was an error installing the product</exception>
    /// <remarks><p>
    /// To completely remove a product, set REMOVE=ALL in <paramRef name="commandLine"/>.
    /// </p><p>
    /// This method displays the user interface with the current settings and
    /// log mode. You can change user interface settings with the <see cref="SetInternalUI(InstallUIOptions)"/>
    /// and <see cref="SetExternalUI(ExternalUIHandler,InstallLogModes)"/> functions. You can set the log mode with the
    /// <see cref="EnableLog(InstallLogModes,string)"/> function.
    /// </p><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiinstallproduct.asp">MsiInstallProduct</a>
    /// </p></remarks>
    public static void InstallProduct(string packagePath, string commandLine)
    {
        uint ret = NativeMethods.MsiInstallProduct(packagePath, commandLine);
        Installer.CheckInstallResult(ret);
    }

    /// <summary>
    /// Installs or uninstalls a product.
    /// </summary>
    /// <param name="productCode">Product code of the product to be configured.</param>
    /// <param name="installLevel">Specifies the default installation configuration of the
    /// product. The <paramref name="installLevel"/> parameter is ignored and all features
    /// are installed if the <paramref name="installState"/> parameter is set to any other
    /// value than <see cref="InstallState.Default"/>. 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.                                                                                         </param>
    /// <param name="installState">Specifies the installation state for the product.</param>
    /// <param name="commandLine">Specifies the command line property settings. This should
    /// be a list of the format Property=Setting Property=Setting.</param>
    /// <exception cref="InstallerException">There was an error configuring the product</exception>
    /// <remarks><p>
    /// This method displays the user interface with the current settings and
    /// log mode. You can change user interface settings with the <see cref="SetInternalUI(InstallUIOptions)"/>
    /// and <see cref="SetExternalUI(ExternalUIHandler,InstallLogModes)"/> functions. You can set the log mode with the
    /// <see cref="EnableLog(InstallLogModes,string)"/> function.
    /// </p><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiconfigureproduct.asp">MsiConfigureProduct</a>,
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiconfigureproductex.asp">MsiConfigureProductEx</a>
    /// </p></remarks>
    public static void ConfigureProduct(string productCode, int installLevel, InstallState installState, string commandLine)
    {
        uint ret = NativeMethods.MsiConfigureProductEx(productCode, installLevel, (int) installState, commandLine);
        Installer.CheckInstallResult(ret);
    }

    /// <summary>
    /// Configures the installed state for a product feature.
    /// </summary>
    /// <param name="productCode">Product code of the product to be configured.</param>
    /// <param name="feature">Specifies the feature ID for the feature to be configured.</param>
    /// <param name="installState">Specifies the installation state for the feature.</param>
    /// <exception cref="InstallerException">There was an error configuring the feature</exception>
    /// <remarks><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiconfigurefeature.asp">MsiConfigureFeature</a>
    /// </p></remarks>
    public static void ConfigureFeature(string productCode, string feature, InstallState installState)
    {
        uint ret = NativeMethods.MsiConfigureFeature(productCode, feature, (int) installState);
        Installer.CheckInstallResult(ret);
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="patchPackage">path to the patch package</param>
    /// <param name="commandLine">optional command line property settings</param>
    /// <exception cref="InstallerException">There was an error applying the patch</exception>
    /// <remarks><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiapplypatch.asp">MsiApplyPatch</a>
    /// </p></remarks>
    public static void ApplyPatch(string patchPackage, string commandLine)
    {
        Installer.ApplyPatch(patchPackage, null, InstallType.Default, commandLine);
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="patchPackage">path to the patch package</param>
    /// <param name="installPackage">path to the product to be patched, if installType
    /// is set to <see cref="InstallType.NetworkImage"/></param>
    /// <param name="installType">type of installation to patch</param>
    /// <param name="commandLine">optional command line property settings</param>
    /// <exception cref="InstallerException">There was an error applying the patch</exception>
    /// <remarks><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiapplypatch.asp">MsiApplyPatch</a>
    /// </p></remarks>
    public static void ApplyPatch(string patchPackage, string installPackage, InstallType installType, string commandLine)
    {
        uint ret = NativeMethods.MsiApplyPatch(patchPackage, installPackage, (int) installType, commandLine);
        Installer.CheckInstallResult(ret);
    }

    /// <summary>
    /// Removes one or more patches from a single product. To remove a patch from
    /// multiple products, RemovePatches must be called for each product.
    /// </summary>
    /// <param name="patches">List of patches to remove. Each patch can be specified by the GUID
    /// of the patch or the full path to the patch package.</param>
    /// <param name="productCode">The ProductCode (GUID) of the product from which the patches
    /// are removed.  This parameter cannot be null.</param>
    /// <param name="commandLine">optional command line property settings</param>
    /// <exception cref="InstallerException">There was an error removing the patches</exception>
    /// <remarks><p>
    /// The <see cref="RebootRequired"/> and <see cref="RebootInitiated"/> properties should be
    /// tested after calling this method.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiremovepatches.asp">MsiRemovePatches</a>
    /// </p></remarks>
    public static void RemovePatches(IList<string> 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);
    }

    /// <summary>
    /// Determines which patches apply to a specified product MSI and in what sequence.
    /// </summary>
    /// <param name="productPackage">Full path to an MSI file that is the target product
    /// for the set of patches.</param>
    /// <param name="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.</param>
    /// <param name="errorHandler">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.</param>
    /// <returns>An array of selected patch strings from <paramref name="patches"/>, indicating
    /// the set of applicable patches.  The items are re-ordered to be in the best sequence.</returns>
    /// <remarks><p>
    /// If an item in <paramref name="patches"/> is a file path but does not end in .MSP or .XML,
    /// it is assumed to be an MSP file.
    /// </p><p>
    /// As this overload uses InstallContext.None, it does not consider the current state of
    /// the system.
    /// </p><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidetermineapplicablepatches.asp">MsiDetermineApplicablePatches</a>
    /// </p></remarks>
    public static IList<string> DetermineApplicablePatches(
        string productPackage,
        string[] patches,
        InapplicablePatchHandler errorHandler)
    {
        return DetermineApplicablePatches(productPackage, patches, errorHandler, null, UserContexts.None);
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="product">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.</param>
    /// <param name="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.</param>
    /// <param name="errorHandler">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.</param>
    /// <param name="userSid">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 <paramref name="context"/> is set to
    /// <see cref="UserContexts.None"/> or <see cref="UserContexts.Machine"/>, then
    /// <paramref name="userSid"/> must be null. For the current user context, <paramref name="userSid"/>
    /// can be null and <paramref name="context"/> can be set to <see cref="UserContexts.UserManaged"/>
    /// or <see cref="UserContexts.UserUnmanaged"/>.</param>
    /// <param name="context">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 <see cref="UserContexts.Machine"/>, <see cref="UserContexts.UserManaged"/>,
    /// <see cref="UserContexts.UserUnmanaged"/>, or <see cref="UserContexts.None"/>.</param>
    /// <returns>An array of selected patch strings from <paramref name="patches"/>, indicating
    /// the set of applicable patches.  The items are re-ordered to be in the best sequence.</returns>
    /// <remarks><p>
    /// If an item in <paramref name="patches"/> is a file path but does not end in .MSP or .XML,
    /// it is assumed to be an MSP file.
    /// </p><p>
    /// 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.
    /// </p><p>
    /// Win32 MSI APIs:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidetermineapplicablepatches.asp">MsiDetermineApplicablePatches</a>
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msideterminepatchsequence.asp">MsiDeterminePatchSequence</a>
    /// </p></remarks>
    public static IList<string> 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, (int) 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<string> patchSeq = new List<string>(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;
    }

    /// <summary>
    /// 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.
    /// </summary>
    /// <param name="patchPackages">The set of patch packages to be applied.
    /// Each item is the full path to an MSP file.</param>
    /// <param name="productCode">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.</param>
    /// <param name="commandLine">optional command line property settings</param>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiapplymultiplepatches.asp">MsiApplyMultiplePatches</a>
    /// </p></remarks>
    public static void ApplyMultiplePatches(
        IList<string> 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);
    }

    /// <summary>
    /// 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
    /// <see cref="DetermineApplicablePatches(string,string[],InapplicablePatchHandler,string,UserContexts)"/>
    /// instead of the full patch file.
    /// </summary>
    /// <param name="patchPath">Full path to the patch being queried.</param>
    /// <returns>XML string containing patch data.</returns>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiextractpatchxmldata.asp">MsiExtractPatchXMLData</a>
    /// </p></remarks>
    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();
    }

    /// <summary>
    /// [MSI 3.1] Migrates a user's application configuration data to a new SID.
    /// </summary>
    /// <param name="oldSid">Previous user SID that data is to be migrated from</param>
    /// <param name="newSid">New user SID that data is to be migrated to</param>
    /// <remarks><p>
    /// Win32 MSI API:
    /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msinotifysidchange.asp">MsiNotifySidChange</a>
    /// </p></remarks>
    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
        }
    }
}
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.