【问题标题】:is there a better way to handle RPC_E_CALL_REJECTED exceptions when doing visual studio automation?在进行 Visual Studio 自动化时,是否有更好的方法来处理 RPC_E_CALL_REJECTED 异常?
【发布时间】:2011-07-16 20:41:22
【问题描述】:

这是我目前正在做的:

    protected void setupProject()
    {
        bool lbDone = false;
        int liCount = 0;
        while (!lbDone && liCount < pMaxRetries)
        {
            try
            {
                pProject.ProjectItems.Item("Class1.cs").Delete();
                lbDone = true;
            }
            catch (System.Runtime.InteropServices.COMException loE)
            {
                liCount++;
                if ((uint)loE.ErrorCode == 0x80010001)
                {
                    // RPC_E_CALL_REJECTED - sleep half sec then try again
                    System.Threading.Thread.Sleep(pDelayBetweenRetry);
                }
            }
        }
    }

现在我在大多数对 EnvDTE 内容的调用中都有 try catch 块,它运行良好。我遇到的问题是当我遍历一个集合并对每个项目执行一次操作时。

foreach(ProjectItem pi in pProject.ProjectItems)
{
    // do something to pi
}

有时我在foreach(ProjectItem pi in pProject.ProjectItems) 行中遇到异常。 由于我不想在收到 RPC_E_CALL_REJECTED 异常时重新开始 foreach 循环,所以我不确定我能做什么。

编辑回答评论: 是的,我正在从另一个程序自动化 VS,是的,我通常同时将 VS 用于其他东西。我们有一个读取 xml 文件的应用程序,然后基于 xml 文件生成大约 50 个 VS 解决方案。这通常需要几个小时,因此我会在此期间尝试做其他工作。

【问题讨论】:

  • 这是一个丑陋的低级错误。这里有什么不寻常的地方?您是否从另一个程序而不是加载项自动化 VS?运行此程序时,您是否确保 VS 处于空闲状态?
  • @Hans - 我已经用我的回复更新了问题。
  • Hmya,进程外自动化和“尝试做其他工作”是这里的真正原因。为什么不直接启动另一个 VS 实例?
  • @Hans,为什么不回答这个问题?
  • @Ben - 因为那不是他想要的。可以理解,提问者对此有些脾气暴躁。

标签: c# visual-studio-2010 code-generation


【解决方案1】:

首先,Hans 不想这么说,但“如何做到这一点”的最佳答案是“不要这样做”。 如果可能的话,只需为您的自动化和其他工作使用单独的 Visual Studio 实例。

您需要将问题陈述放在可以处理错误的地方。您可以通过使用 in 整数索引而不是 foreach 来做到这一点。

// You might also need try/catch for this!
int cProjectItems = pProject.ProjectItems.Length;
for(iProjectItem = 0; iProjectItem < cProjectItems; iProjectItem++)
{
   bool bSucceeded = false;
   while(!bSucceeded)
   {
        try{
            ProjectItem pi = pProject.ProjectItems[iProjectItem];
            // do something with pi
            bSucceeded = true;
        }catch (System.Runtime.InteropServices.COMException loE)
        {
            liCount++;
            if ((uint)loE.ErrorCode == 0x80010001)                      {
                // RPC_E_CALL_REJECTED - sleep half sec then try again
                System.Threading.Thread.Sleep(pDelayBetweenRetry);
            }
        }  
   }

}

【讨论】:

  • 我不得不使用 getEnumerator、MoveNext 和 Current 而不是 for 循环。
【解决方案2】:

在这个 MSDN 页面上有一个解决方案:How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors。它展示了如何实现 COM IOleMessageFilter 接口,以便它会自动重试调用。

【讨论】:

  • 谢谢,看起来会有所帮助。
  • 这对我修复这些类型的错误很有帮助。
  • 你会如何在 powershell 中做到这一点?
  • @Denis 我不知道 :-) 我建议发布一个单独的问题,询问如何在 PowerShell 中应用该页面上显示的修复。
  • 显然此修复仅在主应用程序设置了 [STAThread] 属性时才有效,否则仍会出现错误。
【解决方案3】:

我对 MSDN 推荐的方法不太满意,而且看起来相当复杂。我所做的是将重试逻辑(就像在原始帖子中一样)包装到一个通用实用程序函数中。你这样称呼它:

Projects projects = Utils.call( () => (m_dteSolution.Projects) );

“call”函数调用函数(作为 lambda 表达式传入)并在必要时重试。因为它是一个泛型函数,所以你可以用它来调用任何 EnvDTE 属性或方法,它会返回正确的类型。

这是函数的代码:

public static T call<T>(Func<T> fn)
{
    // We will try to call the function up to 100 times...
    for (int i=0; i<100; ++i)
    {
        try
        {
            // We call the function passed in and return the result...
            return fn();
        }
        catch (COMException)
        {
            // We've caught a COM exception, which is most likely
            // a Server is Busy exception. So we sleep for a short
            // while, and then try again...
            Thread.Sleep(1);
        }
    }
    throw new Exception("'call' failed to call function after 100 tries.");
}

正如原帖所说,对 EnvDTE 集合的 foreach 可能是一个问题,因为在循环期间存在隐式调用。所以我使用我的“调用”函数来获取 Count 属性,然后使用索引进行迭代。它比 foreach 更难看,但 'call' 功能使它并没有那么糟糕,因为没有太多的 try...catch。例如:

int numProjects = Utils.call(() => (projects.Count));
for (int i = 1; i <= numProjects; ++i)
{
    Project project = Utils.call(() => (projects.Item(i)));
    parseProject(project);
}

【讨论】:

  • 谢谢。我得试一试,它看起来比我目前的方法干净得多。
  • 查看下面的另一个答案 - 来自微软的官方。它工作得更好,因为不需要检测每个函数调用。但是你必须在main中添加[STAThread],否则它不起作用。
【解决方案4】:

我在使用 C# 读取/写入 Excel 时遇到了同样的错误。奇怪的是,它可以在调试模式下工作,但不能在已部署的机器上工作。我只是将 Excel 应用程序更改为可见,它可以正常工作,尽管速度慢了两倍。在屏幕上动态打开和关闭 Excel 应用程序很烦人,但这似乎是 Excel 最简单的解决方法。

Microsoft.Office.Interop.Excel.Application oApp = new ApplicationClass();
oApp.Visible = true;
oApp.DisplayAlerts = false;

【讨论】:

    猜你喜欢
    • 2012-08-30
    • 1970-01-01
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    相关资源
    最近更新 更多