Potential Bug in 3.1.6/3.2.0 Beta

Topics: Archive - General
Aug 22, 2014 at 2:53 PM
Edited Aug 22, 2014 at 2:59 PM
Hi Sean

Seems like there is a bug in Function Execute-Process when called from Function Execute-MSI, I was using Function Execute-Process directly (not MSI deployment), so didn't find out this bug until now, getting this in the log,
[22-08-2014 16:36:35] [Installation] Execution failed 
Cannot add type. There were compilation errors.
This was a catch exception in Function Execute-Process, after a quick debug, I think this part is throwing out the exception.
# If MSI install, check to see if the MSI installer service is available or if another MSI install is already underway.
# Please note that a race condition is possible after this check where another process waiting for the MSI installer
#  to become available grabs the MSI Installer mutex before we do. Not too concerned about this possible race condition.
If (($FilePath -match 'msiexec') -or ($WaitForMsiExec))
{
    $MsiExecAvailable = Test-MsiExecMutex -MsiExecWaitTime $MsiExecWaitTime
    Start-Sleep -Seconds 1
    If (-not $MsiExecAvailable)
    {
        $returnCode = 1618 # Default MSI exit code for install already in progress
        Throw "Another MSI installation is already in progress. Complete that installation before proceeding with this install."
    }
}
This part is just being added to 3.1.6 if not mistaken. I'm not very good in PowerShell, hope that someone can take a look. hmm, also wondering why no one getting this problem, or actually no one is using 3.1.6 beta?

Thanks.
/Andy
Aug 23, 2014 at 6:58 AM
Just downloaded the latest source 3.2.0, without doing anything else, just added the below line,
Execute-MSI -Action Install -Path "msxml6.msi" -LogName "test_test_1.0"
Getting same error: "[Installation] Execution failed Cannot add type. There were compilation errors."
Developer
Aug 29, 2014 at 12:28 PM
I am taking a look at this issue to see what the problem is. Can you please tell me what version of PowerShell you are using?
Aug 29, 2014 at 3:15 PM
PowerShell 2.0, the default version on Win7.

There is no plan to upgrade all machines to 3.0.
Developer
Aug 31, 2014 at 7:50 AM
Edited Aug 31, 2014 at 7:52 AM
I found the bug in the code. It was kind of strange because it was not causing a problem for me on Win7 and PowerShell 3.0 but I could reproduce the reported error on Win7 PowerShell 2.0.

Any ways, please change the following line of code:
var isMsiExecFree = false;

To:
bool isMsiExecFree = false;

Here is the entire function with the correction included:
Function Test-MsiExecMutex
{
<#
.SYNOPSIS
    Wait, up to a timeout, for the MSI installer service to become free.
    
.DESCRIPTION
    The _MSIExecute mutex is used by the MSI installer service to serialize installations
    and prevent multiple MSI based installations happening at the same time.
    Wait, up to a timeout (default is 10 minutes), for the MSI installer service to become free
    by checking to see if the MSI mutex, "Global\\_MSIExecute", is available.
    
.PARAMETER MsiExecWaitTime
    The length of time to wait for the MSI installer service to become available.
    This variable must be specified as a [timespan] variable type using the [New-TimeSpan] cmdlet.
    Example of specifying a [timespan] variable type: New-TimeSpan -Minutes 5
    
.OUTPUTS
    Returns true for a successful wait, when the installer service has become free.
    Returns false when waiting for the installer service to become free has exceeded the timeout.
    
.EXAMPLE
    Test-MsiExecMutex
    
.EXAMPLE
    Test-MsiExecMutex -MsiExecWaitTime $(New-TimeSpan -Minutes 5)
    
.EXAMPLE
    Test-MsiExecMutex -MsiExecWaitTime $(New-TimeSpan -Seconds 60)
    
.NOTES
    This is an internal script function and should typically not be called directly.
    
.LINK
    http://msdn.microsoft.com/en-us/library/aa372909(VS.85).asp
    http://psappdeploytoolkit.codeplex.com
#>
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$false)]
        [ValidateNotNullOrEmpty()]
        [timespan]$MsiExecWaitTime = $(New-TimeSpan -Seconds $configMSIMutexWaitTime)
    )
    
    Begin
    {       
        $IsMsiExecFreeSource = @'
        using System;
        using System.Threading;
        public class MsiExec
        {
            public static bool IsMsiExecFree(TimeSpan maxWaitTime)
            {
                /// <summary>
                /// Wait (up to a timeout) for the MSI installer service to become free.
                /// </summary>
                /// <returns>
                /// Returns true for a successful wait, when the installer service has become free.
                /// Returns false when waiting for the installer service has exceeded the timeout.
                /// </returns>
                
                // The _MSIExecute mutex is used by the MSI installer service to serialize installations
                // and prevent multiple MSI based installations happening at the same time.
                // For more info: http://msdn.microsoft.com/en-us/library/aa372909(VS.85).aspx
                const string installerServiceMutexName = "Global\\_MSIExecute";
                Mutex MSIExecuteMutex = null;
                bool isMsiExecFree = false;
                
                try
                {
                    MSIExecuteMutex = Mutex.OpenExisting(installerServiceMutexName,
                                    System.Security.AccessControl.MutexRights.Synchronize);
                    isMsiExecFree = MSIExecuteMutex.WaitOne(maxWaitTime, false);
                }
                catch (WaitHandleCannotBeOpenedException)
                {
                    // Mutex doesn't exist, do nothing
                    isMsiExecFree = true;
                }
                catch (ObjectDisposedException)
                {
                    // Mutex was disposed between opening it and attempting to wait on it, do nothing
                    isMsiExecFree = true;
                }
                finally
                {
                    if (MSIExecuteMutex != null && isMsiExecFree)
                    MSIExecuteMutex.ReleaseMutex();
                }
                
                return isMsiExecFree;
            }
        }
'@
        
        If (-not ([System.Management.Automation.PSTypeName]'MsiExec').Type)
        {
            Add-Type -TypeDefinition $IsMsiExecFreeSource -Language CSharp
        }
    }
    Process
    {
        Try
        {
            If ($MsiExecWaitTime.TotalMinutes -gt 1)
            {
                [string]$WaitLogMsg = "$($MsiExecWaitTime.TotalMinutes) minutes"
            }
            ElseIf ($MsiExecWaitTime.TotalMinutes -eq 1)
            {
                [string]$WaitLogMsg = "$($MsiExecWaitTime.TotalMinutes) minute"
            }
            Else
            {
                [string]$WaitLogMsg = "$($MsiExecWaitTime.TotalSeconds) seconds"
            }
            Write-Log "Check to see if the _MSIExecute mutex is available. Wait up to [$WaitLogMsg] for the _MSIExecute mutex to become available."
            [boolean]$IsMsiExecInstallFree = [MsiExec]::IsMsiExecFree($MsiExecWaitTime)
            
            If ($IsMsiExecInstallFree)
            {
                Write-Log 'The _MSIExecute mutex is available.'
            }
            Else
            {
                ## Get the command line for the MSI installation in progress
                $msiInProgressCmdLine = Get-WmiObject -Class Win32_Process -Filter "name = 'msiexec.exe'" |
                                        Select-Object -Property CommandLine -ExpandProperty CommandLine |
                                        Where-Object { $_ -match '\.msi' }
                Write-Log "The _MSIExecute mutex is not available because the following MSI installation is in progress [$($msiInProgressCmdLine.Trim())]"
            }
            Return $IsMsiExecInstallFree
        }
        Catch
        {
            Write-Log "Error while checking if _MSIExecute mutex is available `n$($_.Exception.Message)"
        }
    }
}
Coordinator
Aug 31, 2014 at 8:42 PM
Thanks again - committed to 3.2, planning to release tomorrow.
Sep 1, 2014 at 7:46 AM
thanks both of you, especially mmashwani. :)