/// <summary> /// Executes the query specified by a SQL string. The query may not be a SELECT statement. /// </summary> /// <param name="sql">SQL query string</param> /// <param name="record">Optional Record object containing the values that replace /// the parameter tokens (?) in the SQL query.</param> /// <exception cref="BadQuerySyntaxException">the SQL syntax is invalid</exception> /// <exception cref="InstallerException">the View could not be executed</exception> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> /// <remarks><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseopenview.asp">MsiDatabaseOpenView</a>, /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiviewexecute.asp">MsiViewExecute</a> /// </p></remarks> internal void Execute(string sql, Record record) { if (String.IsNullOrEmpty(sql)) { throw new ArgumentNullException("sql"); } using (View view = this.OpenView(sql)) { view.Execute(record); } }
internal void SaveErrorRecord() { // TODO: pass an affinity handle here? int recordHandle = RemotableNativeMethods.MsiGetLastErrorRecord(0); if (recordHandle != 0) { using (Record errorRec = new Record((IntPtr) recordHandle, true, null)) { this.errorData = new object[errorRec.FieldCount]; for (int i = 0; i < this.errorData.Length; i++) { this.errorData[i] = errorRec[i + 1]; } } } else { this.errorData = null; } }
internal RecordStream(Record record, int field) : base() { this.record = record; this.field = field; }
private static IList<string> GetTablePrimaryKeys(Database db, string table) { if (table == "_Tables") { return new string[] { "Name" }; } else if (table == "_Columns") { return new string[] { "Table", "Number" }; } else if (table == "_Storages") { return new string[] { "Name" }; } else if (table == "_Streams") { return new string[] { "Name" }; } else { int hrec; uint ret = RemotableNativeMethods.MsiDatabaseGetPrimaryKeys( (int) db.Handle, table, out hrec); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } using (Record rec = new Record((IntPtr) hrec, true, null)) { string[] keys = new string[rec.FieldCount]; for (int i = 0; i < keys.Length; i++) { keys[i] = rec.GetString(i + 1); } return keys; } } }
internal MessageResult Message(InstallMessage messageType, Record record) { if (record == null) { throw new ArgumentNullException("record"); } int ret = RemotableNativeMethods.MsiProcessMessage((int) this.Handle, (uint) messageType, (int) record.Handle); if (ret < 0) { throw new InstallerException(); } else if (ret == (int) MessageResult.Cancel) { throw new InstallCanceledException(); } return (MessageResult) ret; }
/// <summary> /// Writes a message to the log, if logging is enabled. /// </summary> /// <param name="msg">The line to be written to the log</param> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiprocessmessage.asp">MsiProcessMessage</a> /// </p></remarks> internal void Log(string msg) { if (msg == null) { throw new ArgumentNullException("msg"); } using (Record rec = new Record(0)) { rec.FormatString = msg; this.Message(InstallMessage.Info, rec); } }
internal string FormatRecord(Record record, string format) { if (record == null) { throw new ArgumentNullException("record"); } return record.ToString(format, this); }
internal string Format(string format) { if (format == null) { throw new ArgumentNullException("format"); } using (Record formatRec = new Record(0)) { formatRec.FormatString = format; return formatRec.ToString(this); } }
/// <summary> /// [MSI 4.0] Gets the list of files that can be updated by one or more patches. /// </summary> /// <param name="productCode">ProductCode (GUID) of the product which is /// the target of the patches</param> /// <param name="patches">list of file paths of one or more patches to be /// analyzed</param> /// <returns>List of absolute paths of files that can be updated when the /// patches are applied on this system.</returns> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetpatchfilelist.asp">MsiGetPatchFileList</a> /// </p></remarks> internal static IList<string> GetPatchFileList(string productCode, IList<string> patches) { if (String.IsNullOrEmpty(productCode)) { throw new ArgumentNullException("productCode"); } if (patches == null || patches.Count == 0) { throw new ArgumentNullException("patches"); } 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"); } IntPtr phFileRecords; uint cFiles; uint ret = NativeMethods.MsiGetPatchFileList( productCode, patchList.ToString(), out cFiles, out phFileRecords); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } List<string> files = new List<string>(); for (uint i = 0; i < cFiles; i++) { int hFileRec = Marshal.ReadInt32(phFileRecords, (int) i); using (Record fileRec = new Record(hFileRec, true, null)) { files.Add(fileRec.GetString(1)); } } return files; }
/// <summary> /// Gets a formatted Windows Installer error message in a specified language. /// </summary> /// <param name="errorRecord">Error record containing the error number in the first field, and /// error-specific parameters in the other fields.</param> /// <param name="culture">The locale for the message.</param> /// <returns>The message string, or null if the error message or locale is not found.</returns> /// <remarks><p> /// Error numbers greater than 2000 refer to MSI "internal" errors, and are always /// returned in English. /// </p></remarks> internal static string GetErrorMessage(Record errorRecord, CultureInfo culture) { if (errorRecord == null) { throw new ArgumentNullException("errorRecord"); } int errorNumber; if (errorRecord.FieldCount < 1 || (errorNumber = (int) errorRecord.GetInteger(1)) == 0) { throw new ArgumentOutOfRangeException("errorRecord"); } string msg = Installer.GetErrorMessage(errorNumber, culture); if (msg != null) { errorRecord.FormatString = msg; msg = errorRecord.ToString((IFormatProvider)null); } return msg; }
/// <summary> /// Gets a formatted Windows Installer error message in the system default language. /// </summary> /// <param name="errorRecord">Error record containing the error number in the first field, and /// error-specific parameters in the other fields.</param> /// <returns>The message string, or null if the error message is not found.</returns> /// <remarks><p> /// Error numbers greater than 2000 refer to MSI "internal" errors, and are always /// returned in English. /// </p></remarks> internal static string GetErrorMessage(Record errorRecord) { return Installer.GetErrorMessage(errorRecord, null); }
/// <summary> /// Executes the specified SQL SELECT query and returns all results as strings. /// </summary> /// <param name="sql">SQL SELECT query string</param> /// <param name="record">Optional Record object containing the values that replace /// the parameter tokens (?) in the SQL query.</param> /// <returns>All results combined into an array</returns> /// <exception cref="BadQuerySyntaxException">the SQL syntax is invalid</exception> /// <exception cref="InstallerException">the View could not be executed</exception> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> /// <remarks><p> /// Multiple rows columns will be collapsed into a single on-dimensional list. /// </p><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseopenview.asp">MsiDatabaseOpenView</a>, /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiviewexecute.asp">MsiViewExecute</a>, /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiviewfetch.asp">MsiViewFetch</a> /// </p></remarks> internal IList<string> ExecuteStringQuery(string sql, Record record) { if (String.IsNullOrEmpty(sql)) { throw new ArgumentNullException("sql"); } using (View view = this.OpenView(sql)) { view.Execute(record); IList<string> results = new List<string>(); int fieldCount = 0; foreach (Record rec in view) using (rec) { if (fieldCount == 0) fieldCount = rec.FieldCount; for (int i = 1; i <= fieldCount; i++) { results.Add(rec.GetString(i)); } } return results; } }
/// <summary> /// Executes the specified SQL SELECT query and returns a single result. /// </summary> /// <param name="sql">SQL SELECT query string</param> /// <param name="record">Optional Record object containing the values that replace /// the parameter tokens (?) in the SQL query.</param> /// <returns>First field of the first result</returns> /// <exception cref="BadQuerySyntaxException">the SQL syntax is invalid</exception> /// <exception cref="InstallerException">the View could not be executed /// or the query returned 0 results</exception> /// <exception cref="InvalidHandleException">the Database handle is invalid</exception> /// <remarks><p> /// Win32 MSI APIs: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidatabaseopenview.asp">MsiDatabaseOpenView</a>, /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiviewexecute.asp">MsiViewExecute</a>, /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msiviewfetch.asp">MsiViewFetch</a> /// </p></remarks> internal object ExecuteScalar(string sql, Record record) { if (String.IsNullOrEmpty(sql)) { throw new ArgumentNullException("sql"); } View view = this.OpenView(sql); Record rec = null; try { view.Execute(record); rec = view.Fetch(); if (rec == null) { throw InstallerException.ExceptionFromReturnCode((uint) NativeMethods.Error.NO_MORE_ITEMS); } return rec[1]; } finally { if (rec != null) rec.Close(); view.Close(); } }