Debugging Windows Installer MSI Packages with PowerShell

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

Tags: , , , ,

PowerShell | Debugging

About Klaus Graefensteiner

I like the programming of machines.

Add to Google Reader or Homepage

LinkedIn FacebookTwitter View Klaus Graefensteiner's profile on Technorati
Klaus Graefensteiner

Klaus Graefensteiner
works as Developer In Test and is founder of the PowerShell Unit Testing Framework PSUnit. More...

Open Source Projects

PSUnit is a Unit Testing framwork for PowerShell. It is designed for simplicity and hosted by Codeplex.
BlogShell is The tool for lazy developers who like to automate the composition of blog content during the writing of a blog post. It is hosted by CodePlex.

Administration

About

Powered by:
BlogEngine.Net
Version: 1.6.1.0

License:
Creative Commons License

Copyright:
© Copyright 2014, Klaus Graefensteiner.

Disclaimer:
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Theme design:
This blog theme was designed and is copyrighted 2014 by Klaus Graefensteiner

Rendertime:
Page rendered at 4/18/2014 9:56:21 PM (PST Pacific Standard Time UTC DST -7)