Visual Studio 2017 - 消息过滤器指示应用程序正忙

Visual Studio 2017 - Message filter indicated that the application is busy

我正在移植一个小型控制台应用程序,该应用程序删除磁盘上不在 Visual Studio 项目中的文件。此代码在 Visual Studio 2013 年有效,但我在 Visual Studio 2017 年 运行 时收到以下错误:

System.Runtime.InteropServices.COMException: 'The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER))'

当前代码:

public static int DeleteFilesNotInProject(string solutionFileAndPath, string projectName, string extension)
    {
        var returnValue = 0;
        EnvDTE80.DTE2 dte;

        // Targets Visual Studio 2017
        dte = (EnvDTE80.DTE2)Activator.CreateInstance(Type.GetTypeFromProgID("VisualStudio.DTE.15.0", true), true);

        MessageFilter.Register();

        System.Threading.Thread.Sleep(2000);

        while (!dte.Solution.IsOpen)
        {
            // make sure it is ready to open
            System.Threading.Thread.Sleep(500);
            dte.Solution.Open(solutionFileAndPath);
        }

        dte.Solution.Open(solutionFileAndPath);

        System.Threading.Thread.Sleep(5000);

        foreach (Project project in dte.Solution.Projects)
        {
            if (project.UniqueName.EndsWith(projectName))
                foreach (string s in GetFilesNotInProject(project, extension))
                {
                    FileInfo fi = new FileInfo(s);
                    File.SetAttributes(s, FileAttributes.Normal);
                    File.Delete(s);
                    returnValue++;
                }
        }

        dte.Quit();

        MessageFilter.Revoke();

        return returnValue;
    }

while (!dte.Solution.IsOpen) 行抛出异常。我试着把它注释掉,然后它被抛到 foreach (Project project in dte.Solution.Projects) 行。

备注:

使用 CoRegisterMessageFilter function 注册的 IOleMessageFilter 的实现必须在 STA 单元线程中。

来自 CoRegisterMessageFilter 函数文档:

Only one message filter can be registered for each thread. Threads in multithreaded apartments cannot have message filters.

您的问题表明您正在通过小型控制台应用程序进行移植,并表明您正在使用 C#。我不知道 porting 到底意味着什么,但是如果原始代码在 VB.Net 中,VB.Net 会自动将控制台应用程序标记为 STAThreadAttribute,其中- 因为 C# 没有,因此线程是在 MTA 线程中创建的。

在 C# 中,您将属性应用于入口方法 (Main),如下所示。

namespace ConsoleApp1
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
        }
    }
}

所以我遇到了与这个问题类似的问题。我按照接受的答案中的指示添加了 STAThread 属性,但除了获得 RPC_E_SERVERCALL_RETRYLATER 异常外,我还获得了 RPC_E_CALL_REJECTED 异常。经过一番谷歌搜索后,我发现了以下 MS 文档:

https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/ms228772(v=vs.100)?redirectedfrom=MSDN

上面来自 MS 的文档指出,为了避免这些错误,我们可以实现一个 COM 消息过滤器 (IOleMessageFilter),如果我们收到 RPC_E_CALL_REJECTED 消息,它只是告诉 COM 接口重试,如果我们收到 RPC_E_SERVERCALL_RETRYLATER 留言。

希望这对迷路的人有所帮助。