// 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.Diagnostics; using System.Text; using WixToolset.Core.Native; /// /// Windows Installer message types. /// [Flags] internal enum InstallMessage { /// /// Premature termination, possibly fatal out of memory. /// FatalExit = 0x00000000, /// /// Formatted error message, [1] is message number in Error table. /// Error = 0x01000000, /// /// Formatted warning message, [1] is message number in Error table. /// Warning = 0x02000000, /// /// User request message, [1] is message number in Error table. /// User = 0x03000000, /// /// Informative message for log, not to be displayed. /// Info = 0x04000000, /// /// List of files in use that need to be replaced. /// FilesInUse = 0x05000000, /// /// Request to determine a valid source location. /// ResolveSource = 0x06000000, /// /// Insufficient disk space message. /// OutOfDiskSpace = 0x07000000, /// /// Progress: start of action, [1] action name, [2] description, [3] template for ACTIONDATA messages. /// ActionStart = 0x08000000, /// /// Action data. Record fields correspond to the template of ACTIONSTART message. /// ActionData = 0x09000000, /// /// Progress bar information. See the description of record fields below. /// Progress = 0x0A000000, /// /// To enable the Cancel button set [1] to 2 and [2] to 1. To disable the Cancel button set [1] to 2 and [2] to 0. /// CommonData = 0x0B000000, /// /// Sent prior to UI initialization, no string data. /// Initilize = 0x0C000000, /// /// Sent after UI termination, no string data. /// Terminate = 0x0D000000, /// /// Sent prior to display or authored dialog or wizard. /// ShowDialog = 0x0E000000 } /// /// Windows Installer log modes. /// [Flags] internal enum InstallLogModes { /// /// Premature termination of installation. /// FatalExit = (1 << ((int)InstallMessage.FatalExit >> 24)), /// /// The error messages are logged. /// Error = (1 << ((int)InstallMessage.Error >> 24)), /// /// The warning messages are logged. /// Warning = (1 << ((int)InstallMessage.Warning >> 24)), /// /// The user requests are logged. /// User = (1 << ((int)InstallMessage.User >> 24)), /// /// The status messages that are not displayed are logged. /// Info = (1 << ((int)InstallMessage.Info >> 24)), /// /// Request to determine a valid source location. /// ResolveSource = (1 << ((int)InstallMessage.ResolveSource >> 24)), /// /// The was insufficient disk space. /// OutOfDiskSpace = (1 << ((int)InstallMessage.OutOfDiskSpace >> 24)), /// /// The start of new installation actions are logged. /// ActionStart = (1 << ((int)InstallMessage.ActionStart >> 24)), /// /// The data record with the installation action is logged. /// ActionData = (1 << ((int)InstallMessage.ActionData >> 24)), /// /// The parameters for user-interface initialization are logged. /// CommonData = (1 << ((int)InstallMessage.CommonData >> 24)), /// /// Logs the property values at termination. /// PropertyDump = (1 << ((int)InstallMessage.Progress >> 24)), /// /// Sends large amounts of information to a log file not generally useful to users. /// May be used for technical support. /// Verbose = (1 << ((int)InstallMessage.Initilize >> 24)), /// /// Sends extra debugging information, such as handle creation information, to the log file. /// ExtraDebug = (1 << ((int)InstallMessage.Terminate >> 24)), /// /// Progress bar information. This message includes information on units so far and total number of units. /// See MsiProcessMessage for an explanation of the message format. /// This message is only sent to an external user interface and is not logged. /// Progress = (1 << ((int)InstallMessage.Progress >> 24)), /// /// If this is not a quiet installation, then the basic UI has been initialized. /// If this is a full UI installation, the full UI is not yet initialized. /// This message is only sent to an external user interface and is not logged. /// Initialize = (1 << ((int)InstallMessage.Initilize >> 24)), /// /// If a full UI is being used, the full UI has ended. /// If this is not a quiet installation, the basic UI has not yet ended. /// This message is only sent to an external user interface and is not logged. /// Terminate = (1 << ((int)InstallMessage.Terminate >> 24)), /// /// Sent prior to display of the full UI dialog. /// This message is only sent to an external user interface and is not logged. /// ShowDialog = (1 << ((int)InstallMessage.ShowDialog >> 24)), /// /// Files in use information. When this message is received, a FilesInUse Dialog should be displayed. /// FilesInUse = (1 << ((int)InstallMessage.FilesInUse >> 24)) } /// /// Windows Installer UI levels. /// [Flags] internal enum InstallUILevels { /// /// No change in the UI level. However, if phWnd is not Null, the parent window can change. /// NoChange = 0, /// /// The installer chooses an appropriate user interface level. /// Default = 1, /// /// Completely silent installation. /// None = 2, /// /// Simple progress and error handling. /// Basic = 3, /// /// Authored user interface with wizard dialog boxes suppressed. /// Reduced = 4, /// /// Authored user interface with wizards, progress, and errors. /// Full = 5, /// /// If combined with the Basic value, the installer shows simple progress dialog boxes but /// does not display a Cancel button on the dialog. This prevents users from canceling the install. /// Available with Windows Installer version 2.0. /// HideCancel = 0x20, /// /// If combined with the Basic value, the installer shows simple progress /// dialog boxes but does not display any modal dialog boxes or error dialog boxes. /// ProgressOnly = 0x40, /// /// If combined with any above value, the installer displays a modal dialog /// box at the end of a successful installation or if there has been an error. /// No dialog box is displayed if the user cancels. /// EndDialog = 0x80, /// /// If this value is combined with the None value, the installer displays only the dialog /// boxes used for source resolution. No other dialog boxes are shown. This value has no /// effect if the UI level is not INSTALLUILEVEL_NONE. It is used with an external user /// interface designed to handle all of the UI except for source resolution. In this case, /// the installer handles source resolution. This value is only available with Windows Installer 2.0 and later. /// SourceResOnly = 0x100 } /// /// Represents the Windows Installer, provides wrappers to /// create the top-level objects and access their methods. /// internal sealed class Installer { /// /// Protect the constructor. /// private Installer() { } /// /// Takes the path to a file and returns a 128-bit hash of that file. /// /// Path to file that is to be hashed. /// The value in this column must be 0. This parameter is reserved for future use. /// Int array that receives the returned file hash information. internal static void GetFileHash(string filePath, int options, out int[] hash) { MsiInterop.MSIFILEHASHINFO hashInterop = new MsiInterop.MSIFILEHASHINFO(); hashInterop.FileHashInfoSize = 20; int error = MsiInterop.MsiGetFileHash(filePath, Convert.ToUInt32(options), hashInterop); if (0 != error) { throw new MsiException(error); } Debug.Assert(20 == hashInterop.FileHashInfoSize); hash = new int[4]; hash[0] = hashInterop.Data0; hash[1] = hashInterop.Data1; hash[2] = hashInterop.Data2; hash[3] = hashInterop.Data3; } /// /// Returns the version string and language string in the format that the installer /// expects to find them in the database. If you just want version information, set /// lpLangBuf and pcchLangBuf to zero. If you just want language information, set /// lpVersionBuf and pcchVersionBuf to zero. /// /// Specifies the path to the file. /// Returns the file version. Set to 0 for language information only. /// Returns the file language. Set to 0 for version information only. internal static void GetFileVersion(string filePath, out string version, out string language) { int versionLength = 20; int languageLength = 20; StringBuilder versionBuffer = new StringBuilder(versionLength); StringBuilder languageBuffer = new StringBuilder(languageLength); int error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength); if (234 == error) { versionBuffer.EnsureCapacity(++versionLength); languageBuffer.EnsureCapacity(++languageLength); error = MsiInterop.MsiGetFileVersion(filePath, versionBuffer, ref versionLength, languageBuffer, ref languageLength); } else if (1006 == error) { // file has no version or language, so no error error = 0; } if (0 != error) { throw new MsiException(error); } version = versionBuffer.ToString(); language = languageBuffer.ToString(); } /// /// Enables an external user-interface handler. /// /// Specifies a callback function. /// Specifies which messages to handle using the external message handler. /// Pointer to an application context that is passed to the callback function. /// The return value is the previously set external handler, or null if there was no previously set handler. internal static InstallUIHandler SetExternalUI(InstallUIHandler installUIHandler, int messageFilter, IntPtr context) { return MsiInterop.MsiSetExternalUI(installUIHandler, messageFilter, context); } /// /// Enables the installer's internal user interface. /// /// Specifies the level of complexity of the user interface. /// Pointer to a window. This window becomes the owner of any user interface created. /// The previous user interface level is returned. If an invalid dwUILevel is passed, then INSTALLUILEVEL_NOCHANGE is returned. internal static int SetInternalUI(int uiLevel, ref IntPtr hwnd) { return MsiInterop.MsiSetInternalUI(uiLevel, ref hwnd); } /// /// Get the source/target and short/long file names from an MSI Filename column. /// /// The Filename value. /// An array of strings of length 4. The contents are: short target, long target, short source, and long source. /// /// If any particular file name part is not parsed, its set to null in the appropriate location of the returned array of strings. /// However, the returned array will always be of length 4. /// internal static string[] GetNames(string value) { string[] names = new string[4]; int targetSeparator = value.IndexOf(":", StringComparison.Ordinal); // split source and target string sourceName = null; string targetName = value; if (0 <= targetSeparator) { sourceName = value.Substring(targetSeparator + 1); targetName = value.Substring(0, targetSeparator); } // split the source short and long names string sourceLongName = null; if (null != sourceName) { int sourceLongNameSeparator = sourceName.IndexOf("|", StringComparison.Ordinal); if (0 <= sourceLongNameSeparator) { sourceLongName = sourceName.Substring(sourceLongNameSeparator + 1); sourceName = sourceName.Substring(0, sourceLongNameSeparator); } } // split the target short and long names int targetLongNameSeparator = targetName.IndexOf("|", StringComparison.Ordinal); string targetLongName = null; if (0 <= targetLongNameSeparator) { targetLongName = targetName.Substring(targetLongNameSeparator + 1); targetName = targetName.Substring(0, targetLongNameSeparator); } // remove the long source name when its identical to the long source name if (null != sourceName && sourceName == sourceLongName) { sourceLongName = null; } // remove the long target name when its identical to the long target name if (null != targetName && targetName == targetLongName) { targetLongName = null; } // remove the source names when they are identical to the target names if (sourceName == targetName && sourceLongName == targetLongName) { sourceName = null; sourceLongName = null; } // target name(s) if ("." != targetName) { names[0] = targetName; } if (null != targetLongName && "." != targetLongName) { names[1] = targetLongName; } // source name(s) if (null != sourceName) { names[2] = sourceName; } if (null != sourceLongName && "." != sourceLongName) { names[3] = sourceLongName; } return names; } /// /// Get a source/target and short/long file name from an MSI Filename column. /// /// The Filename value. /// true to get a source name; false to get a target name /// true to get a long name; false to get a short name /// The name. internal static string GetName(string value, bool source, bool longName) { string[] names = GetNames(value); if (source) { if (longName && null != names[3]) { return names[3]; } else if (null != names[2]) { return names[2]; } } if (longName && null != names[1]) { return names[1]; } else { return names[0]; } } } }