0

I need to queue approximately 20 installations that are fully unattended (Using a C# winform application). Each installation has its own INI file (that is manually created) that contains the proper information on what arguments each installer requires for this procedure (read in before that program is executed). I'm running into issues with many application that when the setup.exe is executed the process closes immediately and launches its MSI (if applicable), causing my procedure to carry out with the next installation assuming that the first is complete. I have read similar problems snooping around the web, but no real solution on the issue... (some workarounds included using a batch file with the /Wait option which should have kept the setup.exe in memory until its MSI has completed). The setup.exe must be launched due to the fact that they contain bootstrappers.

What options do i have to resolve this dilemma?

Here is some sample code that demonstrates the procedure:

            foreach (ListViewItem itm in this.lstSoftwares.Items)
            {
                try
                {
                    if (itm.Checked)
                    {
                        lblStatus.Text = "Status: Installing " + current.ToString() + " of " + count.ToString();

                        string InstallPath = Path.Combine(Application.StartupPath, "Software",
                        itm.Text, itm.Tag.ToString());

                        string CommandLine = itm.SubItems[1].Text;

                        Process process = new Process();
                        process.StartInfo.FileName = InstallPath;
                        process.StartInfo.Arguments = CommandLine;

                        process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                        process.Start();
                        process.WaitForExit();
                        this.lstSoftwares.Items[i].SubItems[2].Text = "Complete";
                        current++;
                    }

Update

right after waitforexit() i'm using a loop that checks if the msiexec is running:

    private bool MSIRunning()
    {           
        try
        {
            using (var mutex = Mutex.OpenExisting(@"Global\_MSIExecute"))
            {                   
                return true;
            }

        }
        catch (Exception)
        {
            return false;
        }
    }

this is a hack in my opionion, but doing the trick so far...

devHead
  • 794
  • 1
  • 15
  • 38
  • How are you queueing them? Please post some code, as right now this post is much too vague and requires details. – Oded Jan 24 '13 at 14:54
  • i pasted the code above, let me know if you need any other info – devHead Jan 24 '13 at 14:58
  • Have you looked at the Process.ExitCode to see what exit code you're getting? – Pete Jan 24 '13 at 15:23
  • ExitCode should be fine. This behaviour is by design. Try adding /w to the arguments. – JosephHirn Jan 24 '13 at 15:29
  • Hi Ginosaji, after all my research i understand this is by design... i was just wondering if i can monitor some proc ID via WMI or if there is no relation at all between the procs what could be an approach... i will try the /w switch now – devHead Jan 24 '13 at 15:33
  • If all of them are based on Windows Installer (MSI) try automating them using the Windows Installer API – Mohammad Jan 24 '13 at 16:02
  • thanks for the idea Mohammad Heskol, but unfortunately we will not know that information... most of the installers are 3rd party and over time we could literally be deploying over 40 or more applications using this technique. – devHead Jan 24 '13 at 16:09
  • Then I think you should consider monitoring each setup executable for any child processes it would spawn and wait until all of the children processes have finished executing to consider it this particular setup has finished and get to start the next one. May be the situation will extend to watch for children processes launched by those children processes also (grand children processes). – Mohammad Jan 24 '13 at 16:18
  • the problem is that i really don't think it's a "real" child process... From my understanding setup.exe contains the bootstrap info, once complete setup.exe simply launches the msi (basically shells it) then closes completely, so in theory it will not be a child process but a completely different one... – devHead Jan 24 '13 at 16:21
  • Odd that /w wouldn't work, as that switch was created specifically for this purpose. You might be able to work something out with WMI, but I can't vouch for how difficult it would be to distinguish between a process that was started by setup.exe and a completely unrelated process. See [this article](http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/46f52ad5-2f97-4ad8-b95c-9e06705428bd) for an example. – JosephHirn Jan 25 '13 at 14:50
  • FWIW, I posted a slightly more robust solution to checking the MSI mutex below. – Mark Richman Apr 01 '16 at 18:28

2 Answers2

0

Querying the MSI Mutex after process.start in a loop (check if Mutex is running every 3 seconds, if not return and proceed with next install) seemed to solve the problem (Noted above).

devHead
  • 794
  • 1
  • 15
  • 38
0

Already answered, but I have a slightly more robust implementation of the MSI mutex check:

public bool IsMsiExecFree(TimeSpan maxWaitTime)
{
    _logger.Info(@"Waiting up to {0}s for Global\_MSIExecute mutex to become free...", maxWaitTime.TotalSeconds);

    // 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;
    var isMsiExecFree = false;

    try
    {
        msiExecuteMutex = Mutex.OpenExisting(installerServiceMutexName,
            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();
    }

    _logger.Info(@"Global\_MSIExecute mutex is free, or {0}s has elapsed.", maxWaitTime.TotalSeconds);

    return isMsiExecFree;
}
Mark Richman
  • 28,948
  • 25
  • 99
  • 159