Мне не очень повезло с рекомендованным способом из 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);
}