Allowed “out” parameter types in a COM automation interface

我与影子孤独终老i 提交于 2019-12-05 18:45:53

Obviously, when you call the interface via IDispatch, all the parameters are always passed as VARIANTs. 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 from VARIANTs 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 a VARIANT, as you discovered. ATL (or whatever library you're using) will take care of translating parameters for you.

  • [in, out] parameters must be VARIANT*. 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 appropriate VARIANT so that VBScript (or whatever IDispatch 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 the DISPPARAMS 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:

enum VARENUM
    {   VT_EMPTY    = 0,
    VT_NULL = 1,
    VT_I2   = 2,
    VT_I4   = 3,
    VT_R4   = 4,
    VT_R8   = 5,
    VT_CY   = 6,
    VT_DATE = 7,
    VT_BSTR = 8,
    VT_DISPATCH = 9,
    VT_ERROR    = 10,
    VT_BOOL = 11,
    VT_VARIANT  = 12,
    VT_UNKNOWN  = 13,
    VT_DECIMAL  = 14,
    VT_I1   = 16,
    VT_UI1  = 17,

You don't see INT there, do you? LONG instead (which is VT_I4) is going to work just fine and be supported nice everywhere.

For scripting environment you are good to go with VARIANTs 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:

/*
 * VARENUM usage key,
 *
 * * [V] - may appear in a VARIANT
 * * [T] - may appear in a TYPEDESC
 * * [P] - may appear in an OLE property set
 * * [S] - may appear in a Safe Array
 *
 *
 *  VT_EMPTY            [V]   [P]     nothing
 *  VT_NULL             [V]   [P]     SQL style Null
 *  VT_I2               [V][T][P][S]  2 byte signed int
 *  VT_I4               [V][T][P][S]  4 byte signed int
 *  VT_R4               [V][T][P][S]  4 byte real
 *  VT_R8               [V][T][P][S]  8 byte real
 *  VT_CY               [V][T][P][S]  currency
 *  VT_DATE             [V][T][P][S]  date
 *  VT_BSTR             [V][T][P][S]  OLE Automation string
 *  VT_DISPATCH         [V][T]   [S]  IDispatch *
 *  VT_ERROR            [V][T][P][S]  SCODE
 *  VT_BOOL             [V][T][P][S]  True=-1, False=0
 *  VT_VARIANT          [V][T][P][S]  VARIANT *
 *  VT_UNKNOWN          [V][T]   [S]  IUnknown *
 *  VT_DECIMAL          [V][T]   [S]  16 byte fixed point
 *  VT_RECORD           [V]   [P][S]  user defined type
 *  VT_I1               [V][T][P][s]  signed char
 *  VT_UI1              [V][T][P][S]  unsigned char
 *  VT_UI2              [V][T][P][S]  unsigned short
 *  VT_UI4              [V][T][P][S]  unsigned long
 *  VT_I8                  [T][P]     signed 64-bit int
 *  VT_UI8                 [T][P]     unsigned 64-bit int
 *  VT_INT              [V][T][P][S]  signed machine int
 *  VT_UINT             [V][T]   [S]  unsigned machine int
 *  VT_INT_PTR             [T]        signed machine register size width
 *  VT_UINT_PTR            [T]        unsigned machine register size width
 *  VT_VOID                [T]        C style void
 *  VT_HRESULT             [T]        Standard return type
 *  VT_PTR                 [T]        pointer type
 *  VT_SAFEARRAY           [T]        (use VT_ARRAY in VARIANT)
 *  VT_CARRAY              [T]        C style array
 *  VT_USERDEFINED         [T]        user defined type
 *  VT_LPSTR               [T][P]     null terminated string
 *  VT_LPWSTR              [T][P]     wide null terminated string
 *  VT_FILETIME               [P]     FILETIME
 *  VT_BLOB                   [P]     Length prefixed bytes
 *  VT_STREAM                 [P]     Name of the stream follows
 *  VT_STORAGE                [P]     Name of the storage follows
 *  VT_STREAMED_OBJECT        [P]     Stream contains an object
 *  VT_STORED_OBJECT          [P]     Storage contains an object
 *  VT_VERSIONED_STREAM       [P]     Stream with a GUID version
 *  VT_BLOB_OBJECT            [P]     Blob contains an object 
 *  VT_CF                     [P]     Clipboard format
 *  VT_CLSID                  [P]     A Class ID
 *  VT_VECTOR                 [P]     simple counted array
 *  VT_ARRAY            [V]           SAFEARRAY*
 *  VT_BYREF            [V]           void* for local use
 *  VT_BSTR_BLOB                      Reserved for system use
 */

There are two different technologies involved in your question:

  • COM Automation, wich evolves around the IDispatch interface and friends (especially the restricted type system which doesn't need specific marshaling code because OLEAUT does the job automatically when it gets a TLB)
  • VBScript, which is one specific COM Automation client.

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!