by Klaus Graefensteiner
13. February 2011 08:57
Introduction
From time to time I come across a Microsoft Installer Package (MSI) that doesn’t install properly. When that happens I immediately Google for the MSI logging registry key, open Regedit.exe, change the registry key to enable logging, run the MSI package again, search for the log file in the %temp% folder, disable the logging flag in the registry and then try to analyze the log for clues why the install failed.
Yesterday I got an email from the creator of Window Clippings that version 3.0 is available for download. I downloaded the boot strapper executable and tried to run it. It seemed to work fine at the beginning, but suddenly disappeared after the initializing dialog. Of course, this is a case for the MSI debugging procedure mentioned in the paragraph above. But this time I decided to automate the whole thing with PowerShell.
Video
Script
Here is the script. All you need is to pass the path to the *.MSI or setup.exe file to the Debug-MSI function and you are ready to go.
<#
To enable Windows Installer logging
To enable Windows Installer logging yourself, open the registry with Regedit.exe and create the following path and keys:
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer
Reg_SZ: Logging
Value: voicewarmupx
The letters in the value field can be in any order. Each letter turns on a different logging mode. Each letter's actual function is as follows for MSI version 1.1:
v - Verbose output
o - Out-of-disk-space messages
i - Status messages
c - Initial UI parameters
e - All error messages
w - Non-fatal warnings
a - Start up of actions
r - Action-specific records
m - Out-of-memory or fatal exit information
u - User requests
p - Terminal properties
+ - Append to existing file
! - Flush each line to the log
x - Extra debugging information. The "x" flag is available only on Windows Server 2003 and later operating systems, and on the MSI redistributable version 3.0, and on later versions of the MSI redistributable.
"*" - Wildcard, log all information except for the v and the x option. To include the v and the x option, specify "/l*vx".
Note This should be used only for troubleshooting purposes and should not be left on because it will have adverse effects on system performance and disk space. Each time you use the Add/Remove Programs tool in Control Panel, a new Msi*.log file is created.
MSI Command Line Switches can be found here: http://msdn.microsoft.com/en-us/library/aa367988(v=vs.85).aspx
PowerShell Command Line Parameter Passing: http://www.tellingmachine.com/post/Passing-string-parameters-when-starting-a-process-using-PowerShell-e28093-What-works-and-what-doesne28099t.aspx
#>
function Enable-MSILogging
{
if((Test-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Installer" -Name "Logging" ) -eq $False)
{
New-Item -Path "HKLM:\Software\Policies\Microsoft\Windows\Installer" -Force
}
Set-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Installer" -Name "Logging" -Value "voicewarmupx" -Force
}
function Disable-MSILogging
{
if(Test-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Installer" -Name "Logging")
{
Remove-ItemProperty -Path "HKLM:\Software\Policies\Microsoft\Windows\Installer" -Name "Logging" -Force
}
}
function Open-YoungestMSILogFile
{
$YoungestMSILogFile = get-ChildItem -Path $env:temp -Filter "MSI*.log" | Sort-object -Property "LastWriteTime" -Descending | Select-Object -First 1
$YoungestMSILogFile
. $YoungestMSILogFile.FullName
}
function Open-AllMSILogFilesCreatedInTimeInterval([DateTime] $StartTime, [DateTime] $EndTime)
{
$SessionMSILogFiles = get-ChildItem -Path $env:temp -Filter "MSI*.log"
$StartTime
$EndTime
if($SessionMSILogFiles -ne $null)
{
foreach( $MSILogFile in $SessionMSILogFiles)
{
if($MSILogFile.LastWriteTime -gt $StartTime.AddSeconds(-1) -and $MSILogFile.LastWriteTime -lt $EndTime.AddSeconds(1))
{
. $MSILogFile.FullName
}
}
}
}
function Debug-MSI([string] $PathToInstaller)
{
Enable-MSILogging
$StartTime = Get-Date
Install-MSI -FilePath $PathToInstaller
$EndTime = Get-Date
Disable-MSILogging
Open-YoungestMSILogFile
Open-AllMSILogFilesCreatedInTimeInterval -StartTime $StartTime -EndTime $EndTime
}
function Install-MSI([string] $FilePath)
{
if(Test-Path -Path $FilePath)
{
$Installer = Get-Item -Path $FilePath
if( $Installer.Extension.ToLower() -eq ".exe")
{
Start-Process -FilePath $FilePath -Wait
}
elseif ( $Installer.Extension.ToLower() -eq ".msi")
{
$FilePathInQuotes = "`"{0}`"" -f $FilePath
$MSICommandLineArguments = "/i" , $FilePathInQuotes
$MSICommandLineArguments
Start-Process -FilePath "MSIExec" -ArgumentList $MSICommandLineArguments -Wait
}
}
}
function Test-ItemProperty([string] $Path, [string] $Name)
{
if(Test-Path $Path)
{
try
{
$ItemProperty = Get-ItemProperty -Path $Path -Name $Name -ErrorAction "SilentlyContinue"
if ($ItemProperty -ne $null)
{
return $true
}
else
{
return $false
}
}
catch
{
return $false
}
}
else
{
return $false
}
}
Debug-MSI -PathToInstaller "C:\Users\Klaus Graefensteiner\Desktop\MSILogging\Click4Time\Click4TimeSetup\Release\setup.exe"
#Debug-MSI -PathToInstaller "C:\Users\Klaus Graefensteiner\Desktop\MSILogging\Click4Time\Click4TimeSetup\Release\Click4TimeSetup.msi"
Download
The script and a Visual Studio 2010 project with a sample MSI and Setup file can be downloaded here: Debug-MSI.zip