Waiting to Install

Waiting to Install

  • Comments 11

If you've ever tried to install a Windows Installer package and had returned ERROR_INSTALL_ALREADY_RUNNING (1618) it's because the Windows Installer execution server is already processing another install, administrative install, or advertisement action. That is, Windows Installer is processing the InstallExecuteSequence table, the AdminExecuteSequence table, or the AdvtExecuteSequence table, respectively. This is because right before the action is run in the server context Windows Installer grabs the _MSIExecute mutex. Take a look at the log below:

=== Verbose logging started: 10/3/2005 14:26:48 Build type: SHIP UNICODE 3.01.4000.2435 Calling process: C:\Documents and Settings\User\Local Settings\Temp\SIT12330.tmp\setup.exe ===
MSI (c) (80:00) [14:26:48:285]: Resetting cached policy values
MSI (c) (80:00) [14:26:48:285]: Machine policy value 'Debug' is 0
MSI (c) (80:00) [14:26:48:285]: ******* RunEngine:
******* Product: C:\WINDOWS\Installer\3dfeb63f.msi
******* Action:
******* CommandLine: **********
MSI (c) (80:00) [14:26:48:285]: Client-side and UI is none or basic: Running entire install on the server.
MSI (c) (80:00) [14:26:48:285]: Grabbed execution mutex.
MSI (c) (80:00) [14:26:48:295]: Cloaking enabled.
MSI (c) (80:00) [14:26:48:295]: Attempting to enable all disabled priveleges before calling Install on Server
MSI (c) (80:00) [14:26:48:295]: Incrementing counter to disable shutdown. Counter after increment: 0
MSI (s) (3C:1C) [14:26:48:315]: Grabbed execution mutex.
MSI (s) (3C:0C) [14:26:48:315]: Resetting cached policy values
MSI (s) (3C:0C) [14:26:48:315]: Machine policy value 'Debug' is 0
MSI (s) (3C:0C) [14:26:48:315]: ******* RunEngine:
******* Product: C:\WINDOWS\Installer\3dfeb63f.msi
******* Action:
******* CommandLine: **********
MSI (s) (3C:0C) [14:26:48:315]: Machine policy value 'DisableUserInstalls' is 0
...

Multiple installer sessions can run in the client context concurrently but only one can enter the server context. If a concurrent installer session attempts to grab the execution mutex when another thread owns it, ERROR_INSTALL_ALREADY_RUNNING is returned. This isn't always desirable in automated testing. SMS could be installing something at the time, for example, and the install you're intending to test will fail.

Since mutexes are owned by a single thread, we can wait to own the mutex and then kick off an install successfully since the client will grab the execution mutex on the same thread if we're making calls to Windows Installer APIs like the MsiInstallProduct function. Let's take a look at some sample code that waits on the mutex and calls MsiInstallProduct which can do just about everything you need from installing a new package to patching, and administrative installs to advertised installs.

#define WIN32_LEAN_AND_MEAN

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <msi.h>
#include <string>

typedef std::basic_string<TCHAR> tstring;

#pragma comment(lib, "msi.lib")

int _tmain(int argc, _TCHAR* argv[])
{
  DWORD dwError = NOERROR;
  LPTSTR pszPackage = NULL;
  tstring strArgs;
  HANDLE hMutex = NULL;
  SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };

  // Make sure at least a package was specified.
  if (2 > argc)
  {
    _ftprintf(stderr,
      TEXT("Error: you must provide the path to a Windows Installer package.\n"));
    _ftprintf(stderr, TEXT("Usage: %s <package> [Windows Installer options]\n"),
      argv[0]);
    return ERROR_INVALID_PARAMETER;
  }

  // Get the path the specified package.
  pszPackage = argv[1];

  // Build up remaining options as a single string.
  for (int i = 2; i < argc; i++)
  {
    strArgs += argv[i];
    strArgs += TEXT(" ");
  }

  // Grab the execution mutex when made available.
  hMutex = CreateMutex(&sa, FALSE, TEXT("_MSIExecute"));
  if (!hMutex)
  {
    dwError = GetLastError();
    _ftprintf(stderr,
      TEXT("Error: failed to grab execution mutex. GetLastError() = %d\n"),
      dwError);
    return dwError;
  }

  // Wait to grab ownership of the execution mutex.
  _tprintf(TEXT("Waiting for execution mutex...\n"));
  dwError = WaitForSingleObject(hMutex, INFINITE);
  if (WAIT_OBJECT_0 == dwError)
  {
    // Install the package on the current thread owning the execution mutex.
    _tprintf(TEXT("Installing the specified package..."));
    dwError = MsiInstallProduct(pszPackage, strArgs.c_str());
    _tprintf(TEXT("done.\n"));
  }

  if (ERROR_SUCCESS != dwError)
  {
    _ftprintf(stderr, TEXT("Error: an error has occured. GetLastError() = %d\n"),
      dwError);
  }

  CloseHandle(hMutex);
  return static_cast<int>(dwError);
}

Now even if another installation is being processed in the server context we can wait to install whatever product we specify by using a command similar to the following. Assume the code was compiled to MsiWait.exe.

MsiWait.exe Package.msi ACTION=INSTALL

Leave a Comment
  • Please add 7 and 7 and type the answer here:
  • Post
  • The MSI team say there's a preferable algorithm for MSI 3.0, posted here:
    http://blogs.msdn.com/windows_installer_team/archive/2005/11/09/487559.aspx

    although that last line needs to say:

    "bInstallRunning = true; // because it won't accept the stop, so it's doing an install. "

    where bInstallRunning is true if there is an install in process. However this newer algorithm seems to have a timing issue (compared to the mutex) because you could get the point where you think you can do your install, but any number of waiting processes could come to the same conclusion and all try to start their setups at the same time, and they won't all work!
  • Phil, that's exactly the problem - even if you're waiting for the service to stop (say, polling for acceptance of the stop request or even if the service is running) there exists a race condition. Using the _MSIExecute mutex will guard against such a race condition.

    Also, the second line of the same pseudo-code to which you refer should also be "bInstallRunning = false". If the service isn't running, no install is processing. mikeb's comment is correct there.
  • How to display new controls with Common Controls 6 in your Windows Installer dialogs with a different bootstrap or external UI handler application.
  • An enhancement might be to have a time out and failure rather than an INFINITE wait.

    Even fancier would be popping up a dialog with a cancel button that says we're waiting on another install.

    Is the "// Install the package on the current thread owning the execution mutex." comment in your code correct?    I don't see where the mutex is opened in this code (and anyway, it would hose msiexec if it were).

    And finally, it might be helpful to up the thread priority to realtime as soon as we get the mutex, so our install is a little more likely to be the first one to get to the point where msiexec grabs the mutex.
  • Matt, you can choose to wait indefinitely or finitely - it's your choice based on your requirements. This is only an example of how to wait for Windows Installer to finish the current server action.

    If you don't install the package on the thread that opened the mutex, the server action will fail because the thread won't own the mutex. That's the whole point of using a mutex - to block other threads and processes until another thread can own the mutex.

    Making the thread real-time priority is another design consideration you should make based on requirements. This example certainly doesn't need to assert that it should be the next in line, and giving the thread a higher priority won't necessarily guarantee that it would be anyway.
  • Hmm... Maybe I'm missing something.   Where do you take ownership of the mutex?   And as I said before, if you did, wouldn't that prevent the install from working?
  • Matt, WaitForSingleObject will wait to own the mutex, waiting either till the mutex is signaled or till any expiration interval expires. It then may own the mutex, thus changing the state from signaled to nonsignaled and other threads calling WaitForSingleObject on the mutex will wait. Any other thread trying to own the mutex will fail unless it waits for the mutex in a similar manner. Windows Installer by default will fail with ERROR_INSTALL_ALREADY_RUNNING (1618). This is why a bootstrap application must implement waiting on the mutex.
  • Some have noticed that the msiexec.exe process seems to run for quite some time after a known installation

  • I need to know the product code of the product that windows installer is installing before launching my installer. I can check the mutex but it only tells me that Windows installer server is busy or not. Is there a recommended way to find the product code that is currently being installed?

  • @kashif, sorry, but that is not supported or even possible that I know of. There are WMI classes that may work, but these are not documented and do not seem to represent running instances.

    Why do you need to know what is installing instead of just waiting?

Page 1 of 1 (11 items)