Database.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 » Database.cs
//---------------------------------------------------------------------
// <copyright file="Database.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.Collections.Generic;
    using System.Globalization;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// Accesses a Windows Installer database.
    /// </summary>
    /// <remarks><p>
    /// The <see cref="Commit"/> method must be called before the Database is closed to write out all
    /// persistent changes. If the Commit method is not called, the installer performs an implicit
    /// rollback upon object destruction.
    /// </p><p>
    /// The client can use the following procedure for data access:<list type="number">
    /// <item><description>Obtain a Database object using one of the Database constructors.</description></item> 
    /// <item><description>Initiate a query using a SQL string by calling the <see cref="OpenView"/>
    ///    method of the Database.</description></item> 
    /// <item><description>Set query parameters in a <see cref="Record"/> and execute the database
    ///    query by calling the <see cref="View.Execute(Record)"/> method of the <see cref="View"/>. This
    ///    produces a result that can be fetched or updated.</description></item> 
    /// <item><description>Call the <see cref="View.Fetch"/> method of the View repeatedly to return
    ///    Records.</description></item> 
    /// <item><description>Update database rows of a Record object obtained by the Fetch method using
    ///    one of the <see cref="View.Modify"/> methods of the View.</description></item> 
    /// <item><description>Release the query and any unfetched records by calling the <see cref="InstallerHandle.Close"/>
    ///    method of the View.</description></item> 
    /// <item><description>Persist any database updates by calling the Commit method of the Database.
    ///    </description></item> 
    /// </list>
    /// </p></remarks>
    public partial class Database : InstallerHandle
    {
        private string filePath;
        private DatabaseOpenMode openMode;
        private SummaryInfo summaryInfo;
        private TableCollection tables;
        private IList<string> deleteOnClose;

        /// <summary>
        /// Opens an existing database in read-only mode.
        /// </summary>
        /// <param name="filePath">Path to the database file.</param>
        /// <exception cref="InstallerException">the database could not be created/opened</exception>
        /// <remarks><p>
        /// Because this constructor initiates database access, it cannot be used with a
        /// running installation.
        /// </p><p>
        /// The Database 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/msiopendatabase.asp">MsiOpenDatabase</a>
        /// </p></remarks>
        public Database(string filePath)
            : this(filePath, DatabaseOpenMode.ReadOnly)
        {
        }

        /// <summary>
        /// Opens an existing database with another database as output.
        /// </summary>
        /// <param name="filePath">Path to the database to be read.</param>
        /// <param name="outputPath">Open mode for the database</param>
        /// <returns>Database object representing the created or opened database</returns>
        /// <exception cref="InstallerException">the database could not be created/opened</exception>
        /// <remarks><p>
        /// When a database is opened as the output of another database, the summary information stream
        /// of the output database is actually a read-only mirror of the original database and thus cannot
        /// be changed. Additionally, it is not persisted with the database. To create or modify the
        /// summary information for the output database it must be closed and re-opened.
        /// </p><p>
        /// The Database 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>
        /// The database is opened in <see cref="DatabaseOpenMode.CreateDirect" /> mode, and will be
        /// automatically commited when it is closed.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopendatabase.asp">MsiOpenDatabase</a>
        /// </p></remarks>
        public Database(string filePath, string outputPath)
            : this((IntPtr) Database.Open(filePath, outputPath), true, outputPath, DatabaseOpenMode.CreateDirect)
        {
        }

        /// <summary>
        /// Opens an existing database or creates a new one.
        /// </summary>
        /// <param name="filePath">Path to the database file. If an empty string
        /// is supplied, a temporary database is created that is not persisted.</param>
        /// <param name="mode">Open mode for the database</param>
        /// <exception cref="InstallerException">the database could not be created/opened</exception>
        /// <remarks><p>
        /// Because this constructor initiates database access, it cannot be used with a
        /// running installation.
        /// </p><p>
        /// The database object should be <see cref="InstallerHandle.Close"/>d after use.
        /// The finalizer will close the handle if it is still open, however due to the nondeterministic
        /// nature of finalization 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>
        /// A database opened in <see cref="DatabaseOpenMode.CreateDirect" /> or
        /// <see cref="DatabaseOpenMode.Direct" /> mode will be automatically commited when it is
        /// closed. However a database opened in <see cref="DatabaseOpenMode.Create" /> or
        /// <see cref="DatabaseOpenMode.Transact" /> mode must have the <see cref="Commit" /> method
        /// called before it is closed, otherwise no changes will be persisted.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiopendatabase.asp">MsiOpenDatabase</a>
        /// </p></remarks>
        public Database(string filePath, DatabaseOpenMode mode)
            : this((IntPtr) Database.Open(filePath, mode), true, filePath, mode)
        {
        }

        /// <summary>
        /// Creates a new database from an MSI handle.
        /// </summary>
        /// <param name="handle">Native MSI database handle.</param>
        /// <param name="ownsHandle">True if the handle should be closed
        /// when the database object is disposed</param>
        /// <param name="filePath">Path of the database file, if known</param>
        /// <param name="openMode">Mode the handle was originally opened in</param>
        protected internal Database(
            IntPtr handle, bool ownsHandle, string filePath, DatabaseOpenMode openMode)
            : base(handle, ownsHandle)
        {
            this.filePath = filePath;
            this.openMode = openMode;
        }

        /// <summary>
        /// Gets the file path the Database was originally opened from, or null if not known.
        /// </summary>
        public String FilePath
        {
            get
            {
                return this.filePath;
            }
        }

        /// <summary>
        /// Gets the open mode for the database.
        /// </summary>
        public DatabaseOpenMode OpenMode
        {
            get
            {
                return this.openMode;
            }
        }

        /// <summary>
        /// Gets a boolean value indicating whether this database was opened in read-only mode.
        /// </summary>
        /// <remarks><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetdatabasestate.asp">MsiGetDatabaseState</a>
        /// </p></remarks>
        public bool IsReadOnly
        {
            get
            {
                if (RemotableNativeMethods.RemotingEnabled)
                {
                    return true;
                }

                int state = NativeMethods.MsiGetDatabaseState((int) this.Handle);
                return state != 1;
            }
        }

        /// <summary>
        /// Gets the collection of tables in the Database.
        /// </summary>
        public TableCollection Tables
        {
            get
            {
                if (this.tables == null)
                {
                    this.tables = new TableCollection(this);
                }
                return this.tables;
            }
        }

        /// <summary>
        /// Gets or sets the code page of the Database.
        /// </summary>
        /// <exception cref="IOException">error exporting/importing the codepage data</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// Getting or setting the code page is a slow operation because it involves an export or import
        /// of the codepage data to/from a temporary file.
        /// </p></remarks>
        public int CodePage
        {
            get
            {
                string tempFile = Path.GetTempFileName();
                StreamReader reader = null;
                try
                {
                    this.Export("_ForceCodepage", tempFile);
                    reader = File.OpenText(tempFile);
                    reader.ReadLine();  // Skip column name record.
                    reader.ReadLine();  // Skip column defn record.
                    string codePageLine = reader.ReadLine();
                    return Int32.Parse(codePageLine.Split('\t')[0], CultureInfo.InvariantCulture.NumberFormat);
                }
                finally
                {
                    if (reader != null) reader.Close();
                    File.Delete(tempFile);
                }
            }

            set
            {
                string tempFile = Path.GetTempFileName();
                StreamWriter writer = null;
                try
                {
                    writer = File.AppendText(tempFile);
                    writer.WriteLine("");
                    writer.WriteLine("");
                    writer.WriteLine("{0}\t_ForceCodepage", value);
                    writer.Close();
                    writer = null;
                    this.Import(tempFile);
                }
                finally
                {
                    if (writer != null) writer.Close();
                    File.Delete(tempFile);
                }
            }
        }

        /// <summary>
        /// Gets the SummaryInfo object for this database that can be used to examine and modify properties
        /// to the summary information stream.
        /// </summary>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// The object returned from this property does not need to be explicitly persisted or closed.
        /// Any modifications will be automatically saved when the database is committed.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetsummaryinformation.asp">MsiGetSummaryInformation</a>
        /// </p></remarks>
        public SummaryInfo SummaryInfo
        {
            get
            {
                if (this.summaryInfo == null || this.summaryInfo.IsClosed)
                {
                    lock (this.Sync)
                    {
                        if (this.summaryInfo == null || this.summaryInfo.IsClosed)
                        {
                            int summaryInfoHandle;
                            int maxProperties = this.IsReadOnly ? 0 : SummaryInfo.MAX_PROPERTIES;
                            uint ret = RemotableNativeMethods.MsiGetSummaryInformation((int) this.Handle, null, (uint) maxProperties, out summaryInfoHandle);
                            if (ret != 0)
                            {
                                throw InstallerException.ExceptionFromReturnCode(ret);
                            }
                            this.summaryInfo = new SummaryInfo((IntPtr) summaryInfoHandle, true);
                        }
                    }
                }
                return this.summaryInfo;
            }
        }

        /// <summary>
        /// Creates a new Database object from an integer database handle.
        /// </summary>
        /// <remarks><p>
        /// This method is only provided for interop purposes.  A Database object
        /// should normally be obtained from <see cref="Session.Database"/> or
        /// a public Database constructor.
        /// </p></remarks>
        /// <param name="handle">Integer database handle</param>
        /// <param name="ownsHandle">true to close the handle when this object is disposed</param>
        public static Database FromHandle(IntPtr handle, bool ownsHandle)
        {
            return new Database(
                handle,
                ownsHandle,
                null,
                NativeMethods.MsiGetDatabaseState((int) handle) == 1 ? DatabaseOpenMode.Direct : DatabaseOpenMode.ReadOnly);
        }

        /// <summary>
        /// Schedules a file or directory for deletion after the database handle is closed.
        /// </summary>
        /// <param name="path">File or directory path to be deleted. All files and subdirectories
        /// under a directory are deleted.</param>
        /// <remarks><p>
        /// Once an item is scheduled, it cannot be unscheduled.
        /// </p><p>
        /// The items cannot be deleted if the Database object is auto-disposed by the
        /// garbage collector; the handle must be explicitly closed.
        /// </p><p>
        /// Files which are read-only or otherwise locked cannot be deleted,
        /// but they will not cause an exception to be thrown.
        /// </p></remarks>
        public void DeleteOnClose(string path)
        {
            if (this.deleteOnClose == null)
            {
                this.deleteOnClose = new List<string>();
            }
            this.deleteOnClose.Add(path);
        }

        /// <summary>
        /// Merges another database with this database.
        /// </summary>
        /// <param name="otherDatabase">The database to be merged into this database</param>
        /// <param name="errorTable">Optional name of table to contain the names of the tables containing
        /// merge conflicts, the number of conflicting rows within the table, and a reference to the table
        /// with the merge conflict.</param>
        /// <exception cref="MergeException">merge failed due to a schema difference or data conflict</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// Merge does not copy over embedded cabinet files or embedded transforms from the
        /// reference database into the target database. Embedded data streams that are listed in the
        /// Binary table or Icon table are copied from the reference database to the target database.
        /// Storage embedded in the reference database are not copied to the target database.
        /// </p><p>
        /// The Merge method merges the data of two databases. These databases must have the same
        /// codepage. The merge fails if any tables or rows in the databases conflict. A conflict exists
        /// if the data in any row in the first database differs from the data in the corresponding row
        /// of the second database. Corresponding rows are in the same table of both databases and have
        /// the same primary key in both databases. The tables of non-conflicting databases must have
        /// the same number of primary keys, same number of columns, same column types, same column names,
        /// and the same data in rows with identical primary keys. Temporary columns however don't matter
        /// in the column count and corresponding tables can have a different number of temporary columns
        /// without creating conflict as long as the persistent columns match.
        /// </p><p>
        /// If the number, type, or name of columns in corresponding tables are different, the
        /// schema of the two databases are incompatible and the installer will stop processing tables
        /// and the merge fails. The installer checks that the two databases have the same schema before
        /// checking for row merge conflicts. If the schemas are incompatible, the databases have be
        /// modified.
        /// </p><p>
        /// If the data in particular rows differ, this is a row merge conflict, the merge fails
        /// and creates a new table with the specified name. The first column of this table is the name
        /// of the table having the conflict. The second column gives the number of rows in the table
        /// having the conflict.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabasemerge.asp">MsiDatabaseMerge</a>
        /// </p></remarks>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
        public void Merge(Database otherDatabase, string errorTable)
        {
            if (otherDatabase == null)
            {
                throw new ArgumentNullException("otherDatabase");
            }

            uint ret = NativeMethods.MsiDatabaseMerge((int) this.Handle, (int) otherDatabase.Handle, errorTable);
            if (ret != 0)
            {
                if (ret == (uint) NativeMethods.Error.FUNCTION_FAILED)
                {
                    throw new MergeException(this, errorTable);
                }
                else if (ret == (uint) NativeMethods.Error.DATATYPE_MISMATCH)
                {
                    throw new MergeException("Schema difference between the two databases.");
                }
                else
                {
                    throw InstallerException.ExceptionFromReturnCode(ret);
                }
            }
        }

        /// <summary>
        /// Merges another database with this database.
        /// </summary>
        /// <param name="otherDatabase">The database to be merged into this database</param>
        /// <exception cref="MergeException">merge failed due to a schema difference or data conflict</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// MsiDatabaseMerge does not copy over embedded cabinet files or embedded transforms from
        /// the reference database into the target database. Embedded data streams that are listed in
        /// the Binary table or Icon table are copied from the reference database to the target database.
        /// Storage embedded in the reference database are not copied to the target database.
        /// </p><p>
        /// The Merge method merges the data of two databases. These databases must have the same
        /// codepage. The merge fails if any tables or rows in the databases conflict. A conflict exists
        /// if the data in any row in the first database differs from the data in the corresponding row
        /// of the second database. Corresponding rows are in the same table of both databases and have
        /// the same primary key in both databases. The tables of non-conflicting databases must have
        /// the same number of primary keys, same number of columns, same column types, same column names,
        /// and the same data in rows with identical primary keys. Temporary columns however don't matter
        /// in the column count and corresponding tables can have a different number of temporary columns
        /// without creating conflict as long as the persistent columns match.
        /// </p><p>
        /// If the number, type, or name of columns in corresponding tables are different, the
        /// schema of the two databases are incompatible and the installer will stop processing tables
        /// and the merge fails. The installer checks that the two databases have the same schema before
        /// checking for row merge conflicts. If the schemas are incompatible, the databases have be
        /// modified.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabasemerge.asp">MsiDatabaseMerge</a>
        /// </p></remarks>
        public void Merge(Database otherDatabase) { this.Merge(otherDatabase, null); }

        /// <summary>
        /// Checks whether a table exists and is persistent in the database.
        /// </summary>
        /// <param name="table">The table to the checked</param>
        /// <returns>true if the table exists and is persistent in the database; false otherwise</returns>
        /// <exception cref="ArgumentException">the table is unknown</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// To check whether a table exists regardless of persistence,
        /// use <see cref="TableCollection.Contains"/>.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseistablepersistent.asp">MsiDatabaseIsTablePersistent</a>
        /// </p></remarks>
        public bool IsTablePersistent(string table)
        {
            if (String.IsNullOrEmpty(table))
            {
                throw new ArgumentNullException("table");
            }
            uint ret = RemotableNativeMethods.MsiDatabaseIsTablePersistent((int) this.Handle, table);
            if (ret == 3)  // MSICONDITION_ERROR
            {
                throw new InstallerException();
            }
            return ret == 1;
        }

        /// <summary>
        /// Checks whether a table contains a persistent column with a given name.
        /// </summary>
        /// <param name="table">The table to the checked</param>
        /// <param name="column">The name of the column to be checked</param>
        /// <returns>true if the column exists in the table; false if the column is temporary or does not exist.</returns>
        /// <exception cref="InstallerException">the View could not be executed</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// To check whether a column exists regardless of persistence,
        /// use <see cref="ColumnCollection.Contains"/>.
        /// </p></remarks>
        public bool IsColumnPersistent(string table, string column)
        {
            if (String.IsNullOrEmpty(table))
            {
                throw new ArgumentNullException("table");
            }
            if (String.IsNullOrEmpty(column))
            {
                throw new ArgumentNullException("column");
            }
            using (View view = this.OpenView(
                "SELECT `Number` FROM `_Columns` WHERE `Table` = '{0}' AND `Name` = '{1}'", table, column))
            {
                view.Execute();
                using (Record rec = view.Fetch())
                {
                    return (rec != null);
                }
            }
        }

        /// <summary>
        /// Gets the count of all rows in the table.
        /// </summary>
        /// <param name="table">Name of the table whose rows are to be counted</param>
        /// <returns>The count of all rows in the table</returns>
        /// <exception cref="InstallerException">the View could not be executed</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        public int CountRows(string table)
        {
            return this.CountRows(table, null);
        }

        /// <summary>
        /// Gets the count of all rows in the table that satisfy a given condition.
        /// </summary>
        /// <param name="table">Name of the table whose rows are to be counted</param>
        /// <param name="where">Conditional expression, such as could be placed on the end of a SQL WHERE clause</param>
        /// <returns>The count of all rows in the table satisfying the condition</returns>
        /// <exception cref="BadQuerySyntaxException">the SQL WHERE syntax is invalid</exception>
        /// <exception cref="InstallerException">the View could not be executed</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        public int CountRows(string table, string where)
        {
            if (String.IsNullOrEmpty(table))
            {
                throw new ArgumentNullException("table");
            }

            int count;
            using (View view = this.OpenView(
                "SELECT `{0}` FROM `{1}`{2}",
                this.Tables[table].PrimaryKeys[0],
                table,
                (where != null && where.Length != 0 ? " WHERE " + where : "")))
            {
                view.Execute();
                for (count = 0; ; count++)
                {
                    // Avoid creating unnecessary Record objects by not calling View.Fetch().
                    int recordHandle;
                    uint ret = RemotableNativeMethods.MsiViewFetch((int) view.Handle, out recordHandle);
                    if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS)
                    {
                        break;
                    }

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

                    RemotableNativeMethods.MsiCloseHandle(recordHandle);
                }
            }
            return count;
        }

        /// <summary>
        /// Finalizes the persistent form of the database. All persistent data is written
        /// to the writeable database, and no temporary columns or rows are written.
        /// </summary>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// For a database open in <see cref="DatabaseOpenMode.ReadOnly"/> mode, this method has no effect.
        /// </p><p>
        /// For a database open in <see cref="DatabaseOpenMode.CreateDirect" /> or <see cref="DatabaseOpenMode.Direct" />
        /// mode, it is not necessary to call this method because the database will be automatically committed
        /// when it is closed. However this method may be called at any time to persist the current state of tables
        /// loaded into memory.
        /// </p><p>
        /// For a database open in <see cref="DatabaseOpenMode.Create" /> or <see cref="DatabaseOpenMode.Transact" />
        /// mode, no changes will be persisted until this method is called. If the database object is closed without
        /// calling this method, the database file remains unmodified.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabasecommit.asp">MsiDatabaseCommit</a>
        /// </p></remarks>
        public void Commit()
        {
            if (this.summaryInfo != null && !this.summaryInfo.IsClosed)
            {
                this.summaryInfo.Persist();
                this.summaryInfo.Close();
                this.summaryInfo = null;
            }
            uint ret = NativeMethods.MsiDatabaseCommit((int) this.Handle);
            if (ret != 0)
            {
                throw InstallerException.ExceptionFromReturnCode(ret);
            }
        }

        /// <summary>
        /// Copies the structure and data from a specified table to a text archive file.
        /// </summary>
        /// <param name="table">Name of the table to be exported</param>
        /// <param name="exportFilePath">Path to the file to be created</param>
        /// <exception cref="FileNotFoundException">the file path is invalid</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseexport.asp">MsiDatabaseExport</a>
        /// </p></remarks>
        public void Export(string table, string exportFilePath)
        {
            if (table == null)
            {
                throw new ArgumentNullException("table");
            }

            FileInfo file = new FileInfo(exportFilePath);
            uint ret = NativeMethods.MsiDatabaseExport((int) this.Handle, table, file.DirectoryName, file.Name);
            if (ret != 0)
            {
                if (ret == (uint) NativeMethods.Error.BAD_PATHNAME)
                {
                    throw new FileNotFoundException(null, exportFilePath);
                }
                else
                {
                    throw InstallerException.ExceptionFromReturnCode(ret);
                }
            }
        }

        /// <summary>
        /// Imports a database table from a text archive file, dropping any existing table.
        /// </summary>
        /// <param name="importFilePath">Path to the file to be imported. 
        /// The table name is specified within the file.</param>
        /// <exception cref="FileNotFoundException">the file path is invalid</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseimport.asp">MsiDatabaseImport</a>
        /// </p></remarks>
        public void Import(string importFilePath)
        {
            if (String.IsNullOrEmpty(importFilePath))
            {
                throw new ArgumentNullException("importFilePath");
            }

            FileInfo file = new FileInfo(importFilePath);
            uint ret = NativeMethods.MsiDatabaseImport((int) this.Handle, file.DirectoryName, file.Name);
            if (ret != 0)
            {
                if (ret == (uint) NativeMethods.Error.BAD_PATHNAME)
                {
                    throw new FileNotFoundException(null, importFilePath);
                }
                else
                {
                    throw InstallerException.ExceptionFromReturnCode(ret);
                }
            }
        }

        /// <summary>
        /// Exports all database tables, streams, and summary information to archive files.
        /// </summary>
        /// <param name="directoryPath">Path to the directory where archive files will be created</param>
        /// <exception cref="FileNotFoundException">the directory path is invalid</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// The directory will be created if it does not already exist.
        /// </p><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseexport.asp">MsiDatabaseExport</a>
        /// </p></remarks>
        public void ExportAll(string directoryPath)
        {
            if (String.IsNullOrEmpty(directoryPath))
            {
                throw new ArgumentNullException("directoryPath");
            }

            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
            }

            this.Export("_SummaryInformation", Path.Combine(directoryPath, "_SummaryInformation.idt"));

            using (View view = this.OpenView("SELECT `Name` FROM `_Tables`"))
            {
                view.Execute();

                foreach (Record rec in view) using (rec)
                {
                    string table = (string) rec[1];

                    this.Export(table, Path.Combine(directoryPath, table + ".idt"));
                }
            }

            if (!Directory.Exists(Path.Combine(directoryPath, "_Streams")))
            {
                Directory.CreateDirectory(Path.Combine(directoryPath, "_Streams"));
            }

            using (View view = this.OpenView("SELECT `Name`, `Data` FROM `_Streams`"))
            {
                view.Execute();

                foreach (Record rec in view) using (rec)
                {
                    string stream = (string) rec[1];
                    if (stream.EndsWith("SummaryInformation", StringComparison.Ordinal)) continue;

                    int i = stream.IndexOf('.');
                    if (i >= 0) 
                    {
                        if (File.Exists(Path.Combine(
                            directoryPath,
                            Path.Combine(stream.Substring(0, i), stream.Substring(i + 1) + ".ibd"))))
                        {
                            continue;
                        }
                    }
                    rec.GetStream(2, Path.Combine(directoryPath, Path.Combine("_Streams", stream)));
                }
            }
        }

        /// <summary>
        /// Imports all database tables, streams, and summary information from archive files.
        /// </summary>
        /// <param name="directoryPath">Path to the directory from which archive files will be imported</param>
        /// <exception cref="FileNotFoundException">the directory path is invalid</exception>
        /// <exception cref="InvalidHandleException">the Database handle is invalid</exception>
        /// <remarks><p>
        /// Win32 MSI API:
        /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseimport.asp">MsiDatabaseImport</a>
        /// </p></remarks>
        public void ImportAll(string directoryPath)
        {
            if (String.IsNullOrEmpty(directoryPath))
            {
                throw new ArgumentNullException("directoryPath");
            }

            if (File.Exists(Path.Combine(directoryPath, "_SummaryInformation.idt")))
            {
                this.Import(Path.Combine(directoryPath, "_SummaryInformation.idt"));
            }

            string[] idtFiles = Directory.GetFiles(directoryPath, "*.idt");
            foreach (string file in idtFiles)
            {
                if (Path.GetFileName(file) != "_SummaryInformation.idt")
                {
                    this.Import(file);
                }
            }

            if (Directory.Exists(Path.Combine(directoryPath, "_Streams")))
            {
                View view = this.OpenView("SELECT `Name`, `Data` FROM `_Streams`");
                Record rec = null;
                try
                {
                    view.Execute();
                    string[] streamFiles = Directory.GetFiles(Path.Combine(directoryPath, "_Streams"));
                    foreach (string file in streamFiles)
                    {
                        rec = this.CreateRecord(2);
                        rec[1] = Path.GetFileName(file);
                        rec.SetStream(2, file);
                        view.Insert(rec);
                        rec.Close();
                        rec = null;
                    }
                }
                finally
                {
                    if (rec != null) rec.Close();
                    view.Close();
                }
            }
        }

        /// <summary>
        /// Creates a new record object with the requested number of fields.
        /// </summary>
        /// <param name="fieldCount">Required number of fields, which may be 0.
        /// The maximum number of fields in a record is limited to 65535.</param>
        /// <returns>A new record object that can be used with the database.</returns>
        /// <remarks><p>
        /// This method is equivalent to directly calling the <see cref="Record" />
        /// constructor in all cases outside of a custom action context. When in a
        /// custom action session, this method allows creation of a record that can
        /// work with a database other than the session database.
        /// </p><p>
        /// The Record 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/msicreaterecord.asp">MsiCreateRecord</a>
        /// </p></remarks>
        public Record CreateRecord(int fieldCount)
        {
            int hRecord = RemotableNativeMethods.MsiCreateRecord((uint) fieldCount, (int) this.Handle);
            return new Record((IntPtr) hRecord, true, (View) null);
        }

        /// <summary>
        /// Returns the file path of this database, or the handle value if a file path was not specified.
        /// </summary>
        public override string ToString()
        {
            if (this.FilePath != null)
            {
                return this.FilePath;
            }
            else
            {
                return "#" + ((int) this.Handle).ToString(CultureInfo.InvariantCulture);
            }
        }

        /// <summary>
        /// Closes the database handle.  After closing a handle, further method calls may throw <see cref="InvalidHandleException"/>.
        /// </summary>
        /// <param name="disposing">If true, the method has been called directly or
        /// indirectly by a user's code, so managed and unmanaged resources will be
        /// disposed. If false, only unmanaged resources will be disposed.</param>
        protected override void Dispose(bool disposing)
        {
            if (!this.IsClosed &&
                (this.OpenMode == DatabaseOpenMode.CreateDirect ||
                 this.OpenMode == DatabaseOpenMode.Direct))
            {
                // Always commit a direct-opened database before closing.
                // This avoids unexpected corruption of the database.
                this.Commit();
            }

            base.Dispose(disposing);

            if (disposing)
            {
                if (this.summaryInfo != null)
                {
                    this.summaryInfo.Close();
                    this.summaryInfo = null;
                }

                if (this.deleteOnClose != null)
                {
                    foreach (string path in this.deleteOnClose)
                    {
                        try
                        {
                            if (Directory.Exists(path))
                            {
                                Directory.Delete(path, true);
                            }
                            else
                            {
                                if (File.Exists(path)) File.Delete(path);
                            }
                        }
                        catch (IOException)
                        {
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }
                    }
                    this.deleteOnClose = null;
                }
            }
        }

        private static int Open(string filePath, string outputPath)
        {
            if (String.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("filePath");
            }

            if (String.IsNullOrEmpty(outputPath))
            {
                throw new ArgumentNullException("outputPath");
            }

            int hDb;
            uint ret = NativeMethods.MsiOpenDatabase(filePath, outputPath, out hDb);
            if (ret != 0)
            {
                throw InstallerException.ExceptionFromReturnCode(ret);
            }
            return hDb;
        }

        private static int Open(string filePath, DatabaseOpenMode mode)
        {
            if (String.IsNullOrEmpty(filePath))
            {
                throw new ArgumentNullException("filePath");
            }

            if (Path.GetExtension(filePath).Equals(".msp", StringComparison.Ordinal))
            {
                const int DATABASEOPENMODE_PATCH = 32;
                int patchMode = (int) mode | DATABASEOPENMODE_PATCH;
                mode = (DatabaseOpenMode) patchMode;
            }

            int hDb;
            uint ret = NativeMethods.MsiOpenDatabase(filePath, (IntPtr) mode, out hDb);
            if (ret != 0)
            {
                throw InstallerException.ExceptionFromReturnCode(
                    ret,
                    String.Format(CultureInfo.InvariantCulture, "Database=\"{0}\"", filePath));
            }
            return hDb;
        }

        private string ExecutePropertyQuery(string property)
        {
            IList<string> values = this.ExecuteStringQuery("SELECT `Value` FROM `Property` WHERE `Property` = '{0}'", property);
            return (values.Count > 0 ? values[0] : null);
        }
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.