次の方法で共有


Windows Installer Troubleshooting Tips From Halloween

Question 1

In my lab automation that silently installs Windows Installer packages. This automation is inconsistently getting Error 1618 from its msiexec calls. Please help unblock this scenario as it's inhibiting our push to ship.

Troubleshooting 1

First, start with the MSDN Library and run a search for "windows installer" 1618. This query (when I click it) turns up Initialization Errors [Windows Installer] as the sixth hit. This page contains

Error code Value Error
ERROR_INSTALL_ALREADY_RUNNING 1618 An installation is already in progress

Next, have a look at the machine event log and look for the messages per the MSDN Topic Event Logging

Finally, when one puts this information together, the chances are high that another process, such as SMS or Group Policy, is triggering a silent install of a MSI based product thus the mutex is preventing the simultaneous install of the second product.

Solutions 1

To solve for this scenario, one needs to understand the mechanics of determining if Windows Installer is busy and then one needs to consider the appropriate behavior for their application for the "busy" case. 

Generally if Windows Installer is busy , Windows Installer will set the Mutex.  For a sample demonstrating the mechanics of checking the mutex, see the Windows Installer Platform SDK bootstrapper code example

HANDLE hMutex = 0;
bool bInstallRunning = false;

const char *szMutexName = "Global\\_MSISETUP_{2956EBA1-9B5A-4679-8618-357136DA66CA}";
hMutex = WIN::CreateMutex(NULL /*default security descriptor*/, FALSE, szMutexName);
if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError())
{
bInstallRunning = true;
}
else
{
bInstallRunning = false;
}

// only run one instance at a time
if (bInstallRunning)
{
// silently return - correct return code ?
uiRet = ERROR_INSTALL_ALREADY_RUNNING;
goto CleanUp;
}

// do work here

CleanUp:
if(hMutex)
CloseHandle(hMutex);

For Windows Installer 3.0 or greater, we recommend switching from the mutex to attempting to stop the msi service.  If you are unable to stop the service, the service is busy installing another product.  In pseudocode, these mechanics would look like

 if (MSIServer is not running)
    bInstallRunning = true; // because if the service isn't running there can't be an install running
else  if (MSIServer accepts SERVICE_ACCEPT_STOP)
    bInstallRunning = false; // because if it can stop it can't be installing something
else
    bInstallRunning = false; // because it won't accept the stop, so it's doing an install. 

With either of these solutions, a non-privileged context will be unable to detect the state of the engine.

Now that you know the mechanics, you need to consider what to do when the Windows Installer is "busy".  There are a number of options and which one you choose depends on your judgment.  Some solutions choose surface the fact that the Windows Installer is busy and suggest the user try again later.  Other solutions choose to put the "busy" check in a polling loop and attempt an auto recover from the mutex. 

Question 2

I have a Windows Installer package that InstallInitialize shows all the features and components are installing Action:Local but after the install the feature is advertised. Can you help explain this? (msi packages and verbose logs sent too)

Answer 2

Generally to find these issues I look at the components with conditions. In this case we have the following Component Table Fragments

Component ComponentId Directory_ Attributes Condition KeyPath
C_Server_ConnectorKeys_For_NonRMS.540EA3C0_A5E9_41EA_A585_822C09EA2650 {3162C002-E983-4C45-BE65-A0EADF35AD49} SDK_DIR.540EA3C0_A5E9_41EA_A585_822C09EA2650 4 IS_ROOT_HEALTH_SERVICE=0 R_Server_ConnectorKeys_For_NonRMS10.540EA3C0_A5E9_41EA_A585_822C09EA2650
C_Core_ConnectorKeys.80B659D9_F758_4E7D_B4FA_E53FC737DCC9 {43DC7EF4-E685-45E1-9B3B-9843A2853E43} CORE_DIR.80B659D9_F758_4E7D_B4FA_E53FC737DCC9 4 USE_SETTINGS_FROM_AD=0 R_Core_Connector01.80B659D9_F758_4E7D_B4FA_E53FC737DCC9

Cross Referencing the C_Server_ConnectorKeys_For_NonRMS.540EA3C0_A5E9_41EA_A585_822C09EA2650 GUID {3162C002-E983-4C45-BE65-A0EADF35AD49} with the install log and one finds

MSI (s) (44:6C) [14:26:41:656]: Executing op: ComponentUnregister(ComponentId={3162C002-E983-4C45-BE65-A0EADF35AD49},,BinaryType=0,PreviouslyPinned=1)

Now I take the condition and search for it in the log

 MSI (s) (44!AC) [14:26:26:828]: PROPERTY CHANGE: Modifying IS_ROOT_HEALTH_SERVICE property. Its current value is '0'. Its new value: '1'.
 ...
 Property(S): IS_ROOT_HEALTH_SERVICE = 1

If it were true that this property was set, it would cause the component to be absent and its absence would cause the feature to appear advertised.

Design Suggestion for Question 2

If the advertised state on the feature is the problem, I suspect what’s called for here is another feature. One could have a child feature that just contains the components that will be toggled and is marked to follow parent. Depending on whether that works for all scenarios, there is also the option to have the new feature sit at the root and then write code to toggle the state of the feature.

Editing credit also belongs to

  • Phil Wilson, MSI MVP,

Content credit also belongs to

[Author: Robert Flaming]

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at https://www.microsoft.com/info/cpyright.htm.

Comments

  • Anonymous
    November 11, 2005
    The comment has been removed
  • Anonymous
    November 21, 2005
    Are there any negative ramifications of the feature appearing as advertised in subsequent operations such as upgrades and uninstalls?
    -Ajay
  • Anonymous
    November 25, 2005
    Phil Wilson, MSI MVP has pointed out the eariler code post had two bugs. I've corrected the pseudo code above. Below are the bugs found. Many thanks to Phil! ;^)

    if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError())
    {
    bInstallRunning = true;
    }
    bInstallRunning = false;

    is now

    if (!hMutex || ERROR_ALREADY_EXISTS == GetLastError())
    {
    bInstallRunning = true;
    }
    else
    {
    bInstallRunning = false;
    }

    and

    if (MSIServer is not running)
    bInstallRunning = true;
    else if (MSIServer accepts SERVICE_ACCEPT_STOP)
    bInstallRunning = true;
    else
    bInstallRunning = false;

    is now

    if (MSIServer is not running)
    bInstallRunning = true; // because if the service isn't running there can't be an install running
    else if (MSIServer accepts SERVICE_ACCEPT_STOP)
    bInstallRunning = false; // because if it can stop it can't be installing something
    else
    bInstallRunning = false; // because it won't accept the stop, so it's doing an install.
  • Anonymous
    December 12, 2005
    Shouldn't:

    if (MSIServer is not running)
    bInstallRunning = true; // because if the service isn't running there can't be an install running
    else if (MSIServer accepts SERVICE_ACCEPT_STOP)
    bInstallRunning = false; // because if it can stop it can't be installing something
    else
    bInstallRunning = false; // because it won't accept the stop, so it's doing an install.

    really be:

    if (MSIServer is not running)
    bInstallRunning = false; // because if the service isn't running there can't be an install running
    else if (MSIServer accepts SERVICE_ACCEPT_STOP)
    bInstallRunning = false; // because if it can stop it can't be installing something
    else
    bInstallRunning = true; // because it won't accept the stop, so it's doing an install.
  • Anonymous
    January 23, 2006
    The comment has been removed
  • Anonymous
    January 23, 2006
    Checking the service state or acceptance of the stop request isn't thread-safe. Unless support for the _MSIExecute mutex is going away - and I doubt that since the Windows Installer team is very good about backward compatibility - I would rather use thread-safe code as with the sample at http://blogs.msdn.com/heaths/archive/2006/01/23/516454.aspx.
  • Anonymous
    December 08, 2006
    Some have noticed that the msiexec.exe process seems to run for quite some time after a known installation