У меня такая проблема.
Есть COM`овская dll`ка, написанная на C++. Внутри dll`ки вызывается CoInitializeEx(NULL, COINIT_MULTITHREADED) — там необходимо использовать многопоточную модель.
При вызове методов dll`ки из приложения, написанного на C++, всё нормально. А при вызове из приложения, написанного на VB, CoInitializeEx возвращает ошибку RPC_E_CHANGED_MODE — "cannot change thread mode after it is set".
Вызов делается так:
Dim a As Object
Set a = CreateObject("XXX.XXX")
a.TestCoInitEx
Кто-нибудь сталкивался с этим? Как вызвать из VB методы многопоточной COM`овской dll`ки?
Здравствуйте, Eugene Sh, Вы писали:
ES>У меня такая проблема. ES>Есть COM`овская dll`ка, написанная на C++. Внутри dll`ки вызывается CoInitializeEx(NULL, COINIT_MULTITHREADED) — там необходимо использовать многопоточную модель. ES>При вызове методов dll`ки из приложения, написанного на C++, всё нормально. А при вызове из приложения, написанного на VB, CoInitializeEx возвращает ошибку RPC_E_CHANGED_MODE — "cannot change thread mode after it is set". ES>Вызов делается так: ES>Dim a As Object ES>Set a = CreateObject("XXX.XXX") ES>a.TestCoInitEx
ES>Кто-нибудь сталкивался с этим? Как вызвать из VB методы многопоточной COM`овской dll`ки?
Проблемма возникает из за того, что VB уже инициализировал аппартамент как STA. Вызывать обьекты, находящиеся в MTA нужно так же как и те, которые в STA. т.е просто вызывать их, но не забыть их правильно зарегистрировать.
ЗЫ: Обычно при реализации обьекта в dll не надо вызывать CoInitialize так как CoInitialize обычно вызывает или клиент COM обьекта или подсистема COM не явно.
Здравствуйте, Tom, Вы писали:
Tom>Здравствуйте, Eugene Sh, Вы писали:
ES>>У меня такая проблема. ES>>Есть COM`овская dll`ка, написанная на C++. Внутри dll`ки вызывается CoInitializeEx(NULL, COINIT_MULTITHREADED) — там необходимо использовать многопоточную модель. ES>>При вызове методов dll`ки из приложения, написанного на C++, всё нормально. А при вызове из приложения, написанного на VB, CoInitializeEx возвращает ошибку RPC_E_CHANGED_MODE — "cannot change thread mode after it is set". ES>>Вызов делается так: ES>>Dim a As Object ES>>Set a = CreateObject("XXX.XXX") ES>>a.TestCoInitEx
ES>>Кто-нибудь сталкивался с этим? Как вызвать из VB методы многопоточной COM`овской dll`ки? Tom>Проблемма возникает из за того, что VB уже инициализировал аппартамент как STA. Вызывать обьекты, находящиеся в MTA нужно так же как и те, которые в STA. т.е просто вызывать их, но не забыть их правильно зарегистрировать.
Sorry за непонятливость, но что значит "просто вызывать их, но не забыть их правильно зарегистрировать"?
Здравствуйте, Tom, Вы писали:
ES>>Sorry за непонятливость, но что значит "просто вызывать их, но не забыть их правильно зарегистрировать"?
Tom>
Tom>Dim MyObj as MyMultiThreadedObject
Tom>MyObj.MyMethod
Tom>
Tom>Зарегестрировать — в реестре правильно. Т.е указать поточную модель или free или both
Ясно. Но проблема в следующем.
Вызов методов то происходит нормально, но вот работают они не так как надо.
Вызывается CoCreateInstance для объектов, лежащих в другой dll, являющейся многопоточной. И CoCreateInstance из-за несовпадения моделей потоков возвращает ошибку E_NOINTERFACE.
Здравствуйте, Eugene Sh, Вы писали:
ES>Здравствуйте, Tom, Вы писали:
ES>>>Sorry за непонятливость, но что значит "просто вызывать их, но не забыть их правильно зарегистрировать"?
Tom>>
Tom>>Dim MyObj as MyMultiThreadedObject
Tom>>MyObj.MyMethod
Tom>>
Tom>>Зарегестрировать — в реестре правильно. Т.е указать поточную модель или free или both
ES>Ясно. Но проблема в следующем. ES>Вызов методов то происходит нормально, но вот работают они не так как надо. ES>Вызывается CoCreateInstance для объектов, лежащих в другой dll, являющейся многопоточной. И CoCreateInstance из-за несовпадения моделей потоков возвращает ошибку E_NOINTERFACE.
ES>В реестре у меня прописано both.
Это кто тебе сказал, что E_NOINTERFACE возникает из за этого? E_NOINTERFACE означает, что QueryInterface этого обьекта не нашёл запрашиваемого интерфейса.
Здравствуйте, Tom, Вы писали:
Tom>Здравствуйте, Eugene Sh, Вы писали:
ES>>Здравствуйте, Tom, Вы писали:
ES>>>>Sorry за непонятливость, но что значит "просто вызывать их, но не забыть их правильно зарегистрировать"?
Tom>>>
Tom>>>Dim MyObj as MyMultiThreadedObject
Tom>>>MyObj.MyMethod
Tom>>>
Tom>>>Зарегестрировать — в реестре правильно. Т.е указать поточную модель или free или both
ES>>Ясно. Но проблема в следующем. ES>>Вызов методов то происходит нормально, но вот работают они не так как надо. ES>>Вызывается CoCreateInstance для объектов, лежащих в другой dll, являющейся многопоточной. И CoCreateInstance из-за несовпадения моделей потоков возвращает ошибку E_NOINTERFACE.
ES>>В реестре у меня прописано both. Tom> Tom>Это кто тебе сказал, что E_NOINTERFACE возникает из за этого? E_NOINTERFACE означает, что QueryInterface этого обьекта не нашёл запрашиваемого интерфейса.
Да. Но когда я вызываю свою dll из программы, написанной на C++, CoInitializeEx(NULL, COINIT_MULTITHREADED) проходит нормально(возвращает S_FALSE, т.е. уже была инициализирована именно во многопоточной модели), и в дальнейшем вызов CoCreateInstance проходит нормально. В чём может быть дело?
ES>Да. Но когда я вызываю свою dll из программы, написанной на C++, CoInitializeEx(NULL, COINIT_MULTITHREADED) проходит нормально(возвращает S_FALSE, т.е. уже была инициализирована именно во многопоточной модели), и в дальнейшем вызов CoCreateInstance проходит нормально. В чём может быть дело?
Да не ломай себе голову. Поставь бряк. поинт в QueryInterface и всё.
Здравствуйте, Tom, Вы писали:
ES>>Да. Но когда я вызываю свою dll из программы, написанной на C++, CoInitializeEx(NULL, COINIT_MULTITHREADED) проходит нормально(возвращает S_FALSE, т.е. уже была инициализирована именно во многопоточной модели), и в дальнейшем вызов CoCreateInstance проходит нормально. В чём может быть дело?
Tom>Да не ломай себе голову. Поставь бряк. поинт в QueryInterface и всё.
Похоже, что такой вызов действительно всегда будет приводить к ошибке — кажется, нельзя из однопоточной модели вызвать объект из многопоточной dll.
Остаётся вопрос — нельзя ли из VB осуществить вызов так, чтобы моя dll загрузилась в многопоточной модели?
Здравствуйте, Eugene Sh, Вы писали:
ES>Здравствуйте, Tom, Вы писали:
ES>>>Да. Но когда я вызываю свою dll из программы, написанной на C++, CoInitializeEx(NULL, COINIT_MULTITHREADED) проходит нормально(возвращает S_FALSE, т.е. уже была инициализирована именно во многопоточной модели), и в дальнейшем вызов CoCreateInstance проходит нормально. В чём может быть дело?
Tom>>Да не ломай себе голову. Поставь бряк. поинт в QueryInterface и всё.
ES>Похоже, что такой вызов действительно всегда будет приводить к ошибке — кажется, нельзя из однопоточной модели вызвать объект из многопоточной dll. ES>Остаётся вопрос — нельзя ли из VB осуществить вызов так, чтобы моя dll загрузилась в многопоточной модели?
Молодой человек Вы сюда пришли за ответом на вопрос. Я Вам отвечаю, что не в этом дело. В чём угодно, но не в этом! Обьекты можно и нужно использовать между аппартаментами. Возможно(и скорее всего) проблемма возникает из за того, что для запрашиваемого интерфейса не зарегистрирован proxy/stub.
Твоя проблема уж точно не связана с VB. Скорее это из общей тоерии СОМ и С++ в частности, т.к. неверен DLL сервер, а не VB клиент.
ES>Есть COM`овская dll`ка, написанная на C++. Внутри dll`ки вызывается CoInitializeEx(NULL, COINIT_MULTITHREADED) — там необходимо использовать многопоточную модель.
Внутри DLL-сервера допускается вызывать CoInitialize/CoInitializeEx только для созданных этой DLL потоков. Вот для них и нужно вызывать инициализацию СОМ. Все остальные вызовы неверны и должны быть отброшены (независимо работает ли правильно клиент на С++ ниже или нет).
ES>При вызове методов dll`ки из приложения, написанного на C++, всё нормально.
Значит, приложение, написанное на C++, ошибочно. А как известно существует закон парности ошибок: 2 ошибки ошибкой не считаются.
ES>А при вызове из приложения, написанного на VB, CoInitializeEx возвращает ошибку RPC_E_CHANGED_MODE — "cannot change thread mode after it is set".
А это и есть следствие неверного вызова CoInitialize/CoInitializeEx в DLL-ке.
ES>Кто-нибудь сталкивался с этим? Как вызвать из VB методы многопоточной COM`овской dll`ки?
Тут проблемы нет, и с этим сталкивался каждый. Расположение объекта в том или ином апартменте определяется его записью в Реестре (Apartment, Free или Both). Все остальные объекты или потоки сервера, которые нужны этому создаваемому объекту, уже на его совести.
PS
Код ошибки E_NOINTERFACE, который ты приводишь, связан с маршалингом в другой апартмент и преодолевается предоставлением прокси/стаба на запрашиваемый интерфейс. Или использованием [oleautomation] атрибута для твоего интерфейса и typelib-маршалинга из СОМа.