COM-исходящий вызов приводит к "Исходящий вызов не может быть выполнен, так как приложение отправляет синхронный вызов".

У меня есть COM-сервер (С++/STA (приложение на основе MFC)) и COM-клиент (С#/MTA). COM-сервер должен жить в STA, так как это приложение MFC (у меня нет выбора в этом вопросе). Клиент выдает вызов серверу, а сервер выдает обратный вызов клиенту. То, где происходит ошибка (RPC_E_CANTCALLOUT_ININPUTSYNCCALL). Я предполагаю, что если сервер был MTA, эта проблема никогда бы не возникла, но, к сожалению, документация для MFC явно отрицает инициализацию квартиры как MTA.

Любые идеи о том, как обойти эту проблему?

Я играю с идеей позволить объекту сервера (объект, который я просматриваю в текущей таблице объектов) жить в собственной квартире (MTA). Будет ли это хорошей идеей, или есть что-то более простое, чтобы попробовать в первую очередь?

ОБНОВЛЕНИЕ

Объект сервера - это просто тонкий интерфейс, указывающий на определенные функции внутри приложения. Большую часть времени он просто читает и записывает в ячейки памяти, но есть экземпляры, где он генерирует оконные сообщения для различных окон в приложении. Сам объект сервера - это не все приложение.

Ответ 1

RPC_E_CANTCALLOUT_ININPUTSYNCCALL означает, что вы попытались сделать маршированный COM-вызов из обработчика для сообщения Windows, отправленного через SendMessage. Это поможет избежать определенных ситуаций взаимоблокировки. У вас есть несколько вариантов, которые сводятся к "избегать вызовов COM в обработчике SendMessage":

  • Вы можете использовать PostMessage для очереди сообщения для себя, а в этом отправленном обработчике сообщений вызывать обратный вызов COM.
  • Вы можете использовать асинхронный DCOM и избегать блокировки результата вызова из обработчика сообщений.
  • Вы можете маршалить интерфейс обратного вызова, затем вызвать его из thread элемент работы пула. Поскольку это не зависит от основного цикла сообщений приложения, это не будет в вызове SendMessage, и он может даже быть в MTA.
  • Вы можете отказаться от поддержки MFC COM и вызвать CoRegisterClassObject непосредственно из другого потока. Это означает, что любые вызовы на ваш COM-COM-сервер будут вызываться из пула потоков COM (или, если вы используете поток STA, из этого потока), а не из потока MFC-интерфейса, поэтому вам нужно будет использовать сообщения Windows для связи через потоки; но если вам нужно сделать синхронные обратные вызовы клиенту, это лучший подход.

Ответ 2

Независимо от того, насколько сильно я поворачиваюсь и поворачиваюсь, я не могу удалить себя из контекста STA в приложении, когда я вызываюсь от клиента. Неважно, если я размещаю серверный объект в MTA, мне все равно придется подчиняться законам COM. В этом случае STA является действительно противным "исправительным учреждением". Я делаю тяжелое время...

Это привело меня к довольно уродливому пути, но он работает. Вместо того, чтобы использовать COM для обратной связи с клиентом, я вручную переношу свой собственный путь связи в MTA, который размещает объект сервера и ссылки обратного вызова. Я в основном создаю свой собственный код сортировки, настраивая очередь вызовов (контейнер STL с параметрами для отправки), которые поток MTA берет и доставляет клиенту. Затем ответ возвращается к коду, отвечающему на начальный вызов. Синхронизация выполняется с использованием объектов событий Win32.

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

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

Ответ 3

Вы можете создать таймер, когда получите сообщение, а затем выполните свои COM-вызовы и последующую обработку в своем TimerProc/WinProc под WM_TIMER. Это работает для меня.