Improvements to IPHTerminateOrphanedVSTA sample

Below are significant improvements to the IPH sample posted here  through the use of global mutexes for cross-process synchronization. 

Question:  What does the IPHTerminateOrphanedVSTA sample do?

Answer:  You may have noticed that the IDE process (vsta.exe) may be orphaned if the host application crashes or abnormally terminates.  The IPHTerminateOrphanedVSTA sample uses an in-process host to watch for an orphaned vsta process (its own process) and terminates it.   It does not interfere when VSTA projects are opened with the standalone VSTA IDE (no host application process).

After you have installed the original IPH sample, the following improvements can be applied to that sample: 

VstaRuntimeIntegration.cs :

Connect() creates a global mutex named after the host app PID, owned by the host process, and stored as VSTAMutex.

When application Disconnect()s VSTA (when host app exits), it releases the mutex properly.

 

VstaRuntimeIntegration.cs :

 

        private Mutex VSTAMutex;

        internal void Connect(Application hostApplication)

        {

            this.application = hostApplication;

 

            // Get the current process.

            Process currentProcess = Process.GetCurrentProcess();

            try

            {

                Mutex.OpenExisting(@"Global\"+ "Visual Studio Tools for Applications Host Process" + currentProcess.Id.ToString());

            }

            catch (Exception ex)

            {

   // Create a global mutex with a unique name based on the application’s processID.

                VSTAMutex = new Mutex(true, @"Global\" + "Visual Studio Tools for Applications Host Process" + currentProcess.Id.ToString());

            }

 

            // Create a Service Provider.

            InstantiateServiceProvider();

 

            // Load Application Level AddIns.

            LoadAppLevelAddIns();

 

            // Load Macros In-Process.

            LoadMacrosInProcess();

        }

 

        internal void Disconnect()

        {

            try

            {

                VSTAMutex.ReleaseMutex();

                VSTAMutex.Close();

            }

            catch (Exception ex)

            {

            }

        }

 

IPH.cs :

Notice that the polling loop has been replaced with two global mutexes (or system mutexes).

The IPH looks through all processes for the host application process with a unique global mutex name originating with the host application:

@"Global\" + "Visual Studio Tools for Applications Host Process" + hostproc.Id.ToString()

When this process is found the IPH creates a unique global mutex (VSTAHostMonitorMutex) with the name:

@"Global\" + "Visual Studio Tools for Applications Host Monitor" + hostProc.Id.ToString());

and creates a monitor thread.  The monitor thread will open the existing host mutex (VSTAHostMutex) and block or ‘wait’ for the host process to release its mutex (when the host closes or crashes).  If the VSTA IDE process (vsta.exe) is orphaned, the thread will terminate its process, vsta.exe (the IDE).

 

VSTAHostMonitorMutex is assigned when the IPH is loaded for the first time only because OpenExisting() fails with an exception this first time:

Mutex.OpenExisting(@"Global\" + "Visual Studio Tools for Applications Host Monitor" + vstaHostProc.Id.ToString());

When the IPH is loaded additional times, OpenExisting() of the host monitor mutex does not fail, which means that no duplicate monitor threads are created.

 

IPH.cs :

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.VisualStudio.Tools.Applications.DesignTime;

using Microsoft.VisualStudio.Tools.Applications.DesignTime.Interop;

using System.Diagnostics;

using System.Threading;

 

//IPHTerminateOrphanedVSTA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5993c97a24fb6aa3, processorArchitecture=MSIL

//{2CD68381-5859-4afe-88AC-42D13CD4B335}

 

namespace IPHTerminateOrphanedVSTA

{

    public class IPH : IInProcessHost

    {

        //To debug this IPH follow this sequence:

        //1. start ShapeAppMacroRecordingCSharp sample directly from .exe or select 'Debug | Start Without Debugging' in VS

        //2. Launch VSTA IDE (Alt + F11), should load a macro project with no IPH

        //  (if macro project has IPH, just remove entry  <InProcHost>{2CD68381-5859-4afe-88AC-42D13CD4B335}</InProcHost> from ShapeAppMacros.csproj before 'Tools | ShowIDE' opens it).

        //3. In VS, 'Debug | Attach To Process...'  select vsta.exe

        //4. In VSTA IDE, 'File | New Project', select CSharp template, discard open project and this IPH (IPHTerminateOrphanedVSTA.IPH.SetAdapter() should be called.

        //5. From 'Task Manager', kill ShapeAppCSharp.exe and the CheckForHostObserverThread.CheckforHostProcess should drop into section where this call is made:

        //    System.Diagnostics.Process vstaProc = System.Diagnostics.Process.GetCurrentProcess(); so that vsta.exe can be terminated.

 

        //mutex is used to block monitor thread executing shutdown until host closes or crashes

        static Mutex VSTAHostMutex;

        static Process vstaHostProc;

        //mutex is used create single monitor thread (thread ensures vsta.exe is always terminated when host terminates)

        static Mutex VSTAHostMonitorMutex;

  

        #region IInProcessHost Members

 

        public void SetAdapter(IVstaHostAdapter newHostAdapter)

        {

            //get vstaHostProc from globalMutex created by host

            Process[] procs = System.Diagnostics.Process.GetProcesses();

            foreach (Process proc in procs)

            {

                try

                {

                    Mutex.OpenExisting(@"Global\" + "Visual Studio Tools for Applications Host Process" + proc.Id.ToString());

                    //we identified host application process via the global mutex it created with its own procid

                    vstaHostProc = proc;

                    break;

                }

                catch (Exception)

                {

                    //if no mutex, then try the next proc

                }

            }

            if (vstaHostProc != null)

            {

                //create an exclusive VSTAHostMonitorMutex, based on unique PID, to assure that only one monitor thread exists

                try

                {

                    Mutex.OpenExisting(@"Global\" + "Visual Studio Tools for Applications Host Monitor" + vstaHostProc.Id.ToString());

                }

                catch (Exception)

                {

                    //first instance creating this unique mutex, so create monitor thread

                    VSTAHostMonitorMutex = new Mutex(true, @"Global\" + vstaHostProc.Id.ToString());

                    //thread will wait on host mutex for host to terminiate terminate vsta.exe (IDE) if needed

                    ThreadStart MonitorThread = new ThreadStart(MonitorThreadMethod);

                    Thread t = new Thread(MonitorThread);

                    t.Start();

                }

            }

        }

        #endregion IInProcessHost Members

 

 

        #region MonitorThread

        public static void MonitorThreadMethod()

        {

            //Console.WriteLine("VSTA.exe Monitor thread - Started");

            {

                //monitoring thread opens the first global mutex, named after the vstaHostProcID, and waits for it

                try

                {

                    VSTAHostMutex = Mutex.OpenExisting(@"Global\" + "Visual Studio Tools for Applications Host Process" + vstaHostProc.Id.ToString());

                    VSTAHostMutex.WaitOne();

 

                    //proper shutdown does not require call to

                    //TerminateVSTA();

                }

                catch (AbandonedMutexException)

                {

                    //If I get an Abandoned mutex exception, it means that the host app has crashed without releasing properly the global mutex.

                    //Hence I quit the DTE. . . . .

                    //

                    TerminateVSTA();

 

                }

                catch (Exception)

                {

                    //If it cannot be opened (exception), it is a fatal error. . . . .

                    TerminateVSTA();

                }

                finally

                {

 

                    //Otherwise, my host's mutex was released properly at the end of life of the host app,

                    //I release it too properly and exit my thread. . . . .

                    VSTAHostMutex.ReleaseMutex();

                    VSTAHostMutex.Close();

                }

            }

            //Console.WriteLine("VSTA.exe Monitor thread - Returned");

        }

 

        private static void TerminateVSTA()

        {

            // ...time to close vsta.exe and wind down this thread.

            System.Diagnostics.Process vstaProc = System.Diagnostics.Process.GetCurrentProcess();

 

            try

            {

                if (!vstaProc.HasExited)

                {

                    vstaProc.Kill();

                }

                else

                {

                    //safety check:

                    //if no vsta.exe -- vstaProc.HasExited, and somehow this thread is still running, then abort this thread

                    System.Threading.Thread.CurrentThread.Abort();

                }

            }

            catch

            {

            }

        }

 

        #endregion MonitorThread

 

    }

}

 

 

 

 

 


Posted Apr 10 2009, 04:57 PM by Gary
Copyright Summit Software Company, 2008. All rights reserved.