public bool Seek(Record record) { if (record == null) { throw new ArgumentNullException("record"); } uint ret = RemotableNativeMethods.MsiViewModify((int)this.Handle, (int)ViewModifyMode.Seek, (int)record.Handle); record.IsFormatStringInvalid = true; if (ret == (uint)NativeMethods.Error.FUNCTION_FAILED) { return(false); } else if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return(true); }
/// <summary> /// Executes a built-in action, custom action, or user-interface wizard action. /// </summary> /// <param name="action">Name of the action to execute. Case-sensitive.</param> /// <param name="actionData">Optional data to be passed to a deferred custom action.</param> /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> /// <exception cref="InstallCanceledException">the user exited the installation</exception> /// <remarks><p> /// The DoAction method executes the action that corresponds to the name supplied. If the /// name is not recognized by the installer as a built-in action or as a custom action in /// the CustomAction table, the name is passed to the user-interface handler object, which /// can invoke a function or a dialog box. If a null action name is supplied, the installer /// uses the upper-case value of the ACTION property as the action to perform. If no property /// value is defined, the default action is performed, defined as "INSTALL". /// </p><p> /// Actions that update the system, such as the InstallFiles and WriteRegistryValues /// actions, cannot be run by calling MsiDoAction. The exception to this rule is if DoAction /// is called from a custom action that is scheduled in the InstallExecuteSequence table /// between the InstallInitialize and InstallFinalize actions. Actions that do not update the /// system, such as AppSearch or CostInitialize, can be called. /// </p><p> /// If the called action is a deferred, rollback, or commit custom action, then the supplied /// <paramref name="actionData"/> will be available via the <see cref="CustomActionData"/> /// property of that custom action's session. /// </p><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msidoaction.asp">MsiDoAction</a> /// </p></remarks> public void DoAction(string action, CustomActionData actionData) { if (String.IsNullOrEmpty(action)) { throw new ArgumentNullException("action"); } this.ValidateSessionAccess(); if (actionData != null) { this[action] = actionData.ToString(); } uint ret = RemotableNativeMethods.MsiDoAction((int)this.Handle, action); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } }
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; } }
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); } } }
/// <summary> /// Evaluates a logical expression containing symbols and values. /// </summary> /// <param name="condition">conditional expression</param> /// <returns>The result of the condition evaluation</returns> /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> /// <exception cref="ArgumentNullException">the condition is null or empty</exception> /// <exception cref="InvalidOperationException">the conditional expression is invalid</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msievaluatecondition.asp">MsiEvaluateCondition</a> /// </p></remarks> public bool EvaluateCondition(string condition) { if (String.IsNullOrEmpty(condition)) { throw new ArgumentNullException("condition"); } uint value = RemotableNativeMethods.MsiEvaluateCondition((int)this.Handle, condition); if (value == 0) { return(false); } else if (value == 1) { return(true); } else { throw new InvalidOperationException(); } }
/// <summary> /// Sets the value of a field to a string. /// </summary> /// <param name="field">Specifies the field to set.</param> /// <param name="value">new value of the field</param> /// <exception cref="ArgumentOutOfRangeException">The field is less than 0 or greater than the /// number of fields in the Record.</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msirecordsetstring.asp">MsiRecordSetString</a> /// </p></remarks> public void SetString(int field, string value) { this.CheckRange(field); if (value == null) { value = String.Empty; } uint ret = RemotableNativeMethods.MsiRecordSetString((int)this.Handle, (uint)field, value); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } // If we set the FormatString manually, then it should be valid again if (field == 0) { this.IsFormatStringInvalid = false; } }
private ICollection <ValidationErrorInfo> InternalValidate(ViewModifyMode mode, Record record) { uint ret = RemotableNativeMethods.MsiViewModify((int)this.Handle, (int)mode, (int)record.Handle); if (ret == (uint)NativeMethods.Error.INVALID_DATA) { ICollection <ValidationErrorInfo> errorInfo = new List <ValidationErrorInfo>(); while (true) { uint bufSize = 40; StringBuilder column = new StringBuilder("", (int)bufSize); int error = RemotableNativeMethods.MsiViewGetError((int)this.Handle, column, ref bufSize); if (error == -2 /*MSIDBERROR_MOREDATA*/) { column.Capacity = (int)++bufSize; error = RemotableNativeMethods.MsiViewGetError((int)this.Handle, column, ref bufSize); } if (error == -3 /*MSIDBERROR_INVALIDARG*/) { throw InstallerException.ExceptionFromReturnCode((uint)NativeMethods.Error.INVALID_PARAMETER); } else if (error == 0 /*MSIDBERROR_NOERROR*/) { break; } errorInfo.Add(new ValidationErrorInfo((ValidationError)error, column.ToString())); } return(errorInfo); } else if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return(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); }
private static int OpenSummaryInfo(string packagePath, bool enableWrite) { int summaryInfoHandle; int maxProperties = !enableWrite ? 0 : SummaryInfo.MAX_PROPERTIES; uint ret = RemotableNativeMethods.MsiGetSummaryInformation( 0, packagePath, (uint)maxProperties, out summaryInfoHandle); if (ret != 0) { if (ret == (uint)NativeMethods.Error.FILE_NOT_FOUND || ret == (uint)NativeMethods.Error.ACCESS_DENIED) { throw new FileNotFoundException(null, packagePath); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } return(summaryInfoHandle); }
/// <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> /// Gets the designated mode flag for the current install session. /// </summary> /// <param name="mode">The type of mode to be checked.</param> /// <returns>The value of the designated mode flag.</returns> /// <exception cref="InvalidHandleException">the Session handle is invalid</exception> /// <exception cref="ArgumentOutOfRangeException">an invalid mode flag was specified</exception> /// <remarks><p> /// Note that only the following run modes are available to read from /// a deferred custom action:<list type="bullet"> /// <item><description><see cref="InstallRunMode.Scheduled"/></description></item> /// <item><description><see cref="InstallRunMode.Rollback"/></description></item> /// <item><description><see cref="InstallRunMode.Commit"/></description></item> /// </list> /// </p><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetmode.asp">MsiGetMode</a> /// </p></remarks> public bool GetMode(InstallRunMode mode) { return(RemotableNativeMethods.MsiGetMode((int)this.Handle, (uint)mode)); }
public static int InvokeCustomAction(int sessionHandle, string entryPoint, IntPtr remotingDelegatePtr) { Session session = null; string assemblyName, className, methodName; MethodInfo method; try { MsiRemoteInvoke remotingDelegate = (MsiRemoteInvoke) Marshal.GetDelegateForFunctionPointer( remotingDelegatePtr, typeof(MsiRemoteInvoke)); RemotableNativeMethods.RemotingDelegate = remotingDelegate; sessionHandle = RemotableNativeMethods.MakeRemoteHandle(sessionHandle); session = new Session((IntPtr)sessionHandle, false); if (String.IsNullOrEmpty(entryPoint)) { throw new ArgumentNullException("entryPoint"); } if (!CustomActionProxy.FindEntryPoint( session, entryPoint, out assemblyName, out className, out methodName)) { return((int)ActionResult.Failure); } session.Log("Calling custom action {0}!{1}.{2}", assemblyName, className, methodName); method = CustomActionProxy.GetCustomActionMethod( session, assemblyName, className, methodName); if (method == null) { return((int)ActionResult.Failure); } } catch (Exception ex) { if (session != null) { try { session.Log("Exception while loading custom action:"); session.Log(ex.ToString()); } catch (Exception) { } } return((int)ActionResult.Failure); } try { // Set the current directory to the location of the extracted files. Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); object[] args = new object[] { session }; if (DebugBreakEnabled(new string[] { entryPoint, methodName })) { string message = String.Format(CultureInfo.InvariantCulture, "To debug your custom action, attach to process ID {0} (0x{0:x}) and click OK; otherwise, click Cancel to fail the custom action.", System.Diagnostics.Process.GetCurrentProcess().Id ); MessageResult button = NativeMethods.MessageBox( IntPtr.Zero, message, "Custom Action Breakpoint", (int)MessageButtons.OKCancel | (int)MessageIcon.Asterisk | (int)(MessageBoxStyles.TopMost | MessageBoxStyles.ServiceNotification) ); if (MessageResult.Cancel == button) { return((int)ActionResult.UserExit); } } ActionResult result = (ActionResult)method.Invoke(null, args); session.Close(); return((int)result); } catch (InstallCanceledException) { return((int)ActionResult.UserExit); } catch (Exception ex) { session.Log("Exception thrown by custom action:"); session.Log(ex.ToString()); return((int)ActionResult.Failure); } }
/// <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> /// <remarks><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(int fieldCount) : this((IntPtr)RemotableNativeMethods.MsiCreateRecord((uint)fieldCount, 0), true, (View)null) { }
private object this[uint property, Type type] { get { uint dataType; StringBuilder stringValue = new StringBuilder(""); uint bufSize = 0; int intValue; long timeValue = 0; uint ret = RemotableNativeMethods.MsiSummaryInfoGetProperty( (int)this.Handle, property, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); if (ret != 0 && dataType != (uint)VarEnum.VT_LPSTR) { throw InstallerException.ExceptionFromReturnCode(ret); } switch ((VarEnum)dataType) { case VarEnum.VT_EMPTY: { if (type == typeof(DateTime)) { return(DateTime.MinValue); } else if (type == typeof(string)) { return(String.Empty); } else if (type == typeof(short)) { return((short)0); } else { return((int)0); } } case VarEnum.VT_LPSTR: { if (ret == (uint)NativeMethods.Error.MORE_DATA) { stringValue.Capacity = (int)++bufSize; ret = RemotableNativeMethods.MsiSummaryInfoGetProperty( (int)this.Handle, property, out dataType, out intValue, ref timeValue, stringValue, ref bufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return(stringValue.ToString()); } case VarEnum.VT_I2: case VarEnum.VT_I4: { if (type == typeof(string)) { return(intValue.ToString(CultureInfo.InvariantCulture)); } else if (type == typeof(short)) { return((short)intValue); } else { return(intValue); } } case VarEnum.VT_FILETIME: { if (type == typeof(string)) { return(DateTime.FromFileTime(timeValue).ToString(CultureInfo.InvariantCulture)); } else { return(DateTime.FromFileTime(timeValue)); } } default: { throw new InstallerException(); } } } set { uint dataType = (uint)VarEnum.VT_NULL; string stringValue = ""; int intValue = 0; long timeValue = 0; if (type == typeof(short)) { dataType = (uint)VarEnum.VT_I2; intValue = (int)(short)value; // Double cast because value is a *boxed* short. } else if (type == typeof(int)) { dataType = (uint)VarEnum.VT_I4; intValue = (int)value; } else if (type == typeof(string)) { dataType = (uint)VarEnum.VT_LPSTR; stringValue = (string)value; } else // (type == typeof(DateTime)) { dataType = (uint)VarEnum.VT_FILETIME; timeValue = ((DateTime)value).ToFileTime(); } uint ret = NativeMethods.MsiSummaryInfoSetProperty( (int)this.Handle, property, dataType, intValue, ref timeValue, stringValue); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } } }
/// <summary> /// Gets the length of a record field. The count does not include the terminating null. /// </summary> /// <exception cref="ArgumentOutOfRangeException">The field is less than 0 or greater than the /// number of fields in the Record.</exception> /// <remarks><p> /// The returned data size is 0 if the field is null, non-existent, /// or an internal object pointer. The method also returns 0 if the handle is not a valid /// Record handle. /// </p><p> /// If the data is in integer format, the property returns 2 or 4. /// </p><p> /// If the data is in string format, the property returns the character count /// (not including the NULL terminator). /// </p><p> /// If the data is in stream format, the property returns the byte count. /// </p><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msirecorddatasize.asp">MsiRecordDataSize</a> /// </p></remarks> public int GetDataSize(int field) { this.CheckRange(field); return((int)RemotableNativeMethods.MsiRecordDataSize((int)this.Handle, (uint)field)); }
/// <summary> /// Reports whether a record field is null. /// </summary> /// <param name="field">Specifies the field to check.</param> /// <returns>True if the field is null, false otherwise.</returns> /// <exception cref="ArgumentOutOfRangeException">The field is less than 0 or greater than the /// number of fields in the Record.</exception> /// <remarks><p> /// Win32 MSI API: /// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msirecordisnull.asp">MsiRecordIsNull</a> /// </p></remarks> public bool IsNull(int field) { this.CheckRange(field); return(RemotableNativeMethods.MsiRecordIsNull((int)this.Handle, (uint)field)); }
protected override bool ReleaseHandle() { return(RemotableNativeMethods.MsiCloseHandle((int)this.handle) == 0); }