Невозможно исключить COM-тип исключения типа
У меня есть следующий код:
public void Test(IMyInterface iInterface)
{
iInterface.CallMethod ( );
}
Что отлично работает. Однако, если я изменю код для потоковой передачи:
private IMyInterface myInterface;
public void Test(IMyInterface iInterface)
{
myInterface = iInterface;
new Thread ( new ThreadStart ( CallInterfaceMethod) ).Start ( );
}
public void CallInterfaceMethod ( )
{
myInterface.CallMethod ( )
}
Когда я использую поток, я получаю исключение:
Невозможно передать COM-объект типа "System.__ ComObject" в тип интерфейса "IMyInterface".
Эта операция завершилась неудачно, потому что вызов QueryInterface на COM-компоненте для интерфейса с IID '{GUID}' завершился неудачно из-за следующей ошибки: такой интерфейс не поддерживается
Но интерфейс должен поддерживаться просто отлично? У кого-нибудь есть мысли о том, что здесь происходит?
Ответы
Ответ 1
Это неприятное, неприятное исключение возникает из-за концепции, известной как COM-маршаллинг. Суть проблемы заключается в том, что для того, чтобы использовать объекты COM из любого потока, поток должен иметь доступ к информации о типе, которая описывает объект COM.
В описанном сценарии причина сбоя во втором потоке заключается в том, что второй поток не имеет информации о типе интерфейса.
Вы можете попробовать добавить следующее в ваш код:
[ComImport]
[Guid("23EB4AF8-BE9C-4b49-B3A4-24F4FF657B27")]
public interface IMyInterface
{
void CallMethod();
}
По сути, приведенное выше объявление указывает загрузчику COM.NET Framework загружать информацию о типах с использованием традиционных методов из реестра, находить связанную библиотеку типов и переходить оттуда.
Вы также должны ограничить создание COM-объекта одним потоком (чтобы предотвратить сортировку потоков), чтобы помочь решить эту проблему.
Подводя итог, эта ошибка вращается вокруг информации о типе и маршалинге потока. Удостоверьтесь, что каждый поток, который хочет получить доступ к COM-объекту, имеет соответствующую информацию, чтобы демонтировать объект из исходного потока.
PS: эта проблема решена в .NET 4.0 с использованием метода, называемого "эквивалентность типов"
Ответ 2
У меня есть совет, и это помогло мне!
Найдите в основном потоке (Program.cs) строку [STAThread] и измените ее на [MTAThread].
Ответ 3
Я разрабатываю приложение С#, которое использует 7-zip через COM-интерфейсы. Я столкнулся с этой забавной вещью, где мне удалось извлечь архивы из рабочего потока в одном экземпляре, но не в другое, получив такое же исключение.
Я обнаружил, что до тех пор, пока вы инициализируете оскорбительный COM-объект в потоке, где он используется, не генерируется исключение. Мое решение состояло в том, чтобы удалять объекты, которые использовали COM-интерфейсы, и повторно инициализировать их при передаче между потоками.
Ответ 4
Ну, для одного, вы делаете сквозной вызов объекта без его блокировки, это автоматически вызовет некоторые проблемы. Ваш код должен выглядеть больше:
private IMyInterface myInterface;
private static readonly object _myObjectLock = new object();
public void Test(IMyInterface iInterface)
{
myInterface = iInterface;
new Thread ( new ThreadStart ( CallInterfaceMethod) ).Start ( );
}
public void CallInterfaceMethod ( )
{
lock(_myObjectLock)
{
myInterface.CallMethod ( );
}
}
Из того, что я понимаю, ошибка, которую вы указали, может иногда возникать, когда ресурс не может быть доступен, что при такой операции поперечного потока, скорее всего, произойдет. Не цитируйте меня на этом, хотя я не эксперт COM.
Честно говоря, я не думаю, что я бы назвал этот метод таким способом, слишком много рисков при этом. Рассматривали ли вы использование ParameterizedThreadStart и передачу объекта таким образом? Вам все равно нужно безопасно блокировать свои объекты для операций с поперечными потоками, но это было бы безопаснее.
Кроме того, убедитесь, что ваш класс "myInterface" все еще может вызвать метод "CallMethod()". Интерфейсы не имеют реализации, вы можете столкнуться с проблемами при установке "myInterface = iInterface".