如何:修复“应用程序正忙”和“被调用者拒绝了调用”错误

如果以编程方式从外部(进程外)多线程应用程序调入 Visual Studio 自动化,则可能会收到以下错误:

  • 应用程序正忙 (RPC_E_CALL_REJECTED 0x80010001)

  • 被调用者拒绝了调用 (RPC_E_SERVERCALL_RETRYLATER 0x8001010A)

之所以会出现这些错误,是因为外部多线程应用程序与 Visual Studio 之间存在线程争用问题。 通过在 Visual Studio 自动化应用程序中实现 IOleMessageFilter 错误处理程序可以消除这些错误。 (不要将 IOleMessageFilter 与 System.Windows.Forms.IMessageFilter 混淆。)

修复错误

  1. 将以下类添加到应用程序中。

  2. 添加指向“Microsoft 开发环境 8.0”的 COM 引用。此操作将对 EnvDTE 和 EnvDTE80 的引用添加到解决方案中。

  3. 在代码中,创建 EnvDTE80 的实例,如以下示例中所述。

  4. 调用 Message.Register 处理线程错误。

  5. 按常规方式调用自动化代码。

  6. 完成自动化代码后,调用 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.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; 
            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 中(进程内)运行的应用程序(例如,宏或外接程序)调用,则不会出现此情况。 有关此问题背后的原因的更详细说明,请参见 Office 中的线程支持

若要避免这些错误,请在您的应用程序中实现 IOleMessageFilter 处理程序函数。 实现该处理程序函数后,如果您的外部应用程序线程调入 Visual Studio,然后被拒绝(即它从 IOleMessageFilter.HandleIncomingCall 方法返回 SERVERCALL_RETRYLATER),您的应用程序就可以处理该线程,然后重试或取消调用。 若要执行此操作,请在一个单线程单元 (STA) 中通过 Visual Studio 应用程序启动新线程,并将自动化代码包含在 IOleMessageFilter 处理程序中。

请参见

任务

演练:调试外接程序项目