Как обеспечить отсутствие повторного входа на мой COM-сервер основной STA (C ++)? - PullRequest
7 голосов
/ 16 февраля 2010

Хорошо, я подозреваю, что мне будет трудно даже выразить это словами, так как мое понимание COM и апартаментов не совсем подходит для работы; -)

У меня есть внутрипроцессный сервер / компонент COM (C ++), который оборачивает некоторый устаревший код. Из-за ограничений этого унаследованного кода мне нужно убедиться, что методы компонента COM:

  1. вызывается только в одном потоке.
  2. это всегда один и тот же поток для всех экземпляров сервера.
  3. (что я понял только позже) никаких повторных звонков.

Первые два я достиг, зарегистрировав сервер с ThreadingModel = "".

3-я проблема, с которой я даже удивился.

Сервер используется многопоточным клиентом, который я не могу контролировать. Он создает несколько экземпляров сервера / компонента в разных потоках и вызывает их метод DoSomething ().

Это приводит к выбору поведения зависания и сбоя, и я видел трассировки стека, содержащие два вызова DoSomething () как в потоке main-STA, но для разных экземпляров сервера.

Изначально я даже не думал, что это возможно, но теперь у меня есть частичное понимание, и мне нужно знать, если / как это можно предотвратить.

Мое чтение говорит о том, что мне может понадобиться использовать IMessageFilter каким-либо образом, но я не уверен, что это может быть сделано на стороне сервера, или это должен сделать клиент.

Кто-нибудь может помочь?

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

Ответы [ 3 ]

5 голосов
/ 16 февраля 2010

COM маршалы звонки на ваш сервер через цикл сообщений. Вы можете попасть в ад повторного входа с помощью модального цикла COM. Этот цикл заботится о исходящих вызовах, которые необходимо перенаправить обратно в поток клиента. Нравится события. Или звонки вашего сервера на другой сервер, который находится в другой квартире STA. В то время как модальная петля перекачивает сообщения, ожидая завершения исходящего вызова, она может принимать входящие вызовы, как обычная петля сообщений.

Технически, повторный вход может также произойти, когда ваш сервер пытается поддерживать живой пользовательский интерфейс, перекачивая сообщения самостоятельно.

Да, это может быть очень , с которым трудно иметь дело. Я не знаю ни одного магического решения для этого, и я долго искал. Я также посмотрел на IMessageFilter и пришел к выводу, что решать эту проблему бесполезно.

3 голосов
/ 16 февраля 2010

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

ПростоИдея, хотя: если ваш COM-объект всегда должен работать в STA и вызываться только от клиентов, работающих в других квартирах (например, MTA), возможно, вы могли бы прочитать о custom marshalling (предупреждение: не дляслабонервных).Поскольку ваш унаследованный код, по-видимому, не предъявляет никаких требований для запуска в основной STA или, более конкретно, взаимодействует с компонентами графического интерфейса, это может оказаться альтернативой.

2 голосов
/ 16 февраля 2010

Как указывает nobugz, это проблема из-за цикла сообщений.

Я бы, наверное, посмотрел на решение этой проблемы внутри COM-объекта. Это зависит от сложности ваших вызовов, но вы можете рассмотреть вопрос о наличии рабочей очереди внутри вашего COM-объекта, которая обрабатывается одним потоком, который находится под вашим контролем. Затем вы вручную перенаправляете свои входные параметры в блок данных, который вы публикуете в очереди, ваш COM-вызов затем блокирует событие в пользовательской структуре данных вашей очереди «на вызов». Когда вызов обрабатывается в одном потоке, который обрабатывает вашу рабочую очередь, вы извлекаете параметры, делаете вызов, а затем упаковываете результат в пользовательскую структуру данных и устанавливаете событие ... Теперь ваш вызов com может распаковать вещи и вернуться.

Конечно, дьявол кроется в деталях. Я бы лично использовал IOCP для создания пользовательской очереди, но это всего лишь детали реализации.

Да, вы выполняете больше работы, но, учитывая ваши требования (один поток для выполнения всех вызовов и отсутствие повторного входа), это, вероятно, самый надежный способ сделать это. Затем вы можете, конечно, отбросить требования STA для самого COM-объекта, поскольку вы создали объект с поддержкой многопоточности, который может работать в любой квартире ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...