Проблемы с передачей SAFEARRAY через Dispatch
От: LoneTiger  
Дата: 23.08.01 12:06
Оценка:
Всем привет!

Столкнулся со следующей проблемой: мне нужно передать указатель на out-параметр SAFEARRAY через Invoke. Я делаю это так:

--- IDL:

[id(6), helpstring("method OnRenderZBuffer")] HRESULT OnRenderZBuffer([in] SAFEARRAY(BYTE)* saRenderParams, [in] SAFEARRAY(BYTE)* saInZBuffer, [out] SAFEARRAY(BYTE)* psaZBuffer );

--- C++:

HRESULT Fire_OnRenderZBuffer(DWORD dwCookie, SAFEARRAY **psaRenderParams, SAFEARRAY **psaInZBuffer, SAFEARRAY **psaZBuffer)
{
CComVariant* pvars = new CComVariant[3];
if (pDispatch != NULL)
{
VariantClear(&varResult);
pvars[2].vt = VT_ARRAY|VT_UI1;
pvars[2].parray = psaRenderParams;
pvars[1].vt = VT_ARRAY|VT_UI1;
pvars[1].parray = psaInZBuffer;
pvars[0].vt = VT_ARRAY|VT_UI1;
pvars[0].parray = psaZBuffer;
DISPPARAMS disp = { pvars, NULL, 3, 0 };
HRESULT hr = pDispatch->Invoke(0x6, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp,
&varResult, NULL, NULL);
}
}

--------------------

Это не работает (hr содержит код ошибки Incorrect argument). Насколько я понимаю, аргументы SAFEARRAY всегда передаются, как указатели на указатели, независимо от того, in или out?

Кто-нибудь может подсказать, как правильно передавать SAFEARRAY через Invoke?
Re: Проблемы с передачей SAFEARRAY через Dispatch
От: OlegO Россия http://www.mediachase.ru
Дата: 24.08.01 07:01
Оценка:
LT>Кто-нибудь может подсказать, как правильно передавать SAFEARRAY через Invoke?

В VARIANTE, а именно

[id(6), helpstring("method OnRenderZBuffer")] HRESULT OnRenderZBuffer([in] VARIANT saRenderParams, [in] VARIANT saInZBuffer, [out] VARIANT* psaZBuffer );
С уважением, OlegO.
Re: Проблемы с передачей SAFEARRAY через Dispatch
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.08.01 20:57
Оценка:
Здравствуйте LoneTiger, вы писали:


1. у Вас не правильно описаны параметры!

LT>[id(6), helpstring("method OnRenderZBuffer")] HRESULT OnRenderZBuffer([in] SAFEARRAY(BYTE)* saRenderParams, [in] SAFEARRAY(BYTE)* saInZBuffer, [out] SAFEARRAY(BYTE)* psaZBuffer );


[in]-параметрам не надо добавлять *.

2. В IDispatch нет понятия [out]. Есть [out, retval] и тогда результат надо получать и передовать через varResult (в Вашем случае). И есть ByRef. Лучше даже описывать параметр не как [out], а как [in, out].

В коде вызова для [out]-параметра надо указывать:

vars[0].vt = VT_BYREF | VT_ARRAY | VT_UI1;
vars[0].pparray = &psaZBuffer;

Точно не помню, pparray может называться по другому.

3. В примере есть одно излишиство:

CComVariant* pvars = new CComVariant[3];

намного проще так:

CComVariant vars[3];


LT>Это не работает (hr содержит код ошибки Incorrect argument). Насколько я понимаю, аргументы SAFEARRAY всегда передаются, как указатели на указатели, независимо от того, in или out?


Нет, это не так. out всегда прибавляет лишную звездочку — это соглашение COM-а.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Проблемы с передачей SAFEARRAY через Dispatch
От: LoneTiger  
Дата: 26.08.01 16:15
Оценка:
Здравствуйте VladD2, вы писали:

Спасибо всем за ответы, сделал через VARIANT, и все заработало.

VD>[in]-параметрам не надо добавлять *.


Это уже своего рода "шаманство", перед этим был вариант без указателей...

VD>2. В IDispatch нет понятия [out]. Есть [out, retval] и тогда результат надо получать и передовать через varResult (в Вашем случае). И есть ByRef. Лучше даже описывать параметр не как [out], а как [in, out].


Имхо, это не совсем так. Я достаточно часто использую просто [out], и это замечательно работает. Кстати, в моем случае с VARIANTами используется именно [out]. И это работает.

VD>В коде вызова для [out]-параметра надо указывать:

VD>vars[0].vt = VT_BYREF | VT_ARRAY | VT_UI1;
VD>vars[0].pparray = &psaZBuffer;

Делал и так, не помогало...

VD>3. В примере есть одно излишиство:

VD>CComVariant* pvars = new CComVariant[3];
VD>намного проще так:
VD>CComVariant vars[3];

Согласен, но это код, сгенерированный wizard-ом, я вносил только необходимые исправления.
Re[3]: Проблемы с передачей SAFEARRAY через Dispatch
От: VladD2 Российская Империя www.nemerle.org
Дата: 27.08.01 20:48
Оценка:
Здравствуйте LoneTiger, вы писали:

VD>>2. В IDispatch нет понятия [out]. Есть [out, retval] и тогда результат надо получать и передовать через varResult (в Вашем случае). И есть ByRef. Лучше даже описывать параметр не как [out], а как [in, out].


LT>Имхо, это не совсем так. Я достаточно часто использую просто [out], и это замечательно работает. Кстати, в моем случае с VARIANTами используется именно [out]. И это работает.


Не, ну если надо не проблемы решать, а по дискутировать, то конечно... Вот только мне что-то спорить не охота. :( Эти грабли не у Вас первого не у Вас последнего. По-моему, в MS-ной русскоязычной конфе по VC я как-то уже правил подобный код. Повторю еще раз, но это последний.
В IDISPATCH нет понятия чистого [out]-а! Этот интерфейс создавался для VB старых версий, а в нем вообще нет понятия out-параметров. Я ясно излагаю? В dual-интерфейсе Вы можете описать патаметр как чистый out, но при вызове через его IDispatch-часть Вам все равно прийдется передавать параметы ByRef. Ну, вот так криво написан этот IDispatch! У вас могут быть и другие проблемы с out-параметрами при использовании в VB. Вот я Вам и советую описать параметр как [in, out]. Содя по отвту на последнее замечание Вы не просто описываете онтерфейс, а интерфейс событийный. Верно? Ну, вроде точно верно. А если так, то вы делаете еще более грубую ошибку описывая событийный интерфейс как dual! Это еще одни грабли на которые лучше не наступать. Просто запомните: Если Вы не хотите иметь проблем, не реализуйте событийный интерйейс как dual!
Реализуйте его или как чистый dispinterface или как чистый custom-интерфенйс (производный тлько от IUnknown). Перый, вариант нужен для совметимости с VB версии ниже 6-й и скриптовыми языками. Второй, если используете преимущественно C++ и потенциально VB6 (это так же значительно быстрее). Ну, а елси охота походить по граблям... ;o)

VD>>В коде вызова для [out]-параметра надо указывать:

VD>>vars[0].vt = VT_BYREF | VT_ARRAY | VT_UI1;
VD>>vars[0].pparray = &psaZBuffer;

LT>Делал и так, не помогало...


Значит ощибка гдето еще или пробавали не совсем так. Кадайте код погляжу. audit@optim.ru. Кстати, а на чем реализуются события? VB6?

VD>>3. В примере есть одно излишиство:

VD>>CComVariant* pvars = new CComVariant[3];
VD>>намного проще так:
VD>>CComVariant vars[3];

LT>Согласен, но это код, сгенерированный wizard-ом, я вносил только необходимые исправления.



Да, видимо люди писавшие этот код или много выпили или писали его поздно ночью. :)
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: Проблемы с передачей SAFEARRAY через Dispatch
От: LoneTiger  
Дата: 28.08.01 08:16
Оценка:
Здравствуйте VladD2, вы писали:

VD>Не, ну если надо не проблемы решать, а по дискутировать, то конечно... Вот только мне что-то спорить не охота. :( Эти грабли не у Вас первого не у Вас последнего. По-моему, в MS-ной русскоязычной конфе по VC я как-то уже правил подобный код. Повторю еще раз, но это последний.


Ну не надо так нервничать :) Во-первых, это было имхо (я не претендую на истину в последней инстанции), во-вторых, мне интересно разобраться в этой ситуации. Поэтому любая информация по этому вопросу имеет для меня ценность. Дело в том, что я не использую VB, весь проект на VC. Поэтому я и заинтересовался — будет ли чистый [out] в IDispatch нормально работать в VC.

VD>В IDISPATCH нет понятия чистого [out]-а! Этот интерфейс создавался для VB старых версий, а в нем вообще нет понятия out-параметров. Я ясно излагаю? В dual-интерфейсе Вы можете описать патаметр как чистый out, но при вызове через его IDispatch-часть Вам все равно прийдется передавать параметы ByRef. Ну, вот так криво написан этот IDispatch! У вас могут быть и другие проблемы с out-параметрами при использовании в VB. Вот я Вам и советую описать параметр как [in, out]. Содя по отвту на последнее замечание Вы не просто описываете онтерфейс, а интерфейс событийный. Верно? Ну, вроде точно верно. А если так, то вы делаете еще более грубую ошибку описывая событийный интерфейс как dual! Это еще одни грабли на которые лучше не наступать. Просто запомните: Если Вы не хотите иметь проблем, не реализуйте событийный интерйейс как dual!


Угу, все понятно. Проблема в том, что реализация поддержки событий была сгенерирована тем же wizard-ом (тудыть его в качель!), поэтому грабли это не совсем мои :) Я вот одного не понимаю: зачем было делать такой кривой wizard? Если все на самом деле так, как Вы говорите, почему Microsoft до сих пор не пропатчила это дело? У меня стоит SP5, wizard там такой же кривой. В чем тут дело?

VD>Реализуйте его или как чистый dispinterface или как чистый custom-интерфенйс (производный тлько от IUnknown). Перый, вариант нужен для совметимости с VB версии ниже 6-й и скриптовыми языками. Второй, если используете преимущественно C++ и потенциально VB6 (это так же значительно быстрее). Ну, а елси охота походить по граблям... ;o)


Тогда я не совсем понимаю, как будет работать механизм IConnectionPoint. Ведь там все завязано именно на IDispatch, не так ли? Если мой Sink интерфейс будет наследовать только IUnknown, каким макаром будут выполняться вызовы его методов? Объясните, если не сложно.

VD>Значит ощибка гдето еще или пробавали не совсем так. Кадайте код погляжу. audit@optim.ru. Кстати, а на чем реализуются события? VB6?


Спасибо, но у меня уже все заработало. И клиент, и сервер написаны на VC6.

VD>Да, видимо люди писавшие этот код или много выпили или писали его поздно ночью. :)


Эх... мелкософт, одно слово :(
Re[5]: Проблемы с передачей SAFEARRAY через Dispatch
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.08.01 12:28
Оценка:
Здравствуйте LoneTiger, вы писали:

LT>Здравствуйте VladD2, вы писали:


LT>Ну не надо так нервничать :) Во-первых, это было имхо (я не претендую на истину в последней инстанции), во-вторых, мне интересно разобраться в этой ситуации. Поэтому любая информация по этому вопросу имеет для меня ценность. Дело в том, что я не использую VB, весь проект на VC. Поэтому я и заинтересовался — будет ли чистый [out] в IDispatch нормально работать в VC.


Мужик (ничего что на ты?) ты уже десятый кто на мойе памяти по этим граблям ходит! И половина не обяснения требует, а спорить лезит. Вот и злюсь. Ну, ладно... проехали. :)

LT>Угу, все понятно. Проблема в том, что реализация поддержки событий была сгенерирована тем же wizard-ом (тудыть его в качель!), поэтому грабли это не совсем мои :) Я вот одного не понимаю: зачем было делать такой кривой wizard? Если все на самом деле так, как Вы говорите, почему Microsoft до сих пор не пропатчила это дело? У меня стоит SP5, wizard там такой же кривой. В чем тут дело?


Да! Визард глючный. Вернее он (они) не контролирует, что а) нельзя делать дуальный интерфейс событийным, в) в дисп-интерфейсе нельзя объявлять парамтр как чистый out. Игнорируя это он просто генерирует глючный код (хотя может он бдет глючить и на [in, out]-параметы). :(

LT>Тогда я не совсем понимаю, как будет работать механизм IConnectionPoint. Ведь там все завязано именно на IDispatch, не так ли? Если мой Sink интерфейс будет наследовать только IUnknown, каким макаром будут выполняться вызовы его методов? Объясните, если не сложно.


Замечательно будет работать. IDispatch — там совсем не нужен. VB (а уж на сях сам бог велел) прекрасно работает с кастом-интерфейсами. Это кстати раз в десять быстрей. Да, и на C++ проще с нормальными интерфейсами. Если мне не изменяет мой склероз, визард даже с ними работать умеет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: Проблемы с передачей SAFEARRAY через Dispatch
От: LoneTiger  
Дата: 28.08.01 12:49
Оценка:
Здравствуйте VladD2, вы писали:

VD>Мужик (ничего что на ты?) ты уже десятый кто на мойе памяти по этим граблям ходит! И половина не обяснения требует, а спорить лезит. Вот и злюсь. Ну, ладно... проехали. :)


ОК, на ты так на ты :)))

VD>Да! Визард глючный. Вернее он (они) не контролирует, что а) нельзя делать дуальный интерфейс событийным, в) в дисп-интерфейсе нельзя объявлять парамтр как чистый out. Игнорируя это он просто генерирует глючный код (хотя может он бдет глючить и на [in, out]-параметы). :(


Тогда вопрос: а где бы про все эти хитрости почитать? Я вот, например, не сталкивался нигде с упоминанием того факта, что в IDispatch нельзя использовать out. Откуда эта информация?

VD>Замечательно будет работать. IDispatch — там совсем не нужен. VB (а уж на сях сам бог велел) прекрасно работает с кастом-интерфейсами. Это кстати раз в десять быстрей. Да, и на C++ проще с нормальными интерфейсами. Если мне не изменяет мой склероз, визард даже с ними работать умеет.


Да я просил объяснить, КАК это будет работать. Как это работает с IDispatch, я понимаю. Как это будет работать в случае обычного недуального интерфейса, я, честно говоря, не представляю. Можно хотя бы ссылочку какую-нибудь?
Re[7]: Проблемы с передачей SAFEARRAY через Dispatch
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.08.01 15:16
Оценка:
Здравствуйте LoneTiger, вы писали:

LT>Тогда вопрос: а где бы про все эти хитрости почитать? Я вот, например, не сталкивался нигде с упоминанием того факта, что в IDispatch нельзя использовать out. Откуда эта информация?


Ну, кое что есть у нас на сайте www.optim.ru/sc , а можно и здесь. Основной материал в MSDN (но мало и найти тяжело), Iinside OLE помогает... но главное это хорошенько потрахаться. Обычно достаточно года 3-4. :( Особо помогает трах с VB (5 — 6).

LT>Да я просил объяснить, КАК это будет работать. Как это работает с IDispatch, я понимаю. Как это будет работать в случае обычного недуального интерфейса, я, честно говоря, не представляю. Можно хотя бы ссылочку какую-нибудь?


Дык я тебе и обяснил так же как с диспачем.

Вот примеры кода из нашего ascDB:

[
uuid(06E6E491-CCEF-11d3-AEA9-004095E1F072),
helpstring("ascColumn Class")
]
coclass ascColumn
{
[default] interface IascColumn;
[default, source] interface _IascColumnEvents;
};

[
object,
uuid(06E6E490-CCEF-11d3-AEA9-004095E1F072),
helpstring("_IascColumnEvents Interface"),
pointer_default(unique)
]
interface _IascColumnEvents : IUnknown
{
[id(acdName), helpstring("method AfterNameChanged")]
HRESULT AfterNameChanged([in] BSTR NewName);

[id(acdType), helpstring("method AfterTypeChanged")]
HRESULT AfterTypeChanged([in] ASC_COLUMN_DATA_TYPE NewType);

...
};

template <class T>
class CProxy_IascColumnsEvents : public IConnectionPointImpl<T, &IID__IascColumnsEvents, CComDynamicUnkArray>
{
//Warning this class may be recreated by the wizard.
public:
HRESULT Fire_AfterNameChanged(LONG ColumnNumber, BSTR NewName)
{
T* pT = static_cast<T*>(this);
if(pT->EventsFreezed())
return S_FALSE;

HRESULT ret = S_FALSE;
int nConnections = m_vec.GetSize();
for (int nConnectionIndex = 0; nConnectionIndex < nConnections && SUCCEEDED(ret); nConnectionIndex++){
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
_IascColumnsEvents* p_IascColumnsEvents = reinterpret_cast<_IascColumnsEvents*>(sp.p);
if (p_IascColumnsEvents != NULL)
ret = p_IascColumnsEvents->AfterNameChanged(ColumnNumber, NewName);
}
ASC_RETURN_FAILED(ret);

return S_OK;
}
HRESULT Fire_AfterTypeChanged(LONG ColumnNumber, ASC_COLUMN_DATA_TYPE NewType)
{
T* pT = static_cast<T*>(this);
if(pT->EventsFreezed())
return S_FALSE;

HRESULT ret = S_FALSE;
int nConnections = m_vec.GetSize();
for (int nConnectionIndex = 0; nConnectionIndex < nConnections && SUCCEEDED(ret); nConnectionIndex++){
pT->Lock();
CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
pT->Unlock();
_IascColumnsEvents* p_IascColumnsEvents = reinterpret_cast<_IascColumnsEvents*>(sp.p);
if (p_IascColumnsEvents != NULL)
ret = p_IascColumnsEvents->AfterTypeChanged(ColumnNumber, NewType);
}
ASC_RETURN_FAILED(ret);

return S_OK;
}
...
};

В классе к которому подключаются события добавляем:

BEGIN_CONNECTION_POINT_MAP(CascColumns)
CONNECTION_POINT_ENTRY(IID__IascColumnsEvents)
END_CONNECTION_POINT_MAP()

И наследуем его от:

CProxy_IascColumnsEvents


Клиент реализует интерфейс _IascColumnsEvents и подключается через Advise. Собственно все! И никакой разницы с диспачным вариантом. А вот с дуал-интерфейсом это прокатит только если и клиент и сервер написан на сях. VB реализует только диспачную часть и при вызове нормальной произойдет AV. Если же вызывать только диспачную часть, то нахреда делать сам интерфейс дуальным.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.