HOW TO:修正應用程式忙碌中和接收者已拒絕這個呼叫等錯誤

更新:2007 年 11 月

如果您以程式設計方式從外部 (跨處理序) 的多執行緒應用程式呼叫 Visual Studio Automation,有時候會收到「應用程式忙碌中」和「接收者已拒絕這個呼叫」錯誤。這些錯誤的發生原因是外部的多執行緒應用程式和 Visual Studio 之間出現執行緒爭用問題。您可以在 Visual Studio Automation 應用程式中實作 IOleMessageFilter 錯誤處理常式,藉以排除這些錯誤(請勿將 IOleMessageFilter 與 System.Windows.Forms.IMessageFilter 混淆)。

若要修正錯誤

  1. 將下列類別加入至應用程式中。

  2. 加入 COM 參考至 "Microsoft Development Environment 8.0"。這樣就會將 EnvDTE 和 EnvDTE80 的參考加入至您的方案中。

  3. 在程式碼中,建立 EnvDTE80 的執行個體,如下列範例所示。

  4. 呼叫 Message.Register 以便處理執行緒錯誤。

  5. 以平常的方式呼叫 Automation 程式碼。

  6. 當您的 Automation 程式碼完成後,請呼叫 Message.Revoke 來移除執行緒錯誤處理常式。

範例

using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using EnvDTE80;
using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
    class Program
    {
        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.8.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; 
            CoRegisterMessageFilter(newFilter, out oldFilter);
        }

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

穩固程式設計

當您的外部多執行緒應用程式呼叫 Visual Studio 時,它會通過 COM 介面。COM 有時會無法正確處理執行緒,尤其是在時序方面。因此,來自外部應用程式的內送執行緒有時無法在到達的那一刻由 Visual Studio 處理,而導致上述錯誤發生。不過,如果您是從 Visual Studio 內部執行的應用程式 (同處理序) 呼叫,例如巨集或增益集 (Add-In),就不會發生這些錯誤。如需發生原因的詳細說明,請參閱 Office 中的執行緒支援

若要避免這些錯誤,請在您的應用程式中實作 IOleMessageFilter 處理函式。當您這樣做的時候,如果外部應用程式執行緒呼叫 Visual Studio 且遭拒 (亦即,從 IOleMessageFilter.HandleIncomingCall 方法傳回 SERVERCALL_RETRYLATER) 時,您的應用程式就可以處理此呼叫並重試或取消呼叫。若要這樣做,請從 Visual Studio 應用程式將新的執行緒在單一執行緒 Apartment (STA) 中啟始,並以 IOleMessageFilter 處理常式圍繞 Automation 程式碼。

請參閱

工作

逐步解說:偵錯增益集專案