Реализация COM совместимой коллекции на С++
Как он реализуется более чем подробно описано в "Inside Com" Дейла Роджерсона и вопросов не вызывает. Но у него нет ни слова о том, какие ограничения накладываются на выгружаемые данные, структуры.
А они точно накладываются, в каком-нибудь VBA нельзя принимать указатели, где-то может не быть нужного типа данных и т.п.. Я уверен, что есть конкретные требования к выгружаемой коллекции. Из разбросанных статей в интернете я понял, что это должен быть массив и у него должны быть определены ряд команд доступа к его элементам. Но найденные мною требования явно не полные. И ни один из них не для С++.
Еси кто-нибудь имеет ссылку или знает их, помогите пожалуйста!
Как он реализуется более чем подробно описано в "Inside Com" Дейла Роджерсона и вопросов не вызывает. Но у него нет ни слова о том, какие ограничения накладываются на выгружаемые данные, структуры.
А они точно накладываются, в каком-нибудь VBA нельзя принимать указатели, где-то может не быть нужного типа данных и т.п.. Я уверен, что есть конкретные требования к выгружаемой коллекции. Из разбросанных статей в интернете я понял, что это должен быть массив и у него должны быть определены ряд команд доступа к его элементам. Но найденные мною требования явно не полные. И ни один из них не для С++.
Еси кто-нибудь имеет ссылку или знает их, помогите пожалуйста!
Христос Воскрес!
Вопрос свой уточните пожалуйста. СОМ-интерфейс - это СОМ-интерфейс и причем здесь С++?
Данные передаються либо через массивы Variant(TVariant в ВСВ), либо
через BSTR(ближайшим аналогом является в билдере WideString).
По данной ссылке (http://www.rsdn.ru/article/com/QnAinoutbstr.xml) можно так же посмотреть.
У меня ещё вопрос. Дейл Роджерсон в книге дает анализ, когда лучше использовать обычные интерфейсы, диспетчеризацию или дуальные. И рекомендует использовать обычные!!
При этом про передачу данных с использованием BSTR, SafeArray и Variant говорит только рассказывая про IDispatch, т.е. про диспетчеризацию.
Отсюда у меня глубокое непонимание, обязан ли я их использовать просто реализовав IUnknown и используя обычный массив указателей на виртуальные функции, в качестве COM-интерфейса?
Дело в том, что как он говорит, диспетчер, в случае если компонент загружается в адресное пространство клиентского процесса, работает в 100 раз медленнее чем vtbl (массив указателей на виртуальные функции), но вообще ни слова о том, какие есть правила выгрузки данных через такие интерфейсы, о выгрузке говорится только в рамках диспетчеризации. И у меня есть подозрение, что использование Variant как раз является замедляющим диспетчера фактором! Т.е. возможно если использовать Variant в vtbl - я получу замедление как у диспетчера, и потому люди используют что-то ещё? Или нет? :)
Помогите пожалуйста! =)
У меня ещё вопрос. Дейл Роджерсон в книге дает анализ, когда лучше использовать обычные интерфейсы, диспетчеризацию или дуальные. И рекомендует использовать обычные!!
При этом про передачу данных с использованием BSTR, SafeArray и Variant говорит только рассказывая про IDispatch, т.е. про диспетчеризацию.
Отсюда у меня глубокое непонимание, обязан ли я их использовать просто реализовав IUnknown и используя обычный массив указателей на виртуальные функции, в качестве COM-интерфейса?
Дело в том, что как он говорит, диспетчер, в случае если компонент загружается в адресное пространство клиентского процесса, работает в 100 раз медленнее чем vtbl (массив указателей на виртуальные функции), но вообще ни слова о том, какие есть правила выгрузки данных через такие интерфейсы, о выгрузке говорится только в рамках диспетчеризации. И у меня есть подозрение, что использование Variant как раз является замедляющим диспетчера фактором! Т.е. возможно если использовать Variant в vtbl - я получу замедление как у диспетчера, и потому люди используют что-то ещё? Или нет? :)
Помогите пожалуйста! =)
Христос Воскрес!
Вобще-то, все зависит от задач и целей которые при это необходимо достигнуть.
В целом вопрос выглядит так: "Что лучше, маленький кооператив, который размещаеться в гараже, или большое предприятие, на сотне гектаров" :)
Естественно диспечер добавляет дополнительный слой - и за это надо платить дополнительными накладными расходами. Нужно ли это, или вполне можно обойтись - собственно вам и решать.
Вобще-то, все зависит от задач и целей которые при это необходимо достигнуть.
В целом вопрос выглядит так: "Что лучше, маленький кооператив, который размещаеться в гараже, или большое предприятие, на сотне гектаров" :)
Естественно диспечер добавляет дополнительный слой - и за это надо платить дополнительными накладными расходами. Нужно ли это, или вполне можно обойтись - собственно вам и решать.
Я немного не о том. На примере покажу.
{
//PPCA and PSTK management
virtual HRESULT UPregp(ULONG* res, BSTR* TaskName) = 0;
virtual HRESULT UPunregp(ULONG res, BSTR TaskName) = 0;
virtual HRESULT UPregt(Variant* res, SafeArray* Data) = 0;
virtual HRESULT UPunregt(Variant res, SafeArray Data) = 0;
}
Моя DLL выгружает и регистрирует такой интерфейс. Вопрос, сможет ли использовать этот интерфейс Visual Basic? Теоретически должен, потому что ULONG, int и т.п. - это члены Variant и VBA должен уметь с ними работать.
А вот догадается ли он преобразовывать указатели (у него же их нет)? Какие из перечисленных функций интерфейса, VB сможет использовать?
У вас слегка отвлеченное понятие о VB.
Аналогом в VB (не ручаюсь за точность - все таки достаточно редко использую)
гдето так примерно.
У вас слегка отвлеченное понятие о VB.
Аналогом в VB (не ручаюсь за точность - все таки достаточно редко использую)
гдето так примерно.
Да, я с VB практически не знаком. Правильно ли я понимаю, что этот ответ можно понимать как "да, каждый из перечисленных методов интерфейса, после выгрузки компонента в VB, сможет использоваться и не надо думать, что VB такой тупой, он всё это поймет"?
Правильно.
Отлично. Ещё, для работы с BSTR Майкрософт разработал обертку bstr_t, которая скрывает проблемы с памятью и чтением, которые есть у BSTR (связаны с возможным появением нулей в середине строки).
Но при этом я нигде не видел, чтобы говорилось, что COM поддерживает bstr_t. У меня очень отвлеченное понимание, что такое bstr_t, при этом авторы статей не уточняют, если я через интерфейс буду передавать не BSTR, а bstr_t, интерфейс так же будет всё понимать?
Или грамотным решением является передавать BSTR, а потом тут же прятать его в bstr_t и работать уже с оберткой, а перед выходом - конвертировать назад в BSTR?
Как-то сложно всё получается :)
СОМ не поддерживает bstr_t - вы можете передавать тип bstr_t там где требуется BSTR, и быть уверенными что вы получите ожидаемый результат, но если вас беспокоит переносимость методов - то вы должны объявлять их используя только BSTR.
СОМ не поддерживает bstr_t - вы можете передавать тип bstr_t там где требуется BSTR, и быть уверенными что вы получите ожидаемый результат, но если вас беспокоит переносимость методов - то вы должны объявлять их используя только BSTR.
Спасибо!
Но теперь у меня другая засада (как-то жутко невезет с этими bstr):
Выдается ошибка линкера, если нет
#include "comdef.h"
#pragma comment(lib, "comsupp.lib")
Как утверждает интернет, но при этом гугл и яндекс отказываются мне помогать найти эту либу, а на моей машине её, как показывает search по всем дискам, нет =(
И где её в итоге брать не понятно -(
Как в такой ситуации ведут себя старые, много повидавшие программисты? :)
1) выяснил, что такая либа есть в Visual Studio, поставил её, нашел там действительно comsupp.lib.
2) Вставил в проект. Облом. Оказывается эта либа имеет тип(?) COFF, а должна OMF. Интернет подсказал, что Builder имеет утилиту coff2omf для исправления этой проблемы.
3) В результате вызвал утилиту без параметров (так как параметры какие-то очень странные там) и получил из файла в 185kb файлик на 512b. Который якобы OMF.
4) Вставил в проект - облом, он там вообще не видит реализации этих функций и опять вылетает старая пара linker error. Новая либа полностью игнорируется, хотя на COFF Билдер не ругается с нею (и то хорошо). Если вставляю старую и новую либу вместе - старая ведет себя как без новой, та же ошибка с COFF (что это такое я не знаю, поэтому пробовал по-разному).
В итоге, что я делал не так? Почему человек, просто желающий иметь возможность нормально работать с BSTR в своем проекте, который не может без BSTR ввиду необхдимости соответствовать СОМ-интерфейсу, должен ТАК мучиться?
Неужели я один такой, кто пытается использовать bstr_t или variant_t?
А если другие пользуются - они тоже эти круги ада проходят? =(
...
4) Вставил в проект - облом, он там вообще не видит реализации этих функций и опять вылетает старая пара linker error. Новая либа полностью игнорируется, хотя на COFF Билдер не ругается с нею (и то хорошо). Если вставляю старую и новую либу вместе - старая ведет себя как без новой, та же ошибка с COFF (что это такое я не знаю, поэтому пробовал по-разному).
эта утилита не работает со статическими либами...
В итоге, что я делал не так? Почему человек, просто желающий иметь возможность нормально работать с BSTR в своем проекте, который не может без BSTR ввиду необхдимости соответствовать СОМ-интерфейсу, должен ТАК мучиться?
Неужели я один такой, кто пытается использовать bstr_t или variant_t?
А если другие пользуются - они тоже эти круги ада проходят? =(
Другие обходятся WideString, а кто совсем мучиться не желает обходятся ATL(и ее обертками для BSTR).
Как же быть? Выходит на Билдере никто никогда не делает комовские сервера? Даже простенькие внутрипроцессовые? :(
Вообще у меня код более менее голый (чуть-чуть Вин Апи и остальное чистый С++), Вижуал Студию я установил, считаете, что бысрее я его перенесу туда, разбирусь в Студии (я её не видел 7 лет), чем добьюсь корректной работы BSTR в Билдере?
И где её в итоге брать не понятно -(
Правильно говорит. Передавать надо BSTR, но естественное желение потом "красиво" работать с ним в теле функции или я че-то не понимаю тогда про попытки использования bstr_t?
Как же быть? Выходит на Билдере никто никогда не делает комовские сервера? Даже простенькие внутрипроцессовые? :(
Я делаю.
Вижуал Студию я установил, считаете, что бысрее я его перенесу туда, разбирусь в Студии (я её не видел 7 лет),
Зачем? ATL есть в Билдере.
чем добьюсь корректной работы BSTR в Билдере?
я за 15 минут добился корректной работы _bstr_t (включая поиск, закачку, установку и ответ Вам) , а BSTR итак корректно работает
Отличная ссылка, там куча файлов по кому и 4ре либы на нужную мне тему:
comsupp.lib
comsuppd.lib
comsuppw.lib
comsuppwd.lib
Первую я вставил в проект и линкер ошибки больше нет!! УРА!
Итого: обертка для корректного использования BSTR внутри компоненты у меня есть, GIZMO - огромное спасибо!
Но теперь проблема прохода данных через интерфейс, потому что это должен быть только BSTR, и при этом он не функционирует, как должен, это описано в теме "Работа с BSTR" =(
Так что пока результат один - никакого результата всё равно =(
я за 15 минут добился корректной работы _bstr_t (включая поиск, закачку, установку и ответ Вам) , а BSTR итак корректно работает
=) Это потому, что Вы знали, как правильно двигаться, а я вчера танцевал с бубном, что описано выше))
Вот есть код
mbstowcs(wc, "alba", 4);
BSTR bwc = SysAllocString(wc);
Я создал массив wchar-ов длиною 4.
Положил в него слово "alba" длиною 4.
Создал переменную BSTR bwc, используя специальную для этого функцию, положив туда свою переменную alba.
Слово, которое лежит в bwc - "al", длиною 2.
Мне надо добиться того, чтобы BSTR содержал то, что мне надо (в данном примере целиком слово "alba"). Тогда я смогу передавать через интерфейс данные и быть уверенным, что они передаются целиком, а не первые 2 символа...
Может быть правильным решением является самому составить BSTR? Выделить память, записать первые 4ре байта - длину, потом строку, и возвращать указатель на начало строки? Просто насколько я понимаю, SysAllocString для этого и сделан...
GIZMO - огромное спасибо!
Спасибо (на самом деле) Darko Miletic, а здесь Спасибо GIZMO
Мне надо добиться того, чтобы BSTR содержал то, что мне надо (в данном примере целиком слово "alba").
BSTR ws = SysAllocString(L"alba") ?
Да, и это:
и это
mbstowcs(wc, "alba", 4);
BSTR bwc = SysAllocString(wc);
дает на выходе "al". Если я ставлю входящее слово длинною в 8 или 6 символов, всё равно длина выходящего тут будет 2 символа. Первые два из вставляемых. =(
Может у меня Билдер кривой?
ЗЫ: сайт запрещает давать отзыв дважды)
дает на выходе "al". Если я ставлю входящее слово длинною в 8 или 6 символов, всё равно длина выходящего тут будет 2 символа. Первые два из вставляемых. =(
Может у меня Билдер кривой?
Покажи код как ты передаешь строку и для какого метода
ЗЫ: Попробуй передай &WideString("alba") это один фиг развернется в BSTR*
ЗЫ: Попробуй передай &WideString("alba") это один фиг развернется в BSTR*
Если передавать &WideString("alba"), то это будет указатель на указатель, и он ругается.
Ниже три варианта и три попытки вызова функции интерфейса.
{
wchar_t wc[4];
mbstowcs(wc, "alba", 4);
BSTR bwc = SysAllocString(wc);
BSTR bwc2 = SysAllocString(L"alba");
BSTR bwc3 = SysAllocString(WideString("alba"));
I_Face->UPregp(bwc);
I_Face->UPregp(bwc2);
I_Face->UPregp(bwc3);
Как оказалось, идёт правильное выполнение всех трёх =((( Просто подсветка Билдера показывает, будто в слове 2 элемента. А на самом деле слово "alba" передавалось, и видимо давно. А то, что внутри компонента длина слова несовпадала, скорее всего моя невнимательность, там раньше я SysStringLen вызывал от bstr_t, похоже.... не факт, но хочу верить, что это было и именно это причина.
Правда теперь нет уверенности, что оно так всегда будет себя вести, но желаемый результат получен! Ура!