// 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];
}
}
}
}