I'm implementing COM automation (dual interface) for an application. The automation interface will be called from VBScript. I'm not quite clear on what types are allowed for the method's arguments. I do know that basically values have to fit in a VARIANT
, but does that mean every parameter of type int
must be passed through a VARIANT
, or can one pass int
directly?
For example, two methods I have in my MIDL file are:
HRESULT SetDate([in] int Year, [in] int Month, [in] int Day);
HRESULT GetDate([out] int* pYear, [out] int* pMonth, [out] int* pDay);
Calling SetDate
from VBScript works. Calling GetDate
as shown fails, which is to say that in the implementation, which is in C++, ITypeInfo::Invoke
returns a code indicating a type error.
I've observed that if I use a VARIANT
instead of an int
, as shown below, it works.
HRESULT GetDate([out] VARIANT* pYear, [out] VARIANT* pMonth, [out] VARIANT* pDay);
So is int
not allowed for parameters (because of the dual interface), or must I be doing something else wrong? If int
is not allowed, why does SetDate
work - is there a difference between in
and out
parameters in this respect?
Furthermore, this pair of methods works, although both use int
:
[propget] HRESULT System([out, retval] int* pSystem);
[propput] HRESULT System([in] int System);
How come - are the rules for the allowed parameter types different for a property, or when the parameter is declared as retval
?
I can't quite make sense of it all - would be grateful if anyone can clarify this.
Obviously, when you call the interface via
IDispatch
, all the parameters are always passed asVARIANT
s. Yet, your implementation potentially uses other types. How is the gap bridged?ATL (assuming that's what you're using) will implement
Invoke
for you, with code that converts arguments fromVARIANT
s to the proper types used by your method signature before forwarding the call to the actual method.Here are the rules:
[in]
parameters can be just about any type that fits in aVARIANT
, as you discovered. ATL (or whatever library you're using) will take care of translating parameters for you.[in, out]
parameters must beVARIANT*
. If you use anything else, either the call won't work or the return value will be lost (I don't remember which way it goes; you indicated you had a run-time error for this situation). Upon your method returning, ATL will convert the argument into an appropriateVARIANT
so that VBScript (or whateverIDispatch
client made the call) can get a hold of the output value.[retval, out]
are a special case. You can use a pointer to whatever type you choose, and ATL will take care of it. I presume that what makes this possible is that the return value is provided back outside of theDISPPARAMS
mechanism.[out]
... just don't. They don't work - VBScript cannot use[out]
parameters correctly. Mind you, they will "work" in that the method will execute without errors, but VBScript cannot distinguish between[out]
and[in, out]
, which means that VBScript expects your method to release whatever value was on the parameter when you received it. If you use[out]
, whatever the client code placed on the parameter before making the method call will be leaked permanently.Types you are going to have less troubles with are listed on top of
VT_xxx
enumeration in Windows SDK:You don't see
INT
there, do you?LONG
instead (which isVT_I4
) is going to work just fine and be supported nice everywhere.For scripting environment you are good to go with
VARIANT
s and if this makes your life easier on C++ side - with simple types mentioned above. If you need an array,VARIANT
is a good holder for them as well.A nice table there also hints about compatibility of types:
There are two different technologies involved in your question:
out or in/out parameters that are not VARIANT are fine for COM automation in general, but not for VBScript, as VBScript basically only knows VARIANTs.
You will find the definitive explanation on Eric Lippert's blob here: In, Out, In-Out, Make Up Your Mind Already