MsiCreationCommand.cs :  » Build-Systems » NAntContrib » NAnt » Contrib » Tasks » Msi » 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 » Build Systems » NAntContrib 
NAntContrib » NAnt » Contrib » Tasks » Msi » MsiCreationCommand.cs
//
// NAntContrib
//
// Copyright (C) 2004 Kraen Munck (kmc@innomate.com)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Based on original work by Jayme C. Edwards (jcedwards@users.sourceforge.net)
//

using System;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Xml;

using MsmMergeTypeLib;

using NAnt.Core;
using NAnt.Core.Types;

using NAnt.Contrib.Schemas.Msi;

namespace NAnt.Contrib.Tasks.Msi{
    /// <summary>
    /// Summary description for MsiTaskInfo.
    /// </summary>
    public class MsiCreationCommand : InstallerCreationCommand {
        public MsiCreationCommand(msi msi, Task task, Location location, XmlNode node)
            : base(msi, task, location, node) {
        }

        private msi msi {
            get { return (msi) this.MsiBase; }
        }

        protected override string TemplateFileName {
            get { return "MSITaskTemplate.msi"; }
        }

        protected override string ErrorTemplateFileName {
            get { return "MSITaskErrors.mst"; }
        }

        protected override string CabFileName {
            get {
                return Path.GetFileNameWithoutExtension(msi.output) + ".cab";
            }
        }

        protected override string AdvtExecuteName {
            get { return "AdvtExecuteSequence"; }
        }

        protected override void AddModuleComponentVirtual(InstallerDatabase database, InstallerTable modComponentTable, string componentName) {
        }

        protected override void LoadTypeSpecificDataFromTask(InstallerDatabase database, int lastSequence) {
            LoadLaunchCondition(database);
            LoadFeatures(database);

            // The database file must be closed for merging to succeed
            database.Close();

            LoadMergeModules(database.ArchivePath, TempFolderPath);

            // Reopen database after merging
            database.Open();

            ReorderFiles(database, ref lastSequence);
            LoadMedia(database, lastSequence);
            LoadBannerImage(database);
            LoadBackgroundImage(database);
            LoadLicense(database);
        }

        /// <summary>
        /// Loads the banner image.
        /// </summary>
        /// <param name="database">The MSI database.</param>
        private void LoadBannerImage(InstallerDatabase database) {
            // Try to open the Banner
            if (msi.banner != null) {
                string bannerFile = Path.Combine(Project.BaseDirectory, msi.banner);
                if (File.Exists(bannerFile)) {
                    Log(Level.Verbose, "Storing banner '{0}'.", bannerFile);

                    using (InstallerRecordReader reader = database.FindRecords("Binary", new InstallerSearchClause("Name", Comparison.Equals, "bannrbmp"))) {
                        if (reader.Read()) {
                            // Write the Banner file to the MSI database
                            reader.SetValue(1, new InstallerStream(bannerFile));
                            reader.Commit();
                        } else {
                            throw new BuildException("Banner Binary record not found in template database.",
                                Location);
                        }
                    }
                } else {
                    throw new BuildException(string.Format(CultureInfo.InvariantCulture, 
                        "Unable to open banner image '{0}'.", bannerFile), Location);
                }
            }
        }

        /// <summary>
        /// Loads the background image.
        /// </summary>
        /// <param name="database">The MSI database.</param>
        private void LoadBackgroundImage(InstallerDatabase database) {
            // Try to open the Background
            if (msi.background != null) {
                string bgFile = Path.Combine(Project.BaseDirectory, msi.background);
                if (File.Exists(bgFile)) {
                    Log(Level.Info, "Storing background '{0}'.", bgFile);

                    using (InstallerRecordReader reader = database.FindRecords("Binary", new InstallerSearchClause("Name", Comparison.Equals, "dlgbmp"))) {
                        if (reader.Read()) {
                            // Write the Background file to the MSI database
                            reader.SetValue(1, new InstallerStream(bgFile));
                            reader.Commit();
                        } else {
                            throw new BuildException("Background Binary record not found in template database.",
                                Location);
                        }
                    }
                } else {
                    throw new BuildException(string.Format(CultureInfo.InvariantCulture, 
                        "Unable to open background image '{0}'.", bgFile), Location);
                }
            }
        }

        /// <summary>
        /// Loads the license file.
        /// </summary>
        /// <param name="database">The MSI database.</param>
        private void LoadLicense(InstallerDatabase database) {
            if (msi.license != null) {
                // Find the License control
                using (InstallerRecordReader recordReader = database.FindRecords("Control", new InstallerSearchClause("Control", Comparison.Equals, "AgreementText"))) {
                    if (recordReader.Read()) {
                        string licFile = Path.Combine(Project.BaseDirectory, msi.license);
                        Log(Level.Verbose, "Storing license '{0}'.", licFile);

                        // make sure license exists
                        if (!File.Exists(licFile)) {
                            throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                                "License file '{0}' does not exist.", licFile),
                                Location);
                        }

                        StreamReader licenseFileReader = null;
                        try {
                            licenseFileReader = File.OpenText(licFile);
                        } catch (IOException ex) {
                            throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                                "Unable to open license file '{0}'.", licFile), 
                                Location, ex);
                        }

                        try {
                            recordReader.SetValue(9, licenseFileReader.ReadToEnd());
                            recordReader.Commit();
                        } finally {
                            licenseFileReader.Close();
                        }
                    } else {
                        throw new BuildException("Couldn't find AgreementText Control in template database.", 
                            Location);
                    }
                }            
            }
        }

        /// <summary>
        /// Loads records for the Media table.
        /// </summary>
        /// <param name="database">The MSI database.</param>
        /// <param name="LastSequence">The sequence number of the last file in the .cab.</param>
        private void LoadMedia(InstallerDatabase database, int LastSequence) {
            // Open the "Media" Table
            using (InstallerTable mediaTable = database.OpenTable("Media")) {
                mediaTable.InsertRecord("1", LastSequence.ToString(), null, 
                    "#" + Path.GetFileNameWithoutExtension(msi.output) + ".cab", null, null);
            }
        }

        /// <summary>
        /// Loads records for the Features table.
        /// </summary>
        /// <param name="database">The MSI database.</param>
        private void LoadFeatures(InstallerDatabase database) {
            if (msi.features == null) {
                return;
            }

            Log(Level.Verbose, "Adding Features:");

            using (InstallerTable featureTable = database.OpenTable("Feature"), conditionTable = database.OpenTable("Condition")) {
                // Add features from Task definition
                int order = 1;
                int depth = 1;

                foreach (MSIFeature feature in msi.features) {
                    AddFeature(featureTable, conditionTable, null, database, feature, depth, order);
                    order++;
                }
            }
        }

        /// <summary>
        /// Adds a feature record to the Features table.
        /// </summary>
        /// <param name="featureTable">The MSI database Feature table.</param>
        /// <param name="conditionTable">The MSI database Condition table.</param>
        /// <param name="ParentFeature">The name of this feature's parent.</param>
        /// <param name="database">The MSI database.</param>
        /// <param name="Feature">This Feature's Schema element.</param>
        /// <param name="Depth">The tree depth of this feature.</param>
        /// <param name="Order">The tree order of this feature.</param>
        private void AddFeature(InstallerTable featureTable, InstallerTable conditionTable, string ParentFeature,
            InstallerDatabase database, MSIFeature Feature, int Depth, int Order) {
            const int TypicalInstallLevel = 3;
            const int NonTypicalInstallLevel = 4;

            int featureInstallLevel = ( Feature.typical ? TypicalInstallLevel : NonTypicalInstallLevel );

            // Insert the Feature
            featureTable.InsertRecord( Feature.name, ParentFeature, Feature.title, Feature.description, Feature.display, 
                (Feature.typical ? TypicalInstallLevel : NonTypicalInstallLevel ), GetFeatureDirectory(Feature), Feature.attr);

            Log(Level.Verbose, "\t" + Feature.name);

            AddFeatureConditions(Feature, conditionTable);

            if (Feature.feature != null) {
                foreach (MSIFeature childFeature in Feature.feature) {
                    int newDepth = Depth + 1;
                    int newOrder = 1;

                    AddFeature(featureTable, conditionTable, Feature.name,
                        database, childFeature, newDepth, newOrder);
                    newOrder++;
                }
            }
        }

        private string GetFeatureDirectory(MSIFeature Feature) {
            if (Feature.directory != null) {
                return Feature.directory;
            } else {
                IEnumerator featComps = featureComponents.Keys.GetEnumerator();

                while (featComps.MoveNext()) {
                    string componentName = (string)featComps.Current;
                    string featureName = (string)featureComponents[componentName];

                    if (featureName == Feature.name) {
                        return (string)components[componentName];
                    }
                }

                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                    "Feature '{0}' needs to be assigned a component or directory.", 
                    Feature.name), Location);
            }
        }

        private void AddFeatureConditions(MSIFeature Feature, InstallerTable conditionTable) {
            if (Feature.conditions != null) {
                Log(Level.Verbose, "\t\tAdding Feature Conditions...");

                foreach (MSIFeatureCondition featureCondition in Feature.conditions) {
                    try {
                        // Insert the feature's condition
                        conditionTable.InsertRecord(Feature.name, featureCondition.level, featureCondition.expression);
                    } catch (Exception ex) {
                        Log(Level.Info, "Error adding feature condition: " + ex.Message);
                    }
                }

                Log(Level.Verbose, "Done");
            }
        }

        /// <summary>
        /// Loads records for the LaunchCondition table
        /// </summary>
        /// <param name="database">The MSI database.</param>
        private void LoadLaunchCondition(InstallerDatabase database) {
            // Add properties from Task definition
            if (msi.launchconditions != null) {
                Log(Level.Verbose, "Adding Launch Conditions:");

                // Open the Launch Condition Table
                using (InstallerTable table = database.OpenTable("LaunchCondition")) {
                    // Add binary data from Task definition
                    foreach (MSILaunchCondition launchCondition in msi.launchconditions) {
                        Log(Level.Verbose, "\t" + launchCondition.name);

                        table.InsertRecord(launchCondition.condition, launchCondition.description);
                    }
                }
            }
        }

        /// <summary>
        /// Merges Merge Modules into the MSI Database.
        /// </summary>
        /// <param name="Database">The MSI Database.</param>
        /// <param name="TempPath">The path to temporary files.</param>
        private void LoadMergeModules(string Database, string TempPath) {
            // If <mergemodules>...</mergemodules> exists in the nant msi task

            if (msi.mergemodules != null) {
                MsmMerge2Class mergeClass = new MsmMerge2Class();

                int index = 1;

                Log(Level.Verbose, "Storing Merge Modules:");

                if (!Directory.Exists(TempPath))
                    Directory.CreateDirectory(TempPath);

                // Merge module(s) assigned to a specific feature
                foreach (MSIMerge merge in msi.mergemodules) {
                    // Get each merge module file name assigned to this feature
                    NAntFileSet modules = merge.modules;

                    FileSet mergeSet = new FileSet();
                    mergeSet.Parent = this;
                    mergeSet.Project = Project;
                    mergeSet.NamespaceManager = NamespaceManager;

                    XmlElement modulesElem = (XmlElement)((XmlElement)_xmlNode).SelectSingleNode(
                        "nant:mergemodules/nant:merge[@feature='" + merge.feature + "']/nant:modules", 
                        NamespaceManager);

                    mergeSet.Initialize(modulesElem);

                    try {
                        mergeClass.OpenDatabase(Database);
                    } catch (FileLoadException fle) {
                        throw new BuildException("Error while opening the database for merging.", 
                            Location, fle);
                    }

                    // Iterate each module assigned to this feature
                    foreach (string mergeModule in mergeSet.FileNames) {
                        Log(Level.Verbose, "\t" + Path.GetFileName(mergeModule));

                        // Open the merge module (by Filename)
                        try {
                            mergeClass.OpenModule(mergeModule, 1033);
                        } catch (Exception ex) {
                            throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                                "File '{0}' cannot be opened.", mergeModule), Location, ex);
                        }

                        // Once the merge is complete, components in the module are attached to the
                        // feature identified by Feature. This feature is not created and must be
                        // an existing feature. Note that the Merge method gets all the feature
                        // references in the module and substitutes the feature reference for all
                        // occurrences of the null GUID in the module database.

                        if (merge.configurationitems != null) {
                            Log(Level.Verbose, "\t\tConfigurable item(s):");
                            Hashtable configurations = new Hashtable();
                            foreach (MSIConfigurationItem configItem in merge.configurationitems) {
                                if ((configItem.module == null || configItem.module.Equals(String.Empty)) || configItem.module.ToLower().Equals(mergeModule.ToLower()) || configItem.module.ToLower().Equals(Path.GetFileName(mergeModule.ToLower()))) {
                                    if (configItem.module == null || configItem.module.Equals(String.Empty)) {
                                        if (configurations[configItem.name] == null) {
                                            configurations[configItem.name] = configItem.value;
                                            Log(Level.Verbose, "\t\t\t{0}\tValue: {1}", 
                                                configItem.name, configItem.value);
                                        }
                                    } else {
                                        configurations[configItem.name] = configItem.value;
                                        Log(Level.Verbose, "\t\t\t{0}\tValue: {1}", 
                                            configItem.name, configItem.value);
                                    }
                                }
                            }
                            mergeClass.MergeEx(merge.feature, null, new MsmConfigureModule(configurations));
                        } else {
                            mergeClass.Merge(merge.feature, null);
                        }

                        string moduleCab = Path.Combine(Path.GetDirectoryName(Database),
                            "mergemodule" + index + ".cab");

                        index++;

                        mergeClass.ExtractCAB(moduleCab);

                        if (File.Exists(moduleCab)) {
                            // Extract the cabfile contents to a Temp directory
                            try {
                                ExtractCabFileToTempDirectory(moduleCab);
                            } finally {
                                File.Delete(moduleCab);
                            }
                        }
                        mergeClass.CloseModule();
                    }
                    // Close and save the database
                    mergeClass.CloseDatabase(true);

                }
            }
        }

        private void ExtractCabFileToTempDirectory(string moduleCab) {
            Process process = new Process();
            ProcessStartInfo processInfo = new ProcessStartInfo();

            // paths to cabarc need to be quoted, however the destination path
            // must end in a \ so the last quote is left of (this seems to be ok)
            // cabarc does not work (nor does it raise an error) if you end the 
            // destination path with a quote.
            processInfo.Arguments = "-o X \"" +
                moduleCab + "\" \"" + TempFolderPath+ @"\";

            processInfo.CreateNoWindow = false;
            processInfo.WindowStyle = ProcessWindowStyle.Hidden;
            processInfo.WorkingDirectory = msi.output;
            processInfo.FileName = "cabarc";

            process.StartInfo = processInfo;
            process.EnableRaisingEvents = true;

            try {
                process.Start();
            } catch (Exception ex) {
                throw new BuildException("cabarc.exe failed.", Location, ex);
            }

            try {
                process.WaitForExit();
            } catch (Exception ex) {
                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                    "Error extracting merge module cab file '{0}'.", moduleCab), 
                    Location, ex);
            }

            if (process.ExitCode != 0) {
                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
                    "Error extracting merge module cab file '{0}'.\nExit code: {1}.", 
                    moduleCab, process.ExitCode), Location);
            }
        }
    }

    internal class MsmConfigureModule : IMsmConfigureModule {
        #region Private Instance Fields

        private Hashtable _configurations;

        #endregion Private Instance Fields

        #region Internal Instance Constructors

        internal MsmConfigureModule(Hashtable configurations) {
            _configurations = configurations;
        }

        #endregion Internal Instance Constructors

        #region Implementation of IMsmConfigureModule 

        string MsmMergeTypeLib.IMsmConfigureModule.ProvideTextData(string name) {
            if (_configurations[name] != null) {
                return (string) _configurations[name];
            }
            return null;
        }

        int MsmMergeTypeLib.IMsmConfigureModule.ProvideIntegerData(string name) {
            if (_configurations[name] != null) {
                return int.Parse((string)_configurations[name], 
                    CultureInfo.InvariantCulture);
            }
            return 0;
        }

        #endregion Implementation of IMsmConfigureModule 
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.