2010-03-26 94 views
28

我成功實例/使用下面的代碼自動化的Visual Studio:自動化Visual Studio中EnvDTE

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)電話嗎?如果我不包括,代碼試圖錯誤的情況下它已經準備好之前,我得到一個異常:

the message filter indicated that the application is busy. 

不是正好n秒等待,有沒有辦法查詢該對象的準備?

回答

31

作爲解決此問題的一種解決方案,您可以註冊一個通知解決方案加載完成時的事件。

這是類的一個樣本,讓你聽上解決加載事件:

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 */ ; 
+7

這是一個很好的答案,並且完美地工作。順便說一句,我發現一個作弊的工作 - 在Solution.Open打開解決方案後檢查Solution.IsOpen屬性。這顯然是一個阻塞調用,迫使等待devenv.exe的實例準備就緒。 – 2010-03-29 13:59:40

+2

@Dave Swersky,我喜歡Solution.IsOpen,它可以節省大量的代碼:) – Elisha 2010-03-29 21:53:16

+0

這也幫助我在VS11中SolutionEvent看起來破碎了! – riezebosch 2012-04-17 10:28:31

0

我沒有多少運氣的IVSSolutionEvents技術(雖然我沒有完全按照上面的方法來測試代碼)。相反,我創建了一個小函數來幫助我重試呼叫。我知道這並不是特別漂亮,但它很簡單,而且很有效!

下面是我的回答對另一個類似的問題的鏈接:https://stackoverflow.com/a/8565990/1106459

(打電話時其他EnvDTE功能以及打開和關閉該解決方案還與「服務器忙」的錯誤幫助)

7

雖然這裏的解決方案很有創意,但它們要麼不能完全解決問題,要麼使用起來非常麻煩。你應該只是register a message filter as Microsoft recommends。複製在這裏爲了方便(更換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); 
    } 
} 
+1

偉大的解決方案,這應該是答案。這是應該如何做的。其他人實際上對我來說沒有任何可靠的表現。 只是一個提示,請注意頂部的[STAThread],如果沒有它,MessageFilter將不會註冊,因爲它不支持MTA。謝謝! – mikus 2016-02-22 14:31:14

+0

好吧,重試解決方案在http://stackoverflow.com/questions/5330289/is-there-a-better-way-to-handle-rpc-e-call-rejected-exceptions-when-doing-visual/8565990 #8565990會或多或少地做同樣的事情,但使用起來要麻煩得多。這使用com中的內置消息過濾器系統來重試呼叫。 – poizan42 2016-02-23 12:31:27

+0

我使用類似於Coded UI的方式,在那裏我不會等待頁面加載,只是嘗試執行操作直到它工作。但正如你所說,它的長期使用不那麼透明和刺激。尤其是那些COM異常可能不僅發生在第一次調用中,還會發生在以後。 – mikus 2016-02-23 15:13:18