Всем добрый день.
У меня такой вопрос.
Как получить указатель на другой интерфейс из моего метода с использованием ATL?
Например, реализация моего метода такая:
Но, почему-то всегда получаю ошибку, после вызова этого метода в клиенте, типа: «Приложение обратилось к объекту из другого потока».
Как правильно построить COM объект и передать на него указатель клиенту в этом случае?
Всё хорошо, непонятно зечем переменные глобальные. Или это они просто обозначены так? Ну да на эту ошибку они повлиять не должны были.
NEW>Но, почему-то всегда получаю ошибку, после вызова этого метода в клиенте, типа: «Приложение обратилось к объекту из другого потока». NEW>Как правильно построить COM объект и передать на него указатель клиенту в этом случае?
А ты потом этот указатель не передаешь в другой поток случайно? Если да, то вот и получаешь.. Если нет, то странно.
Здравствуйте, NEW_DVA, Вы писали:
NEW>Всем добрый день. NEW>У меня такой вопрос. NEW>Как получить указатель на другой интерфейс из моего метода с использованием ATL? NEW>Например, реализация моего метода такая:
NEW>Но, почему-то всегда получаю ошибку, после вызова этого метода в клиенте, типа: «Приложение обратилось к объекту из другого потока». NEW>Как правильно построить COM объект и передать на него указатель клиенту в этом случае?
NEW>Всем буду, благодарен за помощь. NEW>Спасибо.
Спасибо.
Я попробовал ваш вариант, всё ровно выскакивает та же ошибка в клиентском приложении, при вызове метода уже у созданного объекта (IMyObj). -2147417842 (8001010E) The application called an interface that was
marshalled for a different thread.
SH>А ты потом этот указатель не передаешь в другой поток случайно? Если да, то вот и получаешь.. Если нет, то странно.
Нет, не передаю.
Клиент очень простой:
IMyClass *pIMyClass = NULL;
IMyObj *obj = NULL;
int param;
HRESULT hRes = ::CoCreateInstance(CLSID_MyClass,
NULL,
CLSCTX_SERVER,
IID_IMyClass,
(void**)&pIMyClass);
if(SUCCEEDED(hRes))
{
hRes = pIMyClass->GetMyObject(IID_IMyObj, (IUnknown**)&obj);
if(SUCCEEDED(hRes))
{
hRes = obj->Method1(¶m);/* тут возникает ошибка: -2147417842 (8001010E) The application called an interface that was
marshalled for a different thread.*/
hRes = obj->Release();
}
pIMyClass->Release();
}
NEW>>-2147417842 (8001010E) The application called an interface that was NEW>> marshalled for a different thread.
SH>- а до передачи он работает? Т.е. из GetMyObject его можно вызвать и всё будет хорошо? SH>- не, ты точно его никуда не передаёшь?
1)В методе MyClass::GetMyObject все методы интерфейса IMyObj вызываются нормально, например мы вызываем метод pCMyObj ->AddRef(); (используя CopyTo или не используя).
2) Вот клиент, он очень простой:
IMyClass *pIMyClass = NULL;
IMyObj *obj = NULL;
int param;
HRESULT hRes = ::CoCreateInstance(CLSID_MyClass,
NULL,
CLSCTX_SERVER,
IID_IMyClass,
(void**)&pIMyClass);
if(SUCCEEDED(hRes))
{
hRes = pIMyClass->GetMyObject(IID_IMyObj, (IUnknown**)&obj);
if(SUCCEEDED(hRes))
{
hRes = obj->Method1(¶m);/* тут возникает ошибка: -2147417842 (8001010E) The application called an interface that was
marshalled for a different thread.*/
hRes = obj->Release();
}
pIMyClass->Release();
}
Здравствуйте, NEW_DVA, Вы писали:
NEW>1)В методе MyClass::GetMyObject все методы интерфейса IMyObj вызываются нормально, например мы вызываем метод pCMyObj ->AddRef(); (используя CopyTo или не используя).
AddRef не считается. Попробуй что-нибудь более существенное. Или поппробуй AddRef из клиента.
Кстати, что за сервер? exe, dll?
Здравствуйте, SergH, Вы писали:
SH>AddRef не считается. Попробуй что-нибудь более существенное. Или поппробуй AddRef из клиента. SH>Кстати, что за сервер? exe, dll?
Да, уже попробовал все методы вызываются нормально, например IMyObj::Method1(int* param);
Это exe – сервер, точнее сервис.
Все COM классы наследуются от CComObjectRootEx<CComSingleThreadModel>.
Может какой-то другой подход есть к решению этой проблемы, например объект строить как-то по-другому?
Здравствуйте, NEW_DVA, Вы писали:
NEW>Это exe – сервер, точнее сервис. NEW>Все COM классы наследуются от CComObjectRootEx<CComSingleThreadModel>.
Угу, с этого надо было начинать. В смысле, сразу сказать. А мне спросить...
NEW>Может какой-то другой подход есть к решению этой проблемы, например объект строить как-то по-другому?
Да нет, ты всё правильно делаешь. Кроме того, как подругому создавать объект я не знаю.
Я думаю, у тебя где-то не согласованы поточные модели, поэтому глючит маршалинг. Пока не представляю, правда, где...
На всякий случай для прояснения ситуации:
1. Как инициализируется COM в службе? COINIT_APARTMENTTHREADED/COINIT_MULTITHREADED?
2. Что там происходит дальше? В смысле, цикл выборки сообщений или ожидание какого-нибудь события?
3. Как в сервере с потоками? Один, два, много?
4. До кучи — как клиент инициализирует COM?
Здравствуйте, SergH, Вы писали:
SH>Я думаю, у тебя где-то не согласованы поточные модели, поэтому глючит маршалинг. Пока не представляю, правда, где... SH>На всякий случай для прояснения ситуации:
SH>1. Как инициализируется COM в службе? COINIT_APARTMENTTHREADED/COINIT_MULTITHREADED? SH>2. Что там происходит дальше? В смысле, цикл выборки сообщений или ожидание какого-нибудь события? SH>3. Как в сервере с потоками? Один, два, много? SH>4. До кучи — как клиент инициализирует COM?
1) COM инициализируется в службе следующим образом, в методе:
Вообще то, я создавал все СОМ объекты как Apartment. Чем в принципе отличаются объекты Apartment и Single я так и не понимаю….(но, это уже другая тема…)
Дальше всё идёт стандартно, всё что генерит Wizard у VisulStudio.
Принудительно я ни где потоки не создаю. Всё упрощенно максимально, чтобы разобраться с этой проблемой.
MIDL простой, для этого примера:
ConnectionPoint я пока не трогал, т.е. ни какую реализацию на клиенте ещё не делал.
У клиента, то же инициализация СОМ стандартная, через CoInitialize(NULL).
Вот этому интерфейсу нужна пользовательская прокси/стаб: стандартная прокси/стаб не оказывает влияние riid на возвращаемый интерфейс — возвращается стандартный интерфейс IUnknown, который просто не имеет всех методов интерфейса riid. Так что проще задействовать QueryInterface.
PS
Вот в этом и есть польза void** вместо IUnknown ** в таких случаях.
Здравствуйте, Vi2, Вы писали:
Vi2>Вот этому интерфейсу нужна пользовательская прокси/стаб: стандартная прокси/стаб не оказывает влияние riid на возвращаемый интерфейс — возвращается стандартный интерфейс IUnknown, который просто не имеет всех методов интерфейса riid. Так что проще задействовать QueryInterface.
О, раз уж появился, объясни мне, что у меня происходит при попытке воспроизвести?
— Если сделать IUnknown**, то ты вроде объяснил, поведение стабильное. Я правильно понял, что просто то, что если что-то прописано в midl-е в качестве интерфейса, то iid_is не работает? Или как?
— Если сделать void** (я тоже сразу про него подумал, только я шёл от соображений, что раз QI работает, так просто повторим его интерфейс), всё собирается, но при создании объекта-сервера (т.е. первый CoCreateInstance в клиенте) возвращается ошибка 0x80020008 — "Неверный тип переменной". Это что, не вышло создать такой прокси что-ли?
А работающий вариант выглядит так: возвращать IUnknown, а QI выполнять на клиенте. Все методы IUnknown вызываются нормально.
Здравствуйте, SergH, Вы писали:
SH>- Если сделать IUnknown**, то ты вроде объяснил, поведение стабильное. Я правильно понял, что просто то, что если что-то прописано в midl-е в качестве интерфейса, то iid_is не работает? Или как?
В библиотеке типа нет места для информации, связанной с iid_is. Там указан базовый тип параметра, а это IUnknown. Вот к нему все и приводится.
SH>- Если сделать void** (я тоже сразу про него подумал, только я шёл от соображений, что раз QI работает, так просто повторим его интерфейс), всё собирается, но при создании объекта-сервера (т.е. первый CoCreateInstance в клиенте) возвращается ошибка 0x80020008 — "Неверный тип переменной". Это что, не вышло создать такой прокси что-ли?
Уже не помню, что тут должно быть. Экспериментировал давным-давно, на практике не приходится пользоваться, так что все позабылось. Осталось ощущение, что тип void является типом ограниченного использования и вызывает некоторую диагностику.
SH>А работающий вариант выглядит так: возвращать IUnknown, а QI выполнять на клиенте. Все методы IUnknown вызываются нормально.
Для возрата интерфейсов ЭТОГО же объекта нет нужды придумывать такие функции. Если объект работает в качестве фабрики ДРУГИХ объектов, то лучше реализовать стандартный IServiceProvider с более богатыми возможностями.
Здравствуйте, Vi2, Вы писали:
Vi2>В библиотеке типа нет места для информации, связанной с iid_is. Там указан базовый тип параметра, а это IUnknown. Вот к нему все и приводится.
А где же он тогда нужен? Или только в автоматически генерируемом custom-proxy?
Vi2>Уже не помню, что тут должно быть. Экспериментировал давным-давно, на практике не приходится пользоваться, так что все позабылось. Осталось ощущение, что тип void является типом ограниченного использования и вызывает некоторую диагностику.
Я думал, dual интерфейс с ним вообще не соберётся.. А он собрался (tlb есть, и там void**, я проверил), но не создался..
Vi2>Для возрата интерфейсов ЭТОГО же объекта нет нужды придумывать такие функции. Если объект работает в качестве фабрики ДРУГИХ объектов, то лучше реализовать стандартный IServiceProvider с более богатыми возможностями.
Так, извини, я опять не понял..
Возврат чужих заданных заранее — просто возвращаем.
Возврат своих интерфейсов заданных заранее — просто возвращаем.
Возврат своих интерфейсов по IID — QI.
Возврат чужих по IID — а почему IServiceProvider, чем он лучше самопального решения? .
Здравствуйте, SergH, Вы писали:
SH>А где же он тогда нужен? Или только в автоматически генерируемом custom-proxy?
Не знаю.
SH>Я думал, dual интерфейс с ним вообще не соберётся.. А он собрался (tlb есть, и там void**, я проверил), но не создался..
Он собраться может, т.к. VT_VOID описывает данные именно void. Но при разборе параметров при построении прокси/стаб должна появляться ошибка. Потому как неизвестно как маршаллить данные VT_VOID и VT_PTR (и, может быть, еще один VT_PTR).
SH>Так, извини, я опять не понял.. SH>Возврат чужих заданных заранее — просто возвращаем. SH>Возврат своих интерфейсов заданных заранее — просто возвращаем.
Заданные заранее вроде бы как и не рассматриваются здесь вовсе. Иначе смысла в параметре riid нет.
SH>Возврат своих интерфейсов по IID — QI. SH>Возврат чужих по IID — а почему IServiceProvider, чем он лучше самопального решения? .
Вот ты когда пользуешься QI, ты смотришь на тип интерфейса, с помощью которого ты запрашиваешь новый интерфейс? Нет. Главное чтобы объект был соответствующий. Так и с помощью IServiceProvider можно у любого объекта приложения запрашивать построение других объектов. Если, конечно, реализовано.