在进行 Visual Studio 自动化时,是否有更好的方法来处理 RPC_E_CALL_REJECTED 异常?

发布于 2024-10-22 01:40:53 字数 1197 浏览 2 评论 0 原文

这就是我目前正在做的事情:

    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 解决方案。这通常需要几个小时,所以我会在发生这种情况时尝试做其他工作。

this is what I'm currently doing:

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

now I have that try catch block around most calls to the EnvDTE stuff, and it works well enough. The problem I have is when I to loop through a collection and do something to each item once.

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

Sometimes I get the exception in the foreach(ProjectItem pi in pProject.ProjectItems) line.
Since I don't want to start the foreach loop over if I get the RPC_E_CALL_REJECTED exception I'm not sure what I can do.

Edit to answer comment:
Yes I'm automating VS from another program and yes I usually am using VS for something else at the same time. We have an application that reads an xml file then generates around 50 VS solutions based on the xml file. This usually takes a couple of hours so I try to do other work while this is happening.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你与清晨阳光 2024-10-29 01:40:53

此 MSDN 页面上有一个解决方案:如何:修复“应用程序正忙”和“呼叫被被呼叫者拒绝”错误。它展示了如何实现 COM IOleMessageFilter 接口,以便它自动重试调用。

There is a solution on this MSDN page: How to: Fix 'Application is Busy' and 'Call was Rejected By Callee' Errors. It shows how to implement a COM IOleMessageFilter interface so that it will automatically retry the call.

屋顶上的小猫咪 2024-10-29 01:40:53

首先,汉斯不想这么说,但“如何做到这一点”的最佳答案是“不要这样做”。如果可能的话,只需将 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);
            }
        }  
   }

}

First, Hans doesn't want to say so but the best answer to "how to do this" is "don't do this". Just use separate instances of visual studio for your automation and your other work, if at all possible.

You need to take your problem statement out somewhere you can handle the error. You can do this by using in integer index instead of 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);
            }
        }  
   }

}
一束光,穿透我孤独的魂 2024-10-29 01:40:53

我对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);
}

I didn't have much luck with the recommended way from MSDN, and it seemed rather complicated. What I have done is to wrap up the re-try logic, rather like in the original post, into a generic utility function. You call it like this:

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

The 'call' function calls the function (passed in as a lambda expression) and will retry if necessary. Because it is a generic function, you can use it to call any EnvDTE properties or methods, and it will return the correct type.

Here's the code for the function:

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.");
}

As the original post says, foreach over EnvDTE collections can be a problem as there are implicit calls during the looping. So I use my 'call' function to get the Count proprty and then iterate using an index. It's uglier than foreach, but the 'call' function makes it not so bad, as there aren't so many try...catches around. For example:

int numProjects = Utils.call(() => (projects.Count));
for (int i = 1; i <= numProjects; ++i)
{
    Project project = Utils.call(() => (projects.Item(i)));
    parseProject(project);
}
浅唱ヾ落雨殇 2024-10-29 01:40:53

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

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

I was getting the same error using C# to read/write to Excel. Oddly, it worked in debug mode but not on a deployed machine. I simply changed the Excel app to be Visible, and it works properly, albeit about twice as slow. It is annoying to have an Excel app open and close dynamically on your screen, but this seems to be the simplest work-around for Excel.

Microsoft.Office.Interop.Excel.Application oApp = new ApplicationClass();
oApp.Visible = true;
oApp.DisplayAlerts = false;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文