proper way to use LPVOID's

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

proper way to use LPVOID's

Eric Winger-4
I'm been doing some development using the COM facilityies in Dolphin.
I've let Dolphin import the type libraries and in many cases it
generates LPVOID's as the parameter type in the vtable for the
interface...Obviously this means that when the implementor class
receives a message the type for one of these parameters will be LPVOID.

What I've been struggling with, is how to set the value of the LPVOID
object so that when the COM interface gets this parameter back filled
with goodies, he can use them.

On the interface side I want to send an array of HRESULTS. Therefore I
created:

var := StructureArray length: numberOfElements elementClass:  HRESULT.

which ultimately will call (on the imp side):

DoSomething: var

Then that comes through on the implementation side as:

a LPVOID(an ExternalAddress(NULL))

I thought I could change the value of the the LPVOID (on the imp side)
to point at a new array by doing something like:

DoSomething: anLPVOID

arr := Array new: numberOfElements withAll: S_OK.
arr at: 1 put: S_FALSE.
anLPVOID value: arr yourAddress asExternalAddress detach.

but what comes back on the interface side is:

a HRESULT(0x871FBC4 - Unrecognised HRESULT - 16r871FBC4)

What's interesting is that when I use the same technique with a class I
defined (OPCITEMDEF) instead of HRESULT, this appears to work.

I'm really pretty baffled. And I feel I've tried virtually every
possible way to coerce that LPVOID's value (such as trying value value:,
bytes:... and on and on)

What is the proper way to use LPVOID's in this scenario?

Any help would be really, really, really appreciated.

Eric


Reply | Threaded
Open this post in threaded view
|

Re: proper way to use LPVOID's (simpler question...I hope)

Eric Winger-4
OK, maybe it will help me understand LPVOID if someone can answer this,
more simple, question:

| errors sa |
errors :=StructureArray length: 1 elementClass: HRESULT.
errors at: 1 put: (HRESULT fromInteger: 2).
lpVoid := LPVOID fromAddress: errors yourAddress asExternalAddress detach.
sa := StructureArray fromAddress: lpVoid length: 1 elementClass: HRESULT.
sa inspect

Why isn't this valid? All I'm doing is building a StructureArray and
trying to read it from an LPVOID. I obviously am really ig'nant of how
LPVOIDs & StructureArrays work. But it seems that the above should work.

thx for any input... or any pointers to more info on how Dolphin handles
external structures, and how I can wrap my thick-head around them.

thx much

Eric

Eric Winger wrote:

> I'm been doing some development using the COM facilityies in Dolphin.
> I've let Dolphin import the type libraries and in many cases it
> generates LPVOID's as the parameter type in the vtable for the
> interface...Obviously this means that when the implementor class
> receives a message the type for one of these parameters will be LPVOID.
>
> What I've been struggling with, is how to set the value of the LPVOID
> object so that when the COM interface gets this parameter back filled
> with goodies, he can use them.
>
> On the interface side I want to send an array of HRESULTS. Therefore I
> created:
>
> var := StructureArray length: numberOfElements elementClass:  HRESULT.
>
> which ultimately will call (on the imp side):
>
> DoSomething: var
>
> Then that comes through on the implementation side as:
>
> a LPVOID(an ExternalAddress(NULL))
>
> I thought I could change the value of the the LPVOID (on the imp side)
> to point at a new array by doing something like:
>
> DoSomething: anLPVOID
>
> arr := Array new: numberOfElements withAll: S_OK.
> arr at: 1 put: S_FALSE.
> anLPVOID value: arr yourAddress asExternalAddress detach.
>
> but what comes back on the interface side is:
>
> a HRESULT(0x871FBC4 - Unrecognised HRESULT - 16r871FBC4)
>
> What's interesting is that when I use the same technique with a class I
> defined (OPCITEMDEF) instead of HRESULT, this appears to work.
>
> I'm really pretty baffled. And I feel I've tried virtually every
> possible way to coerce that LPVOID's value (such as trying value value:,
> bytes:... and on and on)
>
> What is the proper way to use LPVOID's in this scenario?
>
> Any help would be really, really, really appreciated.
>
> Eric
>


Reply | Threaded
Open this post in threaded view
|

Re: proper way to use LPVOID's (simpler question...I hope)

Blair McGlashan
Eric

I was intending on answering your previous question, but I haven't found a
spare moment today.

You wrote in message news:[hidden email]...

> OK, maybe it will help me understand LPVOID if someone can answer this,
> more simple, question:
>
> | errors sa |
> errors :=StructureArray length: 1 elementClass: HRESULT.
> errors at: 1 put: (HRESULT fromInteger: 2).
> lpVoid := LPVOID fromAddress: errors yourAddress asExternalAddress detach.
> sa := StructureArray fromAddress: lpVoid length: 1 elementClass: HRESULT.
> sa inspect
>
> Why isn't this valid? All I'm doing is building a StructureArray and
> trying to read it from an LPVOID. I obviously am really ig'nant of how
> LPVOIDs & StructureArrays work. But it seems that the above should work.

It's not valid because LPVOID is always used to represent a
double-indirection (i.e. a pointer to a pointer). Recall that LPVOID is in
the ExternalStructure hierarchy. All ExternalStructures have two possible
forms, value and reference. The "value" form contains a ByteArray with the
structures data embedded, this is equivalent to a C++ declaration of the
form '<struct> x', e.g. RECT r;
The "reference" form contains an ExternalAddress that points to the
externally held data, and is equivalent to a C++ declaration of the form
'<struct>* x'. Unlike in C/C++, however, Dolphin makes most of the
difference between reference and value forms transparent, and there is no
separate syntax for accessing the members/fields.

So how is this relevant to LPVOID? In fact LPVOID is only ever used in
"reference" form, i.e. as a double indirection. Normally in Dolphin
addresses/pointers are represented by instances of ExternalAddress or one of
its subclasses. However there are times when one needs to represent a
pointer to a pointer, probably so that one can update that pointer by
updating through an indirection to it. The canonical example is a pointer
"output" parameter in C/C++ (and in COM of course). This is where LPVOID
typically comes in as the parameter passed to a callback/call-in method. If
you are only calling out through COM interfaces then you normally need not
concern yourself with LPVOID (though you might need it for arrays of
pointers - an example of which is NPApplet>>OnNewApplet:etc, although that
also happens to be a call-in method implementation).

In your expression when you say "LPVOID fromAddress: <x>" you are saying
create me a reference to a pointer at address <x>, consequently the LPVOID
thinks that the 4-bytes at that address, in your case the base address of an
array of HRESULT error codes, i.e. 32-bit integers, is a pointer. Since the
first of your HRESULT codes has the value 2, the LPVOID thinks it is a
reference to a pointer that currently has the value 2. If you were to assign
through the LPVOID using the #value: message, then the first HRESULT would
get replaced.

Normally you will not need to use LPVOID at all (which is just as well as
the double-indirection gets confusing), but it does surface for output
parameters in COM. A straightforward example is IUnknown::QueryInterface().
Note that the call-out (IUnknown>>QueryInterface:ppvObject:) defines its
second parameter as void** (or it could have been lppvoid). Then looking at
the call-in which implements IUnknown::QueryInterface() in Dolphin's COM
framework, namely COMObjectStub>>QueryInterface:ppvObject:, the ppvOut
parameter will be passed to that method as an instance of LPVOID so that its
#value: can be set to the pointer to be passed out (see
COMObjectStub>>innerQueryInterface:ppvObject: for the real action).

Try this:

| errors sa base |
errors :=StructureArray length: 1 elementClass: HRESULT.
errors at: 1 put: (HRESULT fromInteger: 2).
base := errors yourAddress asExternalAddress.
lpVoid := LPVOID fromAddress: base basicYourAddress.
sa := StructureArray fromAddress: lpVoid length: 1 elementClass: HRESULT.
sa inspect

(The #basicYourAddress is necessary because when you send #yourAddress to an
ExternalAddress it answers itself).

I don't know whether this all helps with your original question though. Is
it possible that you could be more specific about the exact COM interface
you are trying to use.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: proper way to use LPVOID's

Blair McGlashan
In reply to this post by Eric Winger-4
Eric

You wrote in message news:[hidden email]...
> I'm been doing some development using the COM facilityies in Dolphin.
> I've let Dolphin import the type libraries and in many cases it
> generates LPVOID's as the parameter type in the vtable for the
> interface...Obviously this means that when the implementor class
> receives a message the type for one of these parameters will be LPVOID.

Dolphin's type-library analyzer should only generate LPVOID parms for
doubly-indirected (typically [output]) arguments. This is where the caller
is expecting to receive a pointer as the result of the method call and
because COM uses the actual function return value for an error/success code,
this can only be achieved using a double indirection.

> What I've been struggling with, is how to set the value of the LPVOID
> object so that when the COM interface gets this parameter back filled
> with goodies, he can use them.
>
> On the interface side I want to send an array of HRESULTS. Therefore I
> created:
>
> var := StructureArray length: numberOfElements elementClass:  HRESULT.
>

Fine so far. But do watch out for the lifetime of that object.

> which ultimately will call (on the imp side):
>
> DoSomething: var
>
> Then that comes through on the implementation side as:
>
> a LPVOID(an ExternalAddress(NULL))
>
> I thought I could change the value of the the LPVOID (on the imp side)
> to point at a new array by doing something like:
>
> DoSomething: anLPVOID
>
> arr := Array new: numberOfElements withAll: S_OK.
> arr at: 1 put: S_FALSE.
> anLPVOID value: arr yourAddress asExternalAddress detach.

If you are actually using Array, then that will fail. Array is an array of
Smalltalk object pointers. Also they cannot be detached, so the Array will
be garbage collected. Also you need to 'detach' the right thing to prevent
the memory being GC'd. Furthermore if the caller is really expecting you to
return an array of HRESULT codes that you are expected to allocate (an
unusual COM interface, but possible), then according to the rules of COM you
must allocate that memory from the COM task memory heap (the caller is
expected to take ownership of it, and so has to know which heap to free it
back to when it has finished with it). What you can't do in this case is to
return a pointer into a Dolphin object, even a ByteArray, because that
memory is still owned by Dolphin and will be GC'd when no further references
to it exist. So taking that all together what you would actually have to do
is:

buffer := COMTaskMemory new: numberOfElements*HRESULT byteSize.
array := StructureArray fromAddress: buffer length: numberOfElements
elementClass: HRESULT.
<...set the values of the elements in the array...>
anLPVOID value: buffer detach.

In this case the StructureArray is only being used as a convenient way to
set the data in the array (though you could also use DWORDArray in this
case). You could also create the StructureArray in Dolphin memory and then
send it #copyToCOMTaskMemory to get a block of memory that you can pass
back.

Even so I would look carefully at the documentation for that COM interface
to see if this is what is really wanted. Array output parameters like this
are quite unusual.

>
> but what comes back on the interface side is:
>
> a HRESULT(0x871FBC4 - Unrecognised HRESULT - 16r871FBC4)
>
> What's interesting is that when I use the same technique with a class I
> defined (OPCITEMDEF) instead of HRESULT, this appears to work.
>
> I'm really pretty baffled. And I feel I've tried virtually every
> possible way to coerce that LPVOID's value (such as trying value value:,
> bytes:... and on and on)

I don't think the problem is your use of LPVOID, so much as confusion over
object lifetimes and the ownership of memory between COM clients and
servers. If the interface one is trying to use is a complicated one (most
COM objects are designed for use by automation clients, and this sort of
complexity does not arise there) then one really can't avoid the need to
read up on the rules of memory management in one of the COM texts.

Regards

Blair


Reply | Threaded
Open this post in threaded view
|

Re: proper way to use LPVOID's

Eric Winger-4
Blair,

Thank you thank you!

The problem was that I wasn't allocating memory properly. Using a buffer
created thusly:

buffer := COMTaskMemory new: numberOfElements*HRESULT byteSize.

seems to have solved the problem. I did this on imp & int side and I
could suddenly return arrays.

Eric

Blair McGlashan wrote:

> Eric
>
> You wrote in message news:[hidden email]...
>
>>I'm been doing some development using the COM facilityies in Dolphin.
>>I've let Dolphin import the type libraries and in many cases it
>>generates LPVOID's as the parameter type in the vtable for the
>>interface...Obviously this means that when the implementor class
>>receives a message the type for one of these parameters will be LPVOID.
>>
>
> Dolphin's type-library analyzer should only generate LPVOID parms for
> doubly-indirected (typically [output]) arguments. This is where the caller
> is expecting to receive a pointer as the result of the method call and
> because COM uses the actual function return value for an error/success code,
> this can only be achieved using a double indirection.
>
>
>>What I've been struggling with, is how to set the value of the LPVOID
>>object so that when the COM interface gets this parameter back filled
>>with goodies, he can use them.
>>
>>On the interface side I want to send an array of HRESULTS. Therefore I
>>created:
>>
>>var := StructureArray length: numberOfElements elementClass:  HRESULT.
>>
>>
>
> Fine so far. But do watch out for the lifetime of that object.
>
>
>>which ultimately will call (on the imp side):
>>
>>DoSomething: var
>>
>>Then that comes through on the implementation side as:
>>
>>a LPVOID(an ExternalAddress(NULL))
>>
>>I thought I could change the value of the the LPVOID (on the imp side)
>>to point at a new array by doing something like:
>>
>>DoSomething: anLPVOID
>>
>>arr := Array new: numberOfElements withAll: S_OK.
>>arr at: 1 put: S_FALSE.
>>anLPVOID value: arr yourAddress asExternalAddress detach.
>>
>
> If you are actually using Array, then that will fail. Array is an array of
> Smalltalk object pointers. Also they cannot be detached, so the Array will
> be garbage collected. Also you need to 'detach' the right thing to prevent
> the memory being GC'd. Furthermore if the caller is really expecting you to
> return an array of HRESULT codes that you are expected to allocate (an
> unusual COM interface, but possible), then according to the rules of COM you
> must allocate that memory from the COM task memory heap (the caller is
> expected to take ownership of it, and so has to know which heap to free it
> back to when it has finished with it). What you can't do in this case is to
> return a pointer into a Dolphin object, even a ByteArray, because that
> memory is still owned by Dolphin and will be GC'd when no further references
> to it exist. So taking that all together what you would actually have to do
> is:
>
> buffer := COMTaskMemory new: numberOfElements*HRESULT byteSize.
> array := StructureArray fromAddress: buffer length: numberOfElements
> elementClass: HRESULT.
> <...set the values of the elements in the array...>
> anLPVOID value: buffer detach.
>
> In this case the StructureArray is only being used as a convenient way to
> set the data in the array (though you could also use DWORDArray in this
> case). You could also create the StructureArray in Dolphin memory and then
> send it #copyToCOMTaskMemory to get a block of memory that you can pass
> back.
>
> Even so I would look carefully at the documentation for that COM interface
> to see if this is what is really wanted. Array output parameters like this
> are quite unusual.
>
>
>>but what comes back on the interface side is:
>>
>>a HRESULT(0x871FBC4 - Unrecognised HRESULT - 16r871FBC4)
>>
>>What's interesting is that when I use the same technique with a class I
>>defined (OPCITEMDEF) instead of HRESULT, this appears to work.
>>
>>I'm really pretty baffled. And I feel I've tried virtually every
>>possible way to coerce that LPVOID's value (such as trying value value:,
>>bytes:... and on and on)
>>
>
> I don't think the problem is your use of LPVOID, so much as confusion over
> object lifetimes and the ownership of memory between COM clients and
> servers. If the interface one is trying to use is a complicated one (most
> COM objects are designed for use by automation clients, and this sort of
> complexity does not arise there) then one really can't avoid the need to
> read up on the rules of memory management in one of the COM texts.
>
> Regards
>
> Blair
>
>
>