public void InstallMsi(string msipath, bool uninstall) { string pars = ""; if (uninstall) { pars = "REMOVE=ALL"; } IntPtr ptr = IntPtr.Zero; InstallUIHandler handler = new InstallUIHandler(MyInstallUIHandler); //отключаем стандартный UI MsiSetInternalUI(INSTALLUILEVEL.INSTALLUILEVEL_NONE, ref ptr); //устанавливаем обработчик сообщений MsiSetExternalUI( handler, INSTALLLOGMODE.INSTALLLOGMODE_ACTIONSTART | INSTALLLOGMODE.INSTALLLOGMODE_ERROR, IntPtr.Zero ); //запускаем установку (удаление) MsiInstallProduct(msipath, pars); }
/// <summary> /// Instantiate a new Validator. /// </summary> public Validator(IMessaging messaging) { this.cubeFiles = new StringCollection(); this.extension = new ValidatorExtension(messaging); this.validationUIHandler = new InstallUIHandler(this.ValidationUIHandler); this.messaging = messaging; }
static extern InstallUIHandler MsiSetExternalUI(InstallUIHandler puiHandler, INSTALLLOGMODE dwMessageFilter, UIntPtr context);
internal static extern InstallUIHandler MsiSetExternalUI(InstallUIHandler installUIHandler, int installLogMode, IntPtr context);
internal static extern InstallUIHandler MsiSetExternalUI(InstallUIHandler installUIHandler, int installLogMode, IntPtr context);
/// <summary> /// Instantiate a new Validator. /// </summary> public Validator() { this.cubeFiles = new StringCollection(); this.extension = new ValidatorExtension(); this.validationUIHandler = new InstallUIHandler(this.ValidationUIHandler); }
/// <summary> /// Validate a database. /// </summary> /// <param name="databaseFile">The database to validate.</param> /// <returns>true if validation succeeded; false otherwise.</returns> public void Validate(string databaseFile) { Dictionary <string, string> indexedICEs = new Dictionary <string, string>(); Dictionary <string, string> indexedSuppressedICEs = new Dictionary <string, string>(); int previousUILevel = (int)InstallUILevels.Basic; IntPtr previousHwnd = IntPtr.Zero; InstallUIHandler previousUIHandler = null; if (null == databaseFile) { throw new ArgumentNullException("databaseFile"); } // initialize the validator extension this.extension.DatabaseFile = databaseFile; this.extension.Output = this.output; this.extension.InitializeValidator(); // if we don't have the temporary files object yet, get one if (null == this.tempFiles) { this.tempFiles = new TempFileCollection(); } Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there // index the ICEs if (null != this.ices) { foreach (string ice in this.ices) { indexedICEs[ice] = null; } } // index the suppressed ICEs if (null != this.suppressedICEs) { foreach (string suppressedICE in this.suppressedICEs) { indexedSuppressedICEs[suppressedICE] = null; } } // copy the database to a temporary location so it can be manipulated string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(databaseFile)); File.Copy(databaseFile, tempDatabaseFile); // remove the read-only property from the temporary database FileAttributes attributes = File.GetAttributes(tempDatabaseFile); File.SetAttributes(tempDatabaseFile, attributes & ~FileAttributes.ReadOnly); Mutex mutex = new Mutex(false, "WixValidator"); try { if (!mutex.WaitOne(0, false)) { this.OnMessage(WixVerboses.ValidationSerialized()); mutex.WaitOne(); } using (Database database = new Database(tempDatabaseFile, OpenDatabase.Direct)) { bool propertyTableExists = database.TableExists("Property"); string productCode = null; // remove the product code from the database before opening a session to prevent opening an installed product if (propertyTableExists) { using (View view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'")) { using (Record record = view.Fetch()) { if (null != record) { productCode = record.GetString(1); using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'")) { } } } } } // merge in the cube databases foreach (string cubeFile in this.cubeFiles) { try { using (Database cubeDatabase = new Database(cubeFile, OpenDatabase.ReadOnly)) { try { database.Merge(cubeDatabase, "MergeConflicts"); } catch { // ignore merge errors since they are expected in the _Validation table } } } catch (Win32Exception e) { if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED { throw new WixException(WixErrors.CubeFileNotFound(cubeFile)); } throw; } } // commit the database before proceeding to ensure the streams don't get confused database.Commit(); // the property table may have been added to the database // from a cub database without the proper validation rows if (!propertyTableExists) { using (View view = database.OpenExecuteView("DROP table `Property`")) { } } // get all the action names for ICEs which have not been suppressed List <string> actions = new List <string>(); using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`")) { while (true) { using (Record record = view.Fetch()) { if (null == record) { break; } string action = record.GetString(1); if (!indexedSuppressedICEs.ContainsKey(action)) { actions.Add(action); } } } } if (0 != indexedICEs.Count) { // Walk backwards and remove those that arent in the list for (int i = actions.Count - 1; 0 <= i; i--) { if (!indexedICEs.ContainsKey(actions[i])) { actions.RemoveAt(i); } } } // disable the internal UI handler and set an external UI handler previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd); previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero); // create a session for running the ICEs this.validationSessionComplete = false; using (Session session = new Session(database)) { // add the product code back into the database if (null != productCode) { // some CUBs erroneously have a ProductCode property, so delete it if we just picked one up using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'")) { } using (View view = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode))) { } } foreach (string action in actions) { this.actionName = action; try { session.DoAction(action); } catch (Win32Exception e) { if (!Messaging.Instance.EncounteredError) { throw e; } // TODO: Review why this was clearing the error state when an exception had happened but an error was already encountered. That's weird. //else //{ // this.encounteredError = false; //} } this.actionName = null; } // Mark the validation session complete so we ignore any messages that MSI may fire // during session clean-up. this.validationSessionComplete = true; } } } catch (Win32Exception e) { // avoid displaying errors twice since one may have already occurred in the UI handler if (!Messaging.Instance.EncounteredError) { if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED { // databaseFile is not passed since during light // this would be the temporary copy and there would be // no final output since the error occured; during smoke // they should know the path passed into smoke this.OnMessage(WixErrors.ValidationFailedToOpenDatabase()); } else if (0x64D == e.NativeErrorCode) { this.OnMessage(WixErrors.ValidationFailedDueToLowMsiEngine()); } else if (0x654 == e.NativeErrorCode) { this.OnMessage(WixErrors.ValidationFailedDueToInvalidPackage()); } else if (0x658 == e.NativeErrorCode) { this.OnMessage(WixErrors.ValidationFailedDueToMultilanguageMergeModule()); } else if (0x659 == e.NativeErrorCode) { this.OnMessage(WixWarnings.ValidationFailedDueToSystemPolicy()); } else { string msgTemp = e.Message; if (null != this.actionName) { msgTemp = String.Concat("Action - '", this.actionName, "' ", e.Message); } this.OnMessage(WixErrors.Win32Exception(e.NativeErrorCode, msgTemp)); } } } finally { Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero); Installer.SetInternalUI(previousUILevel, ref previousHwnd); this.validationSessionComplete = false; // no validation session at this point, so reset the completion flag. mutex.ReleaseMutex(); this.cubeFiles.Clear(); this.extension.FinalizeValidator(); } }
/// <summary> /// Enables an external user-interface handler. /// </summary> /// <param name="installUIHandler">Specifies a callback function.</param> /// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param> /// <param name="context">Pointer to an application context that is passed to the callback function.</param> /// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns> internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context) { return(MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context)); }
/// <summary> /// Validate a database. /// </summary> /// <param name="databaseFile">The database to validate.</param> /// <returns>true if validation succeeded; false otherwise.</returns> public bool Validate(string databaseFile) { InstallUIHandler currentUIHandler = null; Hashtable indexedSuppressedICEs = new Hashtable(); int previousUILevel = (int)InstallUILevels.Basic; IntPtr previousHwnd = IntPtr.Zero; InstallUIHandler previousUIHandler = null; if (null == databaseFile) { throw new ArgumentNullException("databaseFile"); } // initialize the validator extension this.extension.DatabaseFile = databaseFile; this.extension.Output = this.output; this.extension.InitializeValidator(); // if we don't have the temporary files object yet, get one if (null == this.tempFiles) { this.tempFiles = new TempFileCollection(); } Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there // index the suppressed ICEs if (null != this.suppressedICEs) { foreach (string suppressedICE in this.suppressedICEs) { indexedSuppressedICEs[suppressedICE] = null; } } // copy the database to a temporary location so it can be manipulated string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(databaseFile)); File.Copy(databaseFile, tempDatabaseFile); // remove the read-only property from the temporary database FileAttributes attributes = File.GetAttributes(tempDatabaseFile); File.SetAttributes(tempDatabaseFile, attributes & ~FileAttributes.ReadOnly); try { using (Database database = new Database(tempDatabaseFile, OpenDatabase.Direct)) { bool propertyTableExists = database.TableExists("Property"); string productCode = null; // remove the product code from the database before opening a session to prevent opening an installed product if (propertyTableExists) { using (View view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'")) { Record record = null; try { if (null != (record = view.Fetch())) { productCode = record.GetString(1); using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'")) { } } } finally { if (null != record) { record.Close(); } } } } // merge in the cube databases foreach (string cubeFile in this.cubeFiles) { try { using (Database cubeDatabase = new Database(cubeFile, OpenDatabase.ReadOnly)) { try { database.Merge(cubeDatabase, "MergeConflicts"); } catch { // ignore merge errors since they are expected in the _Validation table } } } catch (Win32Exception e) { if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED { throw new WixException(WixErrors.CubeFileNotFound(cubeFile)); } throw; } } // commit the database before proceeding to ensure the streams don't get confused database.Commit(); // the property table may have been added to the database // from a cub database without the proper validation rows if (!propertyTableExists) { using (View view = database.OpenExecuteView("DROP table `Property`")) { } } // get all the action names for ICEs which have not been suppressed ArrayList actions = new ArrayList(); using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`")) { Record record; while (null != (record = view.Fetch())) { string action = record.GetString(1); if (!indexedSuppressedICEs.Contains(action)) { actions.Add(action); } record.Close(); } } // disable the internal UI handler and set an external UI handler previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd); currentUIHandler = new InstallUIHandler(this.ValidationUIHandler); previousUIHandler = Installer.SetExternalUI(currentUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero); // create a session for running the ICEs using (Session session = new Session(database)) { // add the product code back into the database if (null != productCode) { using (View view = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode))) { } } foreach (string action in actions) { session.DoAction(action); } } } } catch (Win32Exception e) { // avoid displaying errors twice since one may have already occurred in the UI handler if (!this.encounteredError) { this.OnMessage(WixErrors.Win32Exception(e.NativeErrorCode, e.Message)); } } finally { Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero); Installer.SetInternalUI(previousUILevel, ref previousHwnd); // very important - this prevents the external UI delegate from being garbage collected too early GC.KeepAlive(currentUIHandler); this.cubeFiles.Clear(); this.extension.FinalizeValidator(); } return(!this.encounteredError); }
/// <summary> /// Instantiate a new Validator. /// </summary> public Validator() { this.cubeFiles = new StringCollection(); this.extension = new ValidatorExtension(); this.validationUIHandler = new InstallUIHandler(this.ValidationUIHandler); }
/// <summary> /// Enables an external user-interface handler. /// </summary> /// <param name="installUIHandler">Specifies a callback function.</param> /// <param name="messageFilter">Specifies which messages to handle using the external message handler.</param> /// <param name="context">Pointer to an application context that is passed to the callback function.</param> /// <returns>The return value is the previously set external handler, or null if there was no previously set handler.</returns> internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context) { return MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context); }
static extern uint MsiSetExternalUI(InstallUIHandler puiHandler, INSTALLLOGMODE dwMessageFilter, IntPtr pvContext);
public static extern InstallUIHandler MsiSetExternalUI([MarshalAs(UnmanagedType.FunctionPtr)] InstallUIHandler puiHandler, uint dwMessageFilter, IntPtr pvContext);
private void RunValidations() { var previousUILevel = (int)InstallUILevels.Basic; var previousHwnd = IntPtr.Zero; InstallUIHandler previousUIHandler = null; try { using (var database = new Database(this.DatabasePath, OpenDatabase.Direct)) { var propertyTableExists = database.TableExists("Property"); string productCode = null; // Remove the product code from the database before opening a session to prevent opening an installed product. if (propertyTableExists) { using (var view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'")) { using (var record = view.Fetch()) { if (null != record) { productCode = record.GetString(1); using (var dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'")) { } } } } } // Merge in the cube databases. foreach (var cubeFile in this.CubeFiles) { var findCubeFile = typeof(WindowsInstallerValidator).Assembly.FindFileRelativeToAssembly(Path.Combine(CubesFolder, cubeFile), searchNativeDllDirectories: false); if (!findCubeFile.Found) { throw new WixException(ErrorMessages.CubeFileNotFound(findCubeFile.Path)); } try { using (var cubeDatabase = new Database(findCubeFile.Path, OpenDatabase.ReadOnly)) { try { database.Merge(cubeDatabase, "MergeConflicts"); } catch { // ignore merge errors since they are expected in the _Validation table } } } catch (Win32Exception e) { if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED { throw new WixException(ErrorMessages.CubeFileNotFound(findCubeFile.Path)); } throw; } } // Commit the database before proceeding to ensure the streams don't get confused. database.Commit(); // The property table may have been added to the database from a cub database without the proper validation rows. if (!propertyTableExists) { using (var view = database.OpenExecuteView("DROP table `Property`")) { } } // Get all the action names for ICEs which have not been suppressed. var actions = new List <string>(); using (var view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`")) { foreach (var record in view.Records) { var action = record.GetString(1); if (!this.SuppressedIces.Contains(action) && this.Ices.Contains(action)) { actions.Add(action); } } } // Disable the internal UI handler and set an external UI handler. previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd); previousUIHandler = Installer.SetExternalUI(this.ValidationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero); // Create a session for running the ICEs. this.ValidationSessionInProgress = true; using (var session = new Session(database)) { // Add the product code back into the database. if (null != productCode) { // Some CUBs erroneously have a ProductCode property, so delete it if we just picked one up. using (var dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'")) { } using (var view = database.OpenExecuteView($"INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{productCode}')")) { } } foreach (var action in actions) { this.CurrentIce = action; try { session.DoAction(action); } catch (Win32Exception e) { if (!this.Callback.EncounteredError) { throw e; } } this.CurrentIce = null; } // Mark the validation session complete so we ignore any messages that MSI may fire // during session clean-up. this.ValidationSessionInProgress = false; } } } catch (Win32Exception e) { // Avoid displaying errors twice since one may have already occurred in the UI handler. if (!this.Callback.EncounteredError) { if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED { // The database path is not passed to this exception since inside wix.exe // this would be the temporary copy and there would be no final output becasue // this error occured; and during standalone validation they should know the path // passed in. throw new WixException(ErrorMessages.ValidationFailedToOpenDatabase()); } else if (0x64D == e.NativeErrorCode) { throw new WixException(ErrorMessages.ValidationFailedDueToLowMsiEngine()); } else if (0x654 == e.NativeErrorCode) { throw new WixException(ErrorMessages.ValidationFailedDueToInvalidPackage()); } else if (0x658 == e.NativeErrorCode) { throw new WixException(ErrorMessages.ValidationFailedDueToMultilanguageMergeModule()); } else if (0x659 == e.NativeErrorCode) { throw new WixException(WarningMessages.ValidationFailedDueToSystemPolicy()); } else { var msg = String.IsNullOrEmpty(this.CurrentIce) ? e.Message : $"Action - '{this.CurrentIce}' {e.Message}"; throw new WixException(ErrorMessages.Win32Exception(e.NativeErrorCode, msg)); } } } finally { this.ValidationSessionInProgress = false; Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero); Installer.SetInternalUI(previousUILevel, ref previousHwnd); } }
/// <summary> /// Creates a new <see cref="UserInterfaceHandler"/> instance to register an external event handler to process /// install messages and triggering separate events for each type of <see cref="InstallMessage"/>. /// </summary> /// <param name="messageFilter">Specifies which messages to handle.</param> public UserInterfaceHandler(InstallLogMode messageFilter) { _previousHandler = NativeMethods.MsiSetExternalUI(Handler, (uint)messageFilter, IntPtr.Zero); }