Example #1
0
        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);
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
        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;
            }
        }
Example #4
0
        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);
                }
            }
        }
Example #5
0
        /// <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();
            }
        }
Example #6
0
        /// <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;
            }
        }
Example #7
0
        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);
        }
Example #8
0
        /// <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);
        }
Example #9
0
        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);
        }
Example #10
0
        /// <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));
        }
Example #11
0
 /// <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);
            }
        }
Example #13
0
 /// <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)
 {
 }
Example #14
0
        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);
                }
            }
        }
Example #15
0
 /// <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));
 }
Example #16
0
 /// <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));
 }
Example #17
0
 protected override bool ReleaseHandle()
 {
     return(RemotableNativeMethods.MsiCloseHandle((int)this.handle) == 0);
 }