// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. namespace WixToolset.Msi { using System; using System.ComponentModel; using System.Globalization; using WixToolset.Core.Native; /// /// Enumeration of different modify modes. /// public enum ModifyView { /// /// Writes current data in the cursor to a table row. Updates record if the primary /// keys match an existing row and inserts if they do not match. Fails with a read-only /// database. This mode cannot be used with a view containing joins. /// Assign = MsiInterop.MSIMODIFYASSIGN, /// /// Remove a row from the table. You must first call the Fetch function with the same /// record. Fails if the row has been deleted. Works only with read-write records. This /// mode cannot be used with a view containing joins. /// Delete = MsiInterop.MSIMODIFYDELETE, /// /// Inserts a record. Fails if a row with the same primary keys exists. Fails with a read-only /// database. This mode cannot be used with a view containing joins. /// Insert = MsiInterop.MSIMODIFYINSERT, /// /// Inserts a temporary record. The information is not persistent. Fails if a row with the /// same primary key exists. Works only with read-write records. This mode cannot be /// used with a view containing joins. /// InsertTemporary = MsiInterop.MSIMODIFYINSERTTEMPORARY, /// /// Inserts or validates a record in a table. Inserts if primary keys do not match any row /// and validates if there is a match. Fails if the record does not match the data in /// the table. Fails if there is a record with a duplicate key that is not identical. /// Works only with read-write records. This mode cannot be used with a view containing joins. /// Merge = MsiInterop.MSIMODIFYMERGE, /// /// Refreshes the information in the record. Must first call Fetch with the /// same record. Fails for a deleted row. Works with read-write and read-only records. /// Refresh = MsiInterop.MSIMODIFYREFRESH, /// /// Updates or deletes and inserts a record into a table. Must first call Fetch with /// the same record. Updates record if the primary keys are unchanged. Deletes old row and /// inserts new if primary keys have changed. Fails with a read-only database. This mode cannot /// be used with a view containing joins. /// Replace = MsiInterop.MSIMODIFYREPLACE, /// /// Refreshes the information in the supplied record without changing the position in the /// result set and without affecting subsequent fetch operations. The record may then /// be used for subsequent Update, Delete, and Refresh. All primary key columns of the /// table must be in the query and the record must have at least as many fields as the /// query. Seek cannot be used with multi-table queries. This mode cannot be used with /// a view containing joins. See also the remarks. /// Seek = MsiInterop.MSIMODIFYSEEK, /// /// Updates an existing record. Non-primary keys only. Must first call Fetch. Fails with a /// deleted record. Works only with read-write records. /// Update = MsiInterop.MSIMODIFYUPDATE } /// /// Wrapper class for MSI API views. /// internal sealed class View : MsiHandle { /// /// Constructor that creates a view given a database handle and a query. /// /// Handle to the database to run the query on. /// Query to be executed. public View(Database db, string query) { if (null == db) { throw new ArgumentNullException("db"); } if (null == query) { throw new ArgumentNullException("query"); } uint handle = 0; int error = MsiInterop.MsiDatabaseOpenView(db.Handle, query, out handle); if (0 != error) { throw new MsiException(error); } this.Handle = handle; } /// /// Executes a view with no customizable parameters. /// public void Execute() { this.Execute(null); } /// /// Executes a query substituing the values from the records into the customizable parameters /// in the view. /// /// Record containing parameters to be substituded into the view. public void Execute(Record record) { int error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle); if (0 != error) { throw new MsiException(error); } } /// /// Fetches the next row in the view. /// /// Returns the fetched record; otherwise null. public Record Fetch() { uint recordHandle; int error = MsiInterop.MsiViewFetch(this.Handle, out recordHandle); if (259 == error) { return null; } else if (0 != error) { throw new MsiException(error); } return new Record(recordHandle); } /// /// Updates a fetched record. /// /// Type of modification mode. /// Record to be modified. public void Modify(ModifyView type, Record record) { int error = MsiInterop.MsiViewModify(this.Handle, Convert.ToInt32(type, CultureInfo.InvariantCulture), record.Handle); if (0 != error) { throw new MsiException(error); } } /// /// Returns a record containing column names or definitions. /// /// Specifies a flag indicating what type of information is needed. Either MSICOLINFO_NAMES or MSICOLINFO_TYPES. /// The record containing information about the column. public Record GetColumnInfo(int columnType) { uint recordHandle; int error = MsiInterop.MsiViewGetColumnInfo(this.Handle, columnType, out recordHandle); if (0 != error) { throw new MsiException(error); } return new Record(recordHandle); } } }