Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Best way to send Structs through COM interface?

512 views
Skip to first unread message

rajb...@community.nospam

unread,
Apr 23, 2009, 3:52:01 PM4/23/09
to
Hi,
I'm looking for the best way to send a group of STRUCTS through a COM
interface. I've created a COM server that serves requests from a product SDK.
The SDK needs privilege escalation, so this way the client can run as an
ordinary user on Vista.

Anyhow, I've looked up "passing custom data types" on the web and saw a
_very_ painful way to do it here:
http://www.codeproject.com/KB/atl/udtdemo.aspx
Frankly I have too many structs to do this, and they'll change periodically
and i don't want to re-write the interface.

I'm looking at some code examples where people use a SAFEARRAY and a
DISPPARAMS and memcpy the struct into, but it seems overly complex for me , i
don't need to use the Invoke() method.

can anyone suggest any alternatives? Are there parts of this puzzle that I
can safely cut out? I'm having a hard time understanding which parts do what.

Thank you.
--
Raj Banerjee, LogMeIn inc

Igor Tandetnik

unread,
Apr 23, 2009, 6:35:26 PM4/23/09
to
rajb...@community.nospam wrote:
> I'm looking for the best way to send a group of STRUCTS through a COM
> interface.

http://vcfaq.mvps.org/com/4.htm
http://vcfaq.mvps.org/com/5.htm

If you want simplicity and don't need to worry about clients written in
VB, go with the second one.

> Frankly I have too many structs to do this, and they'll change
> periodically and i don't want to re-write the interface.

Why do you feel you would need to rewrite the interface?
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925


Barzo

unread,
Apr 24, 2009, 8:44:31 AM4/24/09
to
> > I'm looking for the best way to send a group of STRUCTS through a COM
> > interface.
>
> http://vcfaq.mvps.org/com/4.htmhttp://vcfaq.mvps.org/com/5.htm

>
> If you want simplicity and don't need to worry about clients written in
> VB, go with the second one.

Hi, I'm in the same situation: I need to pass a struct to an event to
VB.
Visual Studio simply create this code:

Where T_ErrorInfo is:

typedef /* [helpstring][version][uuid] */ DECLSPEC_UUID
("BF5573C3-28DD-11DE-ABF5-005056C00008") struct T_ErrorInfo
{
long err_no;
BSTR description;
BSTR routine;
} T_ErrorInfo;


//------------------------------------
HRESULT Fire_OnError( T_ErrorInfo error_info )
{
..... code ....

CComVariant avarParams[1];
avarParams[0] = error_info;
CComVariant varResult;

DISPPARAMS params = { avarParams, NULL, 1, 0 };
hr = pConnection->Invoke(6,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&params,
&varResult,
NULL,
NULL);
}
//------------------------------------
but of course doesn't work because I cannot simply assign: avarParams
[0] = error_info;

I've changed this piece of code with:

//------------------------------------
HRESULT Fire_OnError( T_ErrorInfo error_info )
{
..... code ....

CComVariant avarParams[1];
IRecordInfo *pRI;
hr = GetRecordInfoFromGuids(&LIBID_AxEuroATLib,
1,
0,
LOCALE_USER_DEFAULT,
&__uuidof(T_ErrorInfo),
&pRI);
VariantInit(&avarParams[0]);
avarParams[0].vt = VT_RECORD;
avarParams[0].pvRecord = &error_info;
avarParams[0].pRecInfo = pRI;
pRI = NULL;

//CComVariant avarParams[1];
//avarParams[0] = error_info;
CComVariant varResult;

DISPPARAMS params = { avarParams, NULL, 1, 0 };
hr = pConnection->Invoke(6,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&params,
&varResult,
NULL,
NULL);
}
//------------------------------------

But I get an error:
'T_ErrorInfo' : no GUID has been associated with this object

on calling GetRecordInfoFromGuids.

Is my code right? Or, where is the mistake?

Thanks!
Daniele

Igor Tandetnik

unread,
Apr 24, 2009, 8:54:34 AM4/24/09
to
"Barzo" <dba...@gmail.com> wrote in message
news:f4282ee1-c38d-441d...@j18g2000yql.googlegroups.com

>>> I'm looking for the best way to send a group of STRUCTS through a
>>> COM interface.
>>
>> http://vcfaq.mvps.org/com/4.htmhttp://vcfaq.mvps.org/com/5.htm
>>
>> If you want simplicity and don't need to worry about clients written
>> in VB, go with the second one.
>
> Hi, I'm in the same situation: I need to pass a struct to an event to
> VB.
> Visual Studio simply create this code:

The wizard doesn't handle UDTs correctly. You will have to fix generated
code by hand.

Barzo

unread,
Apr 28, 2009, 11:02:03 AM4/28/09
to
On 24 Apr, 14:54, "Igor Tandetnik" <itandet...@mvps.org> wrote:
>
>
> > I need to pass a struct to an event to VB.
> > Visual Studio simply create this code:
>
> The wizard doesn't handle UDTs correctly. You will have to fix generated
> code by hand.

Ok, I dont' understand one crucial thing..

VB wants to pass UDT in events ByRef so in my IDL I put:

[id(1), helpstring("method OnError")] HRESULT OnError([in] T_ErrorInfo
* error_info);

So, if I think right, for any [in] params the caller (VB runtime) is
responsible to create and destroy the object..

In my C++ code I made this Connection Point implementation:

HRESULT Fire_OnError( T_ErrorInfo * error_info )
{
...

CComVariant avarParams[1];


VariantInit(&avarParams[0]);
avarParams[0].vt = VT_RECORD;

avarParams[0].pvRecord = error_info;

CComVariant varResult;

DISPPARAMS params = { avarParams, NULL, 1, 0 };

hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT,


DISPATCH_METHOD, &params, &varResult, NULL, NULL);

...
}

But, when I have to raise the event calling the Fire_OnError(), how
(and who) the struct is allocated?

In my c++ coclass I cannot do something like the following (I get a
VB6.EXE GPF) because the struct is destroyed, no?

{
T_ErrorInfo t_error_info;

t_error_info.description = _bstr_t( "DESC" );
t_error_info.routine = _bstr_t( "ROUTINE" );
t_error_info.err_no = 1000;

_ConnPointImpl.Fire_OnError( t_error_info );
}

Could someone give me some suggestions?
Thanks a lot!
Daniele.

Igor Tandetnik

unread,
Apr 28, 2009, 11:47:25 AM4/28/09
to
Barzo <dba...@gmail.com> wrote:
> Where T_ErrorInfo is:
>
> typedef /* [helpstring][version][uuid] */ DECLSPEC_UUID
> ("BF5573C3-28DD-11DE-ABF5-005056C00008") struct T_ErrorInfo
> {
> long err_no;
> BSTR description;
> BSTR routine;
> } T_ErrorInfo;
>
> But I get an error:
> 'T_ErrorInfo' : no GUID has been associated with this object

There are two symbols in this code - a struct name "struct T_ErrorInfo"
and a typedef name T_ErrorInfo. The GUID is associated with the former
but not with the latter. Hence the error.

To avoid this confusion, it's best not to use a typedef in your IDL.
There's a minor inconvenience of having to write "struct T_ErrorInfo"
everywhere in the IDL, but it's worth it.

Igor Tandetnik

unread,
Apr 28, 2009, 11:53:37 AM4/28/09
to
Barzo <dba...@gmail.com> wrote:
>>> I need to pass a struct to an event to VB.
>
> VB wants to pass UDT in events ByRef

I'm confused. First you say you (I presume, a C++ program of yours) want
to pass a UDT to VB, and now you say VB wants to pass UDT to you. Which
way is it?

> so in my IDL I put:
>
> [id(1), helpstring("method OnError")] HRESULT OnError([in] T_ErrorInfo
> * error_info);
>
> So, if I think right, for any [in] params the caller (VB runtime) is
> responsible to create and destroy the object..

Yes and no. It's true that an [in] parameter is allocated and freed by
the caller. But note that, when you fire an event, _you_ are the caller,
and event hanlder (presumably on VB side) is the callee.

> In my C++ code I made this Connection Point implementation:
>
> HRESULT Fire_OnError( T_ErrorInfo * error_info )
> }
>

> But, when I have to raise the event calling the Fire_OnError(), how
> (and who) the struct is allocated?

Just put an instance on the stack:

T_ErrorInfo error_info;
Fire_OnError(&error_info);

> {
> T_ErrorInfo t_error_info;
>
> t_error_info.description = _bstr_t( "DESC" );

At the semicolon, a temporary _bstr_t object is destroyed and
deallocates the BSTR. t_error_info.description ends up a dangling
reference.

> t_error_info.routine = _bstr_t( "ROUTINE" );
> t_error_info.err_no = 1000;
>
> _ConnPointImpl.Fire_OnError( t_error_info );

This shouldn't even compile. Fire_OnError takes a pointer to
T_ErrorInfo.

Barzo

unread,
Apr 28, 2009, 12:15:54 PM4/28/09
to
On 28 Apr, 17:53, "Igor Tandetnik" <itandet...@mvps.org> wrote:
> Barzo <dba...@gmail.com> wrote:
> >>> I need to pass a struct to an event to VB.
>
> > VB wants to pass UDT in events ByRef
>
> I'm confused. First you say you (I presume, a C++ program of yours) want
> to pass a UDT to VB, and now you say VB wants to pass UDT to you. Which
> way is it?

I didn't explain well because I'm confused too!!
But one step at time I'll see the light!!
Here, I mean simply that VB wants events parameters to be passed
ByRef..


> Yes and no. It's true that an [in] parameter is allocated and freed by
> the caller. But note that, when you fire an event, _you_ are the caller,
> and event hanlder (presumably on VB side) is the callee.

Oh! Here is my mistake!
Ok..so, I summarize my understanding..

1) VB wants an UDT parameters to be passed ByRef in the events so in
IDL I use :

[id(1), helpstring("method OnError")] HRESULT OnError([in] T_ErrorInfo
* error_info);


2) In the Connection Point implementation, can I pass the struct by
value to avoid problems?

HRESULT Fire_OnError( T_ErrorInfo error_info )


3) In the Fire_OnError body I wrote:

CComVariant avarParams[1];
IRecordInfo *pRI;

hr = GetRecordInfoFromGuids(LIBID_AxEuroATLib,
1,
0,
LOCALE_USER_DEFAULT,
T_SDSInfo_IID,
&pRI);


VariantInit(&avarParams[0]);
avarParams[0].vt = VT_RECORD;

avarParams[0].pvRecord = &sds_info;


avarParams[0].pRecInfo = pRI;
pRI = NULL;

That works, and in VB I receive the event but, when I access to the
UDT it crash:

Private Sub MyObj_OnError(error_info As AxEuroATLib.T_ErrorInfo)
Debug.Print "ERROR : " & error_info.Description <<<<<< CRASH!!!!
End Sub

I hope to be near the end!!
Thanks a lot for the support!!
Daniele.

Igor Tandetnik

unread,
Apr 28, 2009, 2:19:12 PM4/28/09
to
Barzo <dba...@gmail.com> wrote:
> 2) In the Connection Point implementation, can I pass the struct by
> value to avoid problems?
>
> HRESULT Fire_OnError( T_ErrorInfo error_info )

You can, but I don't quite see how it helps. Precisely what kind of
problems is this supposed to avoid?

> 3) In the Fire_OnError body I wrote:
>
> CComVariant avarParams[1];
> IRecordInfo *pRI;
>
> hr = GetRecordInfoFromGuids(LIBID_AxEuroATLib,
> 1,
> 0,
> LOCALE_USER_DEFAULT,
> T_SDSInfo_IID,
> &pRI);
> VariantInit(&avarParams[0]);
> avarParams[0].vt = VT_RECORD;
> avarParams[0].pvRecord = &sds_info;
> avarParams[0].pRecInfo = pRI;
> pRI = NULL;

Now I remember what the deal is with UDTs. When you pack a UDT into a
VARIANT, and later call VariantClear on it (which CComVariant's
destructor would do automatically in your case), VariantClear does three
things:

1. Calls IRecordInfo::RecordClear on your struct. This would deallocate
any fields that require deallocation (such as BSTR fields you have).
2. Releases IRecordInfo pointer.
3. Deallocates the struct itself with CoTaskMemFree (under assumption
that it was allocated by CoTaskMemAlloc).

So you have two options. Either you clean up with VariantClear, then you
need to allocate your struct with CoTaskMemAlloc. Or you avoid
VariantClear (by using plain VARIANT rather than CComVariant) and
perform steps #1 and #2 manually, then the structure can be allocated in
any way you want, including on the stack.

> That works, and in VB I receive the event but, when I access to the
> UDT it crash:
>
> Private Sub MyObj_OnError(error_info As AxEuroATLib.T_ErrorInfo)
> Debug.Print "ERROR : " & error_info.Description <<<<<< CRASH!!!!
> End Sub

Have you noticed the part of my previous response where I pointed out
that you are passing garbage pointers instead of valid BSTRs?

Barzo

unread,
Apr 29, 2009, 4:13:41 AM4/29/09
to
On 28 Apr, 20:19, "Igor Tandetnik" <itandet...@mvps.org> wrote:
>
> Now I remember what the deal is with UDTs. When you pack a UDT into a
> VARIANT, and later call VariantClear on it (which CComVariant's
> destructor would do automatically in your case), VariantClear does three
> things:
>
> 1. Calls IRecordInfo::RecordClear on your struct. This would deallocate
> any fields that require deallocation (such as BSTR fields you have).
> 2. Releases IRecordInfo pointer.
> 3. Deallocates the struct itself with CoTaskMemFree (under assumption
> that it was allocated by CoTaskMemAlloc).
>
> So you have two options. Either you clean up with VariantClear, then you
> need to allocate your struct with CoTaskMemAlloc. Or you avoid
> VariantClear (by using plain VARIANT rather than CComVariant) and
> perform steps #1 and #2 manually, then the structure can be allocated in
> any way you want, including on the stack.
>

Thanks a lot Igor, using CoTaskMemAlloc works!
So, I want say "I don't know how to thank you!!"

Thanks for any explanations! Now I'm a little bit more aware on COM in
ATL!!

Daniele.

Barzo

unread,
Apr 29, 2009, 6:15:33 AM4/29/09
to
On 28 Apr, 20:19, "Igor Tandetnik" <itandet...@mvps.org> wrote:

> Now I remember what the deal is with UDTs. When you pack a UDT into a
> VARIANT, and later call VariantClear on it (which CComVariant's
> destructor would do automatically in your case), VariantClear does three
> things:
>
> 1. Calls IRecordInfo::RecordClear on your struct. This would deallocate
> any fields that require deallocation (such as BSTR fields you have).
> 2. Releases IRecordInfo pointer.
> 3. Deallocates the struct itself with CoTaskMemFree (under assumption
> that it was allocated by CoTaskMemAlloc).
>
> So you have two options. Either you clean up with VariantClear, then you
> need to allocate your struct with CoTaskMemAlloc. Or you avoid
> VariantClear (by using plain VARIANT rather than CComVariant) and
> perform steps #1 and #2 manually, then the structure can be allocated in
> any way you want, including on the stack.

Thanks Igor!
Using CoTaskMemAlloc works!

Thanks again for the support and for your explanations!
Daniele.

rajb...@community.nospam

unread,
May 8, 2009, 1:12:01 PM5/8/09
to
Thanks Igor, I learned about the VARIANT type and implemented it that way.
works great!
--
Raj Banerjee, Cisco Systems
0 new messages