On Monday 3 April 2000 Richard Hartman wrote:
> HRESULT CTest::ReturnABoolean(VARIANT_BOOL *pVal)
> {
> bool retval = MyBoolFunction();
> *pVal = retval ? VARIANT_TRUE : VARIANT_FALSE;
> return S_OK;
> }
Are you sure this will work?
Yes, absolutely. Why not try it out for your own peace of mind?
I had been given the impression that the called
function was responsible for allocating the Variant to hold the return
value.
What VARIANT? See below.
The caller, in that case, is simply providing a pointer to a storage
location for the address of the newly-allocated Variant - NOT a pointer to
an existing Variant into which a new value may be written.
Well, certainly not a pointer to any sort of VARIANT anyway.
Do not get VARIANT the structure confused with VARIANT_BOOL the
typedef. If you look at the definition of VARIANT, in OAIDL.H, you’ll
see something like:
struct tagVARIANT {
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union {
…
VARIANT_BOOL boolVal; /* VT_BOOL */
…
}
};
In WTYPES.H, VARIANT_BOOL is simply a typedef for short.
So ultimately, the caller of ReturnABoolean() above is passing a
pointer to a short where the function can place its return value.
I believe BSTR’s may work the same way. Lots of weirdness and poor
documentation on this C-to-VB boundary.
Again, it’s not specifically C-to-VB. It’s not even restricted to
Automation. This stuff is standard COM and applicable everywhere that
COM is. And the documentation is perfectly adequate: you’ll find it in
the MSDN Library, under Platform SDK\Component Services.
Try to stay on top of how many levels of indirection there are in
parameters. A simple [in] parameter will have none: you get a long or
a VARIANT. An [in, out] or [out] will have one immediately obvious
level since you’re now being passed a long* or VARIANT*. These
pointers must point to a valid object of the appropriate type, and the
storage for this must be allocated by the caller. The callee will
never have to worry about this, and for simple data types such as
long and VARIANT_BOOL (but *not* BSTR, VARIANT and IUnknown*) that’s
the end of the story.
For complex data types such as BSTR, VARIANT and interface pointers
(but *not* VARIANT_BOOL which is a simple data type), there is an
extra layer possible: the structure of one of these objects does not
hold all the data of the object itself. A BSTR is for most purposes
synonymous with a pointer to a string. It does not itself contain the
string though, that must be allocated separately. A VARIANT can
potentially also point to non-resident data: it can be VT_BSTR,
VT_DISPATCH, VT_BYREF|anything, VT_ARRAY|anything. It is this extra
non-resident data which the callee, in some circumstances, may have to
allocate or deallocate.
The rules for allocation are:
[in] parameters. The caller takes care of everything. It is an error
for the callee to attempt to free any passed in data.
[in, out] parameters. The caller will allocation storage for at least
the first level of indirection. There may or may not be any extra data
after that. The callee is free to take the data as passed and take no
further action. If a data must be fed back up the the caller, then the
callee must free the old value, and allocate the new. The caller will
then free the new value when it has finished. All allocation must be
done using the standard CoXxx APIs which take advantage of the task
allocator for allocating real memory.
[out] parameters. Exactly the same as [in, out], except that the
passed in data should be NULL, so you shouldn’t necessarily have to
free it before replacing it with the out data.
It may also be helpful to always ignore the first ‘*’ after an
interface pointer. While technically an IUnknown* *is* a pointer to
something else, you will almost never have occasion to manipulate the
pointed to data directly. So it makes more sense to consider any
interface pointer as an opaque blob which you just happen to be able
to call functions against. If you consider an IUnknown* to be an
IUnknownBlob, then functions such as:
HRESULT MyInterfaceFunc([out] IUnknownBlob *pVal); /* actually means IUnknown **pVal */
HRESULT MyLongFunc([out] long *pVal);
are now clearly orthogonal. The caller is responsible for allocating
storage for the long or IUnknownBlob, and passes you a pointer to that
storage so you can fill it in. It is perhaps this apparent (but not
useful in any practical sense) double-indirection of the IUnknown**
used in [out] parameters that caused you to say:
The caller, in that case, is simply providing a pointer to a storage
location for the address of the newly-allocated Variant
But this is only ever the case for [out] IInterface** parameters. an
[out] BSTR is a BSTR*, and an [out] VARIANT is a VARIANT*, not a
BSTR** or a VARIANT**.
Hope I’ve made this more rather than less clear.
John
you gave me something that i could touch in a world where i’d had too much
something i could feel with my broken hands full of lost ideals but soon i’m
returning to you my friend and we’ll go where the rivers end in the silver sea
and i’ll carry you if you carry me