使用EnvDTE自动化Visual Studio

我使用以下代码成功实例化/自动化Visual Studio:

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 

注意Thread.Sleep(1000)调用? 如果我不包含它,代码会在它准备好之前尝试对实例进行error handling,并且我得到一个exception:

 the message filter indicated that the application is busy. 

而不是等待n秒,有没有办法轮询这个对象准备?

作为此问题的解决方案,您可以注册一个事件,通知何时完成解决方案加载。

这是一个类的示例,可让您在解决方案加载时收听事件:

 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 } 

用法示例:

 DTE2 applicationObject = dte; var serviceProvider = new ServiceProvider(applicationObject as IServiceProvider); solutionEventsListener = new SolutionEventsListener(serviceProvider); solutionEventsListener.AfterSolutionLoaded += () => /* logic here */ ; 

虽然这里的解决方案具有创造性,但它们要么不能完全解决问题,要么使用起来非常麻烦。 您应该只注册Microsoft建议的邮件filter 。

为方便起见,将代码复制到此处(将VisualStudio.DTE.10.0替换为您要打开的任何VS版本),只需注意使用STAThread属性修饰Main方法,如果没有它,则消息过滤将无法工作,并且在原始MSDN中跳过它解。

 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); } } 

我对IVSSolutionEvents技术没有太多运气(虽然我没有像上面那样完全尝试代码)。 相反,我创建了一个小function来帮助我重试呼叫。 我知道这不是特别漂亮,但打电话很简单,而且很有效!

这是我对另一个类似问题的答案的链接: https : //stackoverflow.com/a/8565990/1106459

(在调用其他EnvDTE函数以及打开和关闭解决方案时,它还有助于“服务器忙”错误。)