есть такая проблема
как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн.
в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы.
соответсвенно для них тоже хочется смартпоинтеры, причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать public-методы не описанные в интерфейсе)
попробовал CComPtr
возникло две проблемы:
1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.
2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.
основная проблема под номером 2.
Наверняка ведь кто-то сталкивался с подобной задачей?
может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?
Здравствуйте, shurik., Вы писали:
S>как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн. S>в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы. S>соответсвенно для них тоже хочется смартпоинтеры,
если есть желание использовать эти смартпойнтеры — нужно в stdafx.h сделать import своей собственной библиотеки типов (и убрать #include в модулях cpp генерируемого по idl заголовочного файла с объявлениями интерфейсов)
>причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать >public-методы не описанные в интерфейсе)
чтобы правильно использовать этот прием, нужно не забыть добавить в карту интерфейсов псевдо-интерфейс с произвольным IID (обычно берут CLSID класса), который возвращает указатель на класс.
S>попробовал CComPtr S>возникло две проблемы: S>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема.
покажи код, который не компилируется
S>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.
и _com_ptr_t (typedef'ы для которого генениурет import) и CComPtr одинаково подходят для решения твоей задачи. Для CComPtr можно написать класс, который будет проверять возвращаемый HRESULT и кидать исключение, если FAILED(HRESULT). Тогда разницы между CComPtr и _com_ptr_t не будет практически никакой, не считая того, что _com_ptr_t более "тяжеловесный".
S>может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?
гораздо более простой и работающий способ — присваивать результат вызова метода экземпляру специального класса, который проверит HRESULT и кинет исключение для FAILED(HRESULT)
Здравствуйте, Ivan, Вы писали:
I>Здравствуйте, shurik., Вы писали:
I>если есть желание использовать эти смартпойнтеры — нужно в stdafx.h сделать import своей собственной библиотеки типов (и убрать #include в модулях cpp генерируемого по idl заголовочного файла с объявлениями интерфейсов)
от этого я только ушёл, потому что такой подход пораждает кучу проблем. (этот код писал не я, я только избавлялся от импорта собственной тлбэхи, может там что-то было не правильно, но уже принято решение так не делать и я этого решения отменить не могу)
I>чтобы правильно использовать этот прием, нужно не забыть добавить в карту интерфейсов псевдо-интерфейс с произвольным IID (обычно берут CLSID класса), который возвращает указатель на класс.
вот это идея, спасибо. Как-нибудь обязательно попробую.
S>>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема. I>покажи код, который не компилируется
а вообще проблему уже решил (отнаследовался от CComPtr и переопределил оператор присваивания)
S>>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.
I>и _com_ptr_t (typedef'ы для которого генениурет import) и CComPtr одинаково подходят для решения твоей задачи. Для CComPtr можно написать класс, который будет проверять возвращаемый HRESULT и кидать исключение, если FAILED(HRESULT). Тогда разницы между CComPtr и _com_ptr_t не будет практически никакой, не считая того, что _com_ptr_t более "тяжеловесный".
здесь стоп. Т.е. возможно написать класс сматпоинтера, чтобы в использовании он вёл себя как сгенерённый импортом?
т.е. например
CMyComPtr<CCoClass> ptr;
// здесь кинется исключение, т.к. метод возвратит E_FAIL, а поинтер это поймёт и кинет исключение?
ptr->ReturnEFail();
если ты это имел ввиду, то подскажи плз идею как это сделать.
ведь если пергружать оператор -> то в нём мы возвращаем raw-указатель а как нам после этого получить возвращённый HRESULT...
немного я над этим подумал пока на работу ехал... никаких идей не пришло
I>гораздо более простой и работающий способ — присваивать результат вызова метода экземпляру специального класса, который проверит HRESULT и кинет исключение для FAILED(HRESULT)
это мне не подходит
т.к. код будет примерно следующий
// может произойти исключение
ptrToExternalComObject->Method();
// чтобы оно в случае неудачи кинулось и здесь, надо писать так
EnsureSuccess(ptrToInternalObject->Method());
т.е. постоянно надо думать — какой объект мы используем, если внутренний то ещё парится и писать EnsureSuccess...
вобщем баги такое любят...
Директива #import генерирует обертки вокруг интерфейсов, внутри методов оберток всеравно происходит проверка HRESULT.
Как-то мне лень было везде писать EnsureSuccess(ptrToInternalObject->Method()) и я написал обертку для HRESULT, переопределил метод присваивания и в нем проверял присваеваемое значение и выкидывал exception в случае ошибки. Далее создавал переменную CMyHresult hr и если какой-то метод интерфейса возвращает значение типа HRESULT я просто присваиваю его переменной.
Здравствуйте, TheThief, Вы писали:
TT>Директива #import генерирует обертки вокруг интерфейсов, внутри методов оберток всеравно происходит проверка HRESULT.
Здравствуйте, shurik., Вы писали:
S>Здравствуйте, Ivan, Вы писали:
I>>Здравствуйте, shurik., Вы писали:
S>вот это идея, спасибо. Как-нибудь обязательно попробую.
интересно, как у тебя получается сейчас обойтись без этого
ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?
S>это мне не подходит S>т.к. код будет примерно следующий S>
S>// может произойти исключение
S>ptrToExternalComObject->Method();
S>// чтобы оно в случае неудачи кинулось и здесь, надо писать так
S>EnsureSuccess(ptrToInternalObject->Method());
S>
я имел в виду не вызов функции EnsureSuccess, а использование оператора присваивания (см. соседний пост) — код получается короче и стройнее
S>т.е. постоянно надо думать — какой объект мы используем, если внутренний то ещё парится и писать EnsureSuccess... S>вобщем баги такое любят...
полностью согласен. нужно либо использовать CComPtr (опция raw_interfaces_only для import), либо _com_ptr_t, но смешивать их — ни к чему.
Здравствуйте, Ivan, Вы писали:
I>интересно, как у тебя получается сейчас обойтись без этого I>ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?
а я просто создаю не CoCreateInstance а так
CComPtr<CCoClass> ptr = new CComObject<CCoClass>
так что ничего кастить не надо 8)
I>я имел в виду не вызов функции EnsureSuccess, а использование оператора присваивания (см. соседний пост) — код получается короче и стройнее
см. мой ответ на прошлый пост 8)
хочу без дополнительных переменных...
хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)
S>хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)
#import — это не смарт поинтер
простым смартпоинтером — не обойтись
тебе необходимо грузить библиотеку типов, и делать врапперы для интерфейсов, для которых уже и использовать _com_ptr
Здравствуйте, shurik., Вы писали:
S>есть такая проблема S>как известно, директива #import генерит совсем умные смарт поинтеры, которые проверяют возвращаемое значение и в случае чего кидают екзепшн.
S>в моём ком-сервера активно используются внутренние (реализованные в этом же ком-сервера) коклассы. S>соответсвенно для них тоже хочется смартпоинтеры, причём эти смарт поинтеры должны принимать в параметр шаблона не интерфейс и именно тип класса (чтобы использовать public-методы не описанные в интерфейсе)
S>попробовал CComPtr S>возникло две проблемы: S>1) оператор присвания отказывается работать (что-то типа ambigous conversion to IUnknown*, только на кой они конвертят тип шаблона к IUnknown я так и не понял). Но это проблема решаема. S>2) естественно возвращаемые значения он не проверят, т.е. при использовании внутренних классов приходится оборачивать вызов метода в функцию которая проверяет возвращаемое значение. Такой подход мне не нравится. Хочется единообразия работы с внешними и внутренними коклассами.
S>основная проблема под номером 2. S>Наверняка ведь кто-то сталкивался с подобной задачей? S>может возможно написать свой смарт поинтер и как-нибудь хитро заставить его его получать обратно управление после вызова оператора '->', что бы проверить что же там вернул метод? или может такой уже есть готовый?
По моему опыту, я использую CoCreateinstance внутри сервера только для синхронизации вызова методов объектов через границы апартментов. А так стараюсь не обрезать себе функциональность методами интерфейса, а создаю полноценные классы типа CComObject<CMyClass>.
А чтобы решить вторую проблему можно создать производный класс
class
CMyClass : CComObject<CMyClass>
и переопределить ту же виртуальную функцию, только с нужной проверкой
class CMyClass : CComObject<CMyClass>
{
public STDMETHOD (Method) (...)
{
HRESULT hr = CMyClass::Metod (...)
if FAILED (hr)
...
return HR;
}
}
Здравствуйте, shurik., Вы писали:
S>Здравствуйте, Ivan, Вы писали:
I>>интересно, как у тебя получается сейчас обойтись без этого I>>ты же не используешь dynamic_cast, чтобы получить указатель на класс по указателю на интерфейс ?
S>а я просто создаю не CoCreateInstance а так S>
S>CComPtr<CCoClass> ptr = new CComObject<CCoClass>
S>
S>так что ничего кастить не надо 8)
этого мало допустим, что компонент CCoClass поддерживает интерфейс ICoClass, тогда имея указатель на ICoClass* получить по нему CCoClass* уже нельзя, без dynamic_cast'а. т.е. такой код не сработает
компилятор прав, так как класс CAnohterInternalCoClass нельзя однозначно преобразовать к IUnknown*, правильно получать нужный указатель в том числе и на CAnohterInternalCoClass нужно с помощью QI — см. выше функцию get_raw_ptr
S>хочу без дополнительных переменных... S>хочу чтоб мой смартпоинтер делал как делает #import (без raw_interfaces_only)
тогда лучше всего было бы использовать импорт собственной библиотеки типов, т.к. добиться такого же поведения, которое обеспечивают стандартные врапперы будет проблематично — помимо проверки HRESULT врапперы выполняют часто преобразование типов параметров — BSTR<->_bstr_t и т.п. Могут быть специфические ошибки, связанные с тем, что в одном случае вызов через _com_ptr_t имеет дело с врапперами _variant_t, _bstr_t, а вызов через твой смартпойнтер — с BTSR и VARIANT
и по поводу смартпойнтера CVerySmartPtr — > суть в том что деструктор CDummy вызывается после отработки метода Method
как бы теперь получить HRESULT...
получить можно "нечестным" способом — возвращаемое значение будет находиться в регистре eax. но
— этот код не может работать гарантированно для разных версий компилятора и флажков компиляции
— нет нормального способа проверить тип возвращаемого значения. если метод вернет не HRESULT, а ULONG (AddRef) или void — с проверкой eax может получиться очень нехорошо.
ИМХО, два лучших варианта такие:
— везде использовать _com_ptr_t — импортировать свою библиотеку типов
— везде использовать CComPtr и класс для ловли ислкючений, который будет кидать его в операторе присваивания (кстати,в этом случае ты можешь контролировать — нужно ли кидать исключение или нет)
I>как бы теперь получить HRESULT...
I>получить можно "нечестным" способом — возвращаемое значение будет находиться в регистре eax. но I>- этот код не может работать гарантированно для разных версий компилятора и флажков компиляции I>- нет нормального способа проверить тип возвращаемого значения. если метод вернет не HRESULT, а ULONG (AddRef) или void — с проверкой eax может получиться очень нехорошо.
я тоже думал так сделать (верней просто попробовать, ессно такой бы код я не оставил)
однако деструктор CDummy поганит в Debug'е eax...
I>ИМХО, два лучших варианта такие: I>- везде использовать _com_ptr_t — импортировать свою библиотеку типов I>- везде использовать CComPtr и класс для ловли ислкючений, который будет кидать его в операторе присваивания (кстати,в этом случае ты можешь контролировать — нужно ли кидать исключение или нет)
Здравствуйте, shurik., Вы писали:
S>я тоже думал так сделать (верней просто попробовать, ессно такой бы код я не оставил) S>однако деструктор CDummy поганит в Debug'е eax...
в образовательных целях можно сделать деструктор CDummy — naked и написать код вручную:
Здравствуйте, shurik., Вы писали:
S>круто! S>очень круто, спасибо! S>только в релизе не работает 8)
а что именно не работает ?
в приведенном выше коде есть пара багов:
— правильно __LOCAL_SIZE, а не LOCAL_SIZE
— в зависимости от реализации функции check может потребоваться сохранять значения некоторых регистров. есть соглашение о том, значения каких регистров должны оставаться неизменными после вызова функции (ebp, esi,edi и т.п. — точный список не скажу)
Здравствуйте, Ivan, Вы писали:
I>Здравствуйте, shurik., Вы писали:
S>>круто! S>>очень круто, спасибо! S>>только в релизе не работает 8)
I> а что именно не работает ? I>в приведенном выше коде есть пара багов: I>- правильно __LOCAL_SIZE, а не LOCAL_SIZE
не мог бы рассказать что значит __LOCAL_SIZE?
I>- в зависимости от реализации функции check может потребоваться сохранять значения некоторых регистров. есть соглашение о том, значения каких регистров должны оставаться неизменными после вызова функции (ebp, esi,edi и т.п. — точный список не скажу)
давно хотел с этим поподробней познакомиться...
не подскажешь где про это почитать можно? (в смысле по каким ключевым словам погуглить можно)
Здравствуйте, Ivan, Вы писали:
I>Здравствуйте, shurik., Вы писали:
S>>круто! S>>очень круто, спасибо! S>>только в релизе не работает 8)
I> а что именно не работает ?
разобрался почему не работает...
Method был реализован в описании класса...
т.е. инлайновский был
вынес реализации Method в срр файл и всё сразу стало круто.