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 |
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 > |
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 |
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 |
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 > > > |
Free forum by Nabble | Edit this page |