Get-MsiExitCodeMessage: Get message for MSI exit code by reading it from msimsg.dll

Topics: Archive - Toolkit Extensions
Developer
Jul 31, 2014 at 3:29 AM
Edited Jul 31, 2014 at 3:34 AM
This function uses Write-Log and Write-ErrorStack functions posted in another thread in Toolkit Extensions.

You can use this function in the Execute-Process function in the following manner to automatically translate all MSI exit codes:
Else
{
    Write-Log -Message "Execution failed with exit code [$returnCode]" -Component ${CmdletName} -Severity 3
    If ($FilePath -match 'msiexec')
    {
        Get-MsiExitCodeMessage -MsiErrorCode $returnCode
    }
}
Function Get-MsiExitCodeMessage
{
<#
    .SYNOPSIS
        Get message for MSI error code
    .DESCRIPTION
        Get message for MSI error code by reading it from msimsg.dll
    .PARAMETER MsiErrorCode
        MSI error code
    .EXAMPLE
        Get-MsiExitCodeMessage -MsiErrorCode 1618
    .LINK
        http://msdn.microsoft.com/en-us/library/aa368542(v=vs.85).aspx
#>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullorEmpty()]
        [uint32]$MsiErrorCode
    )
    
    Begin
    {
        ${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
        
        $MsiErrorCodeMsgSource = @"
        /// Get error message from msimsg.dll resource dll
        using System;
        using System.Text;
        using System.Runtime.InteropServices;
        public class MsiErrorCode
        {
            public static string GetMessageFromMsiErrCode(uint errCode) 
            {
                string libPath = "msimsg.dll";
                IntPtr hModuleInstance = LoadLibraryEx(libPath, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE);
                
                StringBuilder sb = new StringBuilder(255);
                LoadString(hModuleInstance, errCode, sb, sb.Capacity + 1);
                
                return sb.ToString();
            }
            
            [DllImport("kernel32.dll")]
            static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, LoadLibraryFlags dwFlags);
            
            enum LoadLibraryFlags : uint
            {
                DONT_RESOLVE_DLL_REFERENCES         = 0x00000001,
                LOAD_IGNORE_CODE_AUTHZ_LEVEL        = 0x00000010,
                LOAD_LIBRARY_AS_DATAFILE            = 0x00000002,
                LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE  = 0x00000040,
                LOAD_LIBRARY_AS_IMAGE_RESOURCE      = 0x00000020,
                LOAD_WITH_ALTERED_SEARCH_PATH       = 0x00000008
            }
            
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
        }
"@

        If (-not ([System.Management.Automation.PSTypeName]'MsiErrorCode').Type)
        {
            Add-Type -TypeDefinition $MsiErrorCodeMsgSource -Language CSharp
        }
    }
    Process
    {
        Try
        {
            Write-Log -Message "Get message for MSI error code [$MsiErrorCode]" -Component ${CmdletName} -Severity 1
            [string]$MsiErrorCodeMsg = [MsiErrorCode]::GetMessageFromMsiErrCode($MsiErrorCode)
            Write-Output $MsiErrorCodeMsg
        }
        Catch
        {
            Write-Log -Message "Failed to get message for MSI error code `n$(Write-ErrorStack)" -Component ${CmdletName} -Severity 3
            Continue
        }
    }
    End
    {
        
    }
}