VB6 GUI не работает в многопоточной среде COM - PullRequest
0 голосов
/ 12 октября 2009

У меня есть COM-клиент VB6, который выполняет вызовы на неработающий STA ATL / COM-сервер. Один из методов сервера, X, может занять некоторое время, чтобы закончить, поэтому я должен иметь возможность отменить его. Я попытался запустить код метода в новом потоке и включить другой метод, Y, который выполняет синхронизацию по времени WaitForSinleObject. Таким образом, клиент сначала вызывает X, затем переходит в цикл, вызывающий VB6 DoEvents, а затем Y, пока Y не покажет, что X завершено. Это прекрасно работает, однако, ложка дегтя заключается в том, что поток X также инициирует события обратно к клиенту через интерфейс IConnectionPoint. События проходят нормально, но любые вызовы GUI не работают, потому что, насколько я могу судить, GUI может работать только в одном потоке, то есть в основном потоке.

Есть ли очевидный способ обойти это, используя мой существующий код? В качестве альтернативы, пожалуйста, можете ли вы предложить другие способы, которыми я мог бы сделать это.

Заранее спасибо.

1 Ответ

1 голос
/ 12 октября 2009

Вы всегда должны маршалировать ваши звонки с точки подключения. Если вы этого не сделаете, вы можете вызвать код VB, но он не работает случайным образом (не маршалированные объекты) или просто не работает (GUI).

Чтобы использовать маршалинг, вам нужно реализовать несколько интерфейсов (см. Ниже).

Другая возможность - преобразовать асинхронные вызовы в VB в синхронные вызовы 'fetch'.

Итак, ваш код взят из (в псевдокоде C ...):

while( !wait( X ) )
{
   doevents();
}

до:

while( !wait( X ) )
{
    doevents();
    fetch_async_data();
}

1) Добавьте маршаллера в свой класс, добавив его в таблицу COM_AGGRGATE:

CComPtr<IUnknown> m_pUnkMarshaler;

BEGIN_COM_MAP(..)
   ...
   COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
END_COM_MAP()

2) Создайте маршаллера в FinalConstruct ()

FinalConstruct()
{
    HRESULT rval = CoCreateFreeThreadedMarshaler( GetControllingUnknown(), &m_pUnkMarshaler.p );
    ...
}

FinalRelease()
{ ...; m_pUnkMarshaler = 0; }

3) Извлеките вашу точку подключения из IConnectionPointImplMT и заблокируйте вызовы внутри системы, если вы можете одновременно выполнять более одного вызова.

4) Не ждите бесконечно в методах вашего объекта, потому что вы можете работать в тупиках.

5) Повторите это для каждого открытого объекта и точки подключения.

(Это должно сработать, но я давно не пробовал ...)

...