Как получить доступ к C ++ COM-объекту в C # Фоновый рабочий? - PullRequest
0 голосов
/ 22 февраля 2019

Я разрабатываю общий компонент C # (dll), который может использоваться любым COM-клиентом.Как часть этого я выставляю исходящий интерфейс, называемый ICallback. Клиент, который использует мою dll, реализует методы и предоставляет объект com через метод входящего интерфейса,

Например,

Ниже2 интерфейса доступны моему c # dll

interface IMyInInterface 
 {
      void Initialize(ICallback object);
 }

 interface ICallback
{
      void doSomething();
}

my c # dll реализует IMyInInterface.

Таким образом, клиентское приложение вызывает метод Initialize и передает указатель ICallback реализации IMyInInterface.

Мой клиент - C ++ atl dll, в котором есть реализация для ICallback.

C ++ Вызов:

         .... 
         /* Code to create callbackImpl goes here */

         MyImplObj.Initialize(callbackImpl);

Мой c # dll - это wpf dll.Поэтому, когда к объекту ICallback обращаются в потоке пользовательского интерфейса, он может вызывать ICallback.Dosomething (). Он отлично работает.

C # Реализация:

class MyImpl : IMyInterface
{

...
            ICallback _Mycallback = null;

            void Initialize(ICallback callback)
             {
                 _MyCallback = callback         
             }

              Button_Click()
              { 
                 _Mycallback.DoSomething();       **// This works fine**
              } 
}

НоПоскольку Dosomething () - это длительная задача, я хотел выполнить эту работу в parllel, поэтому для этого я использую BackgroundWorker.

Когда ICallback.Dosomething () вызывается из фонового работника, как показано ниже, я получаю исключение, так как интерфейс не найден,

class MyImpl : IMyInterface
    {

    ...
                ICallback _Mycallback = null;

                void Initialize(ICallback callback)
                 {
                     _MyCallback = callback         
                 }

                  Button_Click()
                  { 
                     //Code to create Background worker //

                    BackgroundWorker bkgrwkr = new BackgroundWorker();
                    bkgrwkr.WorkerReportsProgress = true;
                    bkgrwkr.WorkerSupportsCancellation = true;            
                    bkgrwkr.DoWork += new DoWorkEventHandler(Worker_DoWork);

                    .....

                  } 

                  void Worker_DoWork()
                  {
                        _Mycallback.DoSomething();       **// This Fails**
                  }
    }    

происходит сбой с исключением интерфейса не найден.

Невозможно привести объект COM типа 'System .__ ComObject' к типу интерфейса 'ICallback'.Эта операция завершилась неудачно, поскольку вызов QueryInterface для компонента COM для интерфейса с IID '{3B7C00E3-C145-4195-B9B4-984EAAC8954D}' завершился неудачно из-за следующей ошибки: такой интерфейс не поддерживается (Исключение из HRESULT: 0x80004002 (E_NOINTERFACE)).

Трассировка стека:

в System.StubHelpers.StubHelpers.GetCOMIPFromRCW (Объект objSrc, IntPtr pCPCMD, IntPtr & ppTarget, Boolean & pfNe)MyDLL.ICallback.DoSomething ()

Как использовать com-объекты из Background worker?Даже я пытался сделать что-то из Thread с квартирой, установленной как STA.такая же ошибка происходит.

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Я нашел причину этой проблемы. Проблема связана с тем, что BackgroundWorker использует Appartment в качестве MTA.Когда я попытался получить состояние квартиры внутри Worker_DoWork с помощью метода System.Threading.Thread.CurrentThread.GetApartmentState (), он вернул MTA.Это основная причина проблемы:

Теперь я решил проблему, используя Задачу, как показано ниже, Используя задачу, также достигнуто параллельное выполнение.

       Button_Click()
       {  
           Task workerTask = Task.Factory.StartNew(
           () =>
           {
               DoWorkDelegate();
           }
           , CancellationToken.None
           , TaskCreationOptions.None
           , TaskScheduler.FromCurrentSynchronizationContext()
           ); 

           workerTask.wait();
       }

       void DoWorkDelegate()
       {
        _Mycallback.DoSomething();     // This works 
       }

Передача TaskScheduler.FromCurrentSynchronizationContext () разрешаетвопрос.Это гарантирует sychronizationContext из Task так же, как текущий контекст.

0 голосов
/ 25 февраля 2019

Проблема в том, что код, запущенный в BackgroundWorker, выполняется в потоке в пуле потоков, где модель квартиры COM не установлена.

Я рекомендую вам вызывать ваш интерфейс из выделенного потока.Используйте Thread.SetApartmentState(ApartmentState.STA); перед вызовом Thread.Start().

Кроме того, объект, который вы передаете Initialize, должен быть создан в потоке того же .Если нет, то такие объекты нужно составлять в особом порядке.Найти более подробную информацию здесь .

...