Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automating Visual Studio with EnvDTE

I am successfully instantiating/automating Visual Studio using the following code:

System.Type t = System.Type.GetTypeFromProgID("VisualStudio.DTE.9.0"); object obj = Activator.CreateInstance(t, true); dte = (DTE)obj; Solution sln = dte.Solution; sln.Open(SolutionFile); System.Threading.Thread.Sleep(1000); //Do stuff with the solution 

Notice the Thread.Sleep(1000) call? If I don't include that, the code tries to bug the instance before it's ready and I get an exception:

the message filter indicated that the application is busy. 

Rather than wait exactly n seconds, is there a way to poll this object for readiness?

like image 369
Dave Swersky Avatar asked Mar 26 '10 17:03

Dave Swersky


People also ask

What is envdte80?

EnvDTE is an assembly-wrapped COM library containing the objects and members for Visual Studio core automation, How to install an assembly in GAC. – Zabba. Mar 18, 2011 at 21:12. It is installed with Visual Studio and or the Visual Studio SDK.

What is Visual Studio DTE?

DTE = "Development Tools Environment" Essentially, I understand it to be the part of Visual Studio that lets you interact with the computer itself. This can control things such as active windows and applications.


2 Answers

As a solution to this issue you can register to an event that notifies when the solution load is done.

This is a sample of class that lets you listen to events on solution loading:

public class SolutionEventsListener : IVsSolutionEvents, IDisposable {     private IVsSolution solution;     private uint solutionEventsCookie;      public event Action AfterSolutionLoaded;     public event Action BeforeSolutionClosed;      public SolutionEventsListener(IServiceProvider serviceProvider)     {         InitNullEvents();          solution = serviceProvider.GetService(typeof (SVsSolution)) as IVsSolution;         if (solution != null)         {             solution.AdviseSolutionEvents(this, out solutionEventsCookie);         }     }      private void InitNullEvents()     {         AfterSolutionLoaded += () => { };         BeforeSolutionClosed += () => { };     }      #region IVsSolutionEvents Members      int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution)     {         AfterSolutionLoaded();         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved)     {         BeforeSolutionClosed();         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel)     {         return VSConstants.S_OK;     }      int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel)     {         return VSConstants.S_OK;     }      #endregion      #region IDisposable Members      public void Dispose()     {         if (solution != null && solutionEventsCookie != 0)         {             GC.SuppressFinalize(this);             solution.UnadviseSolutionEvents(solutionEventsCookie);             AfterSolutionLoaded = null;             BeforeSolutionClosed = null;             solutionEventsCookie = 0;             solution = null;         }     }      #endregion } 

Usage example:

DTE2 applicationObject = dte; var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider); solutionEventsListener = new SolutionEventsListener(serviceProvider); solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ; 
like image 199
Elisha Avatar answered Oct 09 '22 04:10

Elisha


While the solutions here are creative they either won't completely fix the problem or are very cumbersome to use. You should just register a message filter as Microsoft recommends.

Code copied here for convenience (replace VisualStudio.DTE.10.0 with whatever version of VS you want to open), just pay attention to decorate the Main method with STAThread attribute, message filtering won't work without it and it is skipped in original MSDN solution.

using System; using System.Collections.Generic; using System.Text; using EnvDTE; using EnvDTE80; using System.Runtime.InteropServices; using System.Windows.Forms;  namespace ConsoleApplication2 {     class Program     {         [STAThread]         static void Main(string[] args)         {             EnvDTE80.DTE2 dte;             object obj = null;             System.Type t = null;              // Get the ProgID for DTE 8.0.             t = System.Type.GetTypeFromProgID("VisualStudio.DTE.10.0",               true);             // Create a new instance of the IDE.             obj = System.Activator.CreateInstance(t, true);             // Cast the instance to DTE2 and assign to variable dte.             dte = (EnvDTE80.DTE2)obj;              // Register the IOleMessageFilter to handle any threading              // errors.             MessageFilter.Register();             // Display the Visual Studio IDE.             dte.MainWindow.Activate();              // =====================================             // ==Insert your automation code here.==             // =====================================             // For example, get a reference to the solution2 object             // and do what you like with it.             Solution2 soln = (Solution2)dte.Solution;             System.Windows.Forms.MessageBox.Show               ("Solution count: " + soln.Count);             // =====================================              // All done, so shut down the IDE...             dte.Quit();             // and turn off the IOleMessageFilter.             MessageFilter.Revoke();          }     }      public class MessageFilter : IOleMessageFilter     {         //         // Class containing the IOleMessageFilter         // thread error-handling functions.          // Start the filter.         public static void Register()         {             IOleMessageFilter newFilter = new MessageFilter();              IOleMessageFilter oldFilter = null;              int hr = CoRegisterMessageFilter(newFilter, out oldFilter);             if (hr != 0)               Marshal.ThrowExceptionForHR(hr);         }          // Done with the filter, close it.         public static void Revoke()         {             IOleMessageFilter oldFilter = null;              CoRegisterMessageFilter(null, out oldFilter);         }          //         // IOleMessageFilter functions.         // Handle incoming thread requests.         int IOleMessageFilter.HandleInComingCall(int dwCallType,            System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr            lpInterfaceInfo)          {             //Return the flag SERVERCALL_ISHANDLED.             return 0;         }          // Thread call was rejected, so try again.         int IOleMessageFilter.RetryRejectedCall(System.IntPtr            hTaskCallee, int dwTickCount, int dwRejectType)         {             if (dwRejectType == 2)             // flag = SERVERCALL_RETRYLATER.             {                 // Retry the thread call immediately if return >=0 &                  // <100.                 return 99;             }             // Too busy; cancel call.             return -1;         }          int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,            int dwTickCount, int dwPendingType)         {             //Return the flag PENDINGMSG_WAITDEFPROCESS.             return 2;          }          // Implement the IOleMessageFilter interface.         [DllImport("Ole32.dll")]         private static extern int            CoRegisterMessageFilter(IOleMessageFilter newFilter, out            IOleMessageFilter oldFilter);     }      [ComImport(), Guid("00000016-0000-0000-C000-000000000046"),      InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]     interface IOleMessageFilter      {         [PreserveSig]         int HandleInComingCall(              int dwCallType,              IntPtr hTaskCaller,              int dwTickCount,              IntPtr lpInterfaceInfo);          [PreserveSig]         int RetryRejectedCall(              IntPtr hTaskCallee,              int dwTickCount,             int dwRejectType);          [PreserveSig]         int MessagePending(              IntPtr hTaskCallee,              int dwTickCount,             int dwPendingType);     } } 
like image 38
poizan42 Avatar answered Oct 09 '22 03:10

poizan42