Есть ли лучший способ обработки исключений RPC_E_CALL_REJECTED при выполнении автоматизации Visual Studio? - PullRequest
5 голосов
/ 16 марта 2011

Вот что я сейчас делаю:

    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, и это работает достаточно хорошо. У меня проблема, когда я перебираю коллекцию и что-то делаю с каждым элементом один раз.

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

Иногда я получаю исключение в строке foreach(ProjectItem pi in pProject.ProjectItems). Поскольку я не хочу начинать цикл foreach заново, если получаю исключение RPC_E_CALL_REJECTED, я не уверен, что я могу сделать.

Изменить, чтобы ответить на комментарий: Да, я автоматизирую VS из другой программы, и да, я обычно использую VS для чего-то другого одновременно. У нас есть приложение, которое читает XML-файл, а затем генерирует около 50 VS-решений на основе XML-файла. Обычно это занимает пару часов, поэтому я стараюсь выполнять другую работу, пока это происходит.

Ответы [ 4 ]

3 голосов
/ 06 июня 2011

На этой странице MSDN есть решение: Как: исправить ошибки «Приложение занято» и «Ошибка вызова отклонена вызывающим абонентом» . В нем показано, как реализовать интерфейс COM IOleMessageFilter, чтобы он автоматически повторял вызов.

1 голос
/ 19 декабря 2011

Мне не очень повезло с рекомендованным способом из MSDN, и он показался мне довольно сложным. Что я сделал, так это заключил в себе логику повторных попыток, как в оригинальном посте, в общую вспомогательную функцию. Вы называете это так:

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

Функция 'call' вызывает функцию (передается как лямбда-выражение) и при необходимости повторяется. Поскольку это универсальная функция, вы можете использовать ее для вызова любых свойств или методов 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.");
}

Как говорится в оригинальном сообщении, foreach для коллекций EnvDTE может быть проблемой, поскольку во время зацикливания существуют неявные вызовы. Поэтому я использую функцию call для получения свойства Count, а затем выполняю итерацию с использованием индекса. Это хуже, чем foreach, но функция 'call' делает это не так уж плохо, так как не так много попыток ... ловит вокруг. Например:

int numProjects = Utils.call(() => (projects.Count));
for (int i = 1; i <= numProjects; ++i)
{
    Project project = Utils.call(() => (projects.Item(i)));
    parseProject(project);
}
1 голос
/ 17 марта 2011

Во-первых, Ганс не хочет так говорить, но лучший ответ на вопрос «как это сделать» - «не делай этого». Просто используйте отдельные экземпляры Visual Studio для вашей автоматизации и вашей другой работы, , если это вообще возможно .

Вы должны вынести свою формулировку проблемы куда-нибудь, где вы можете обработать ошибку. Вы можете сделать это, используя целочисленный индекс вместо 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);
            }
        }  
   }

}
0 голосов
/ 11 ноября 2015

Я получаю ту же ошибку, используя C # для чтения / записи в Excel. Как ни странно, он работал в режиме отладки, но не на развернутой машине. Я просто изменил приложение Excel на Visible, и оно работает нормально, хотя примерно в два раза медленнее. Раздражает динамическое открытие и закрытие приложения Excel на экране, но это, кажется, самый простой обходной путь для Excel.

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