Подтвердить что ты не робот

Невозможно исключить 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}' завершился неудачно из-за следующей ошибки: такой интерфейс не поддерживается

Но интерфейс должен поддерживаться просто отлично? У кого-нибудь есть мысли о том, что здесь происходит?

4b9b3361

Ответ 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".