【问题标题】:Allowed "out" parameter types in a COM automation interfaceCOM 自动化接口中允许的“out”参数类型
【发布时间】:2013-05-06 21:49:04
【问题描述】:

我正在为应用程序实现 COM 自动化(双接口)。自动化接口将从 VBScript 中调用。我不太清楚该方法的参数允许哪些类型。我知道基本上值必须适合VARIANT,但这是否意味着int 类型的每个参数都必须通过VARIANT,或者可以直接通过int

例如,我的 MIDL 文件中有两种方法:

HRESULT SetDate([in] int Year, [in] int Month, [in] int Day);
HRESULT GetDate([out] int* pYear, [out] int* pMonth, [out] int* pDay);

从 VBScript 调用 SetDate 有效。如图调用GetDate失败,也就是说在C++的实现中,ITypeInfo::Invoke返回一个类型错误的代码。

我观察到,如果我使用VARIANT 而不是int,如下所示,它可以工作。

HRESULT GetDate([out] VARIANT* pYear, [out] VARIANT* pMonth, [out] VARIANT* pDay);

那么int 不允许用于参数(因为双接口),还是我必须做其他错误?如果 int 不允许,为什么 SetDate 工作 - inout 参数在这方面有区别吗?

此外,这对方法有效,尽管两者都使用int

[propget] HRESULT System([out, retval] int* pSystem);
[propput] HRESULT System([in] int System);

为什么 - 对于一个属性,允许的参数类型的规则是否不同,或者当参数声明为 retval 时?

我无法完全理解这一切 - 如果有人能澄清这一点,我将不胜感激。

【问题讨论】:

    标签: com vbscript automation


    【解决方案1】:

    显然,当你通过IDispatch调用接口时,所有参数总是以VARIANTs的形式传递。然而,您的实现可能会使用其他类型。差距是如何弥补的?

    ATL(假设您正在使用)将为您实现Invoke,代码将参数从VARIANTs 转换为您的方法签名使用的正确类型,然后再将调用转发到实际方法。

    规则如下:

    • [in] 参数几乎可以是任何适合VARIANT 的类型,正如您所发现的。 ATL(或您正在使用的任何库)将为您处理转换参数。

    • [in, out] 参数必须为VARIANT*。如果您使用其他任何东西,调用将不起作用或返回值将丢失(我不记得它是哪条路;您表示您在这种情况下遇到了运行时错误)。在您的方法返回时,ATL 会将参数转换为适当的 VARIANT,以便 VBScript(或任何进行调用的 IDispatch 客户端)可以获取输出值。

    • [retval, out] 是一种特殊情况。您可以使用指向您选择的任何类型的指针,ATL 会处理它。我认为使这成为可能的原因是返回值是在DISPPARAMS 机制之外提供的。

    • [out]...别这样。它们不起作用 - VBScript 无法正确使用 [out] 参数。请注意,它们将“工作”,因为该方法将在没有错误的情况下执行,但 VBScript 无法区分 [out][in, out],这意味着 VBScript 期望您的方法在您收到参数时释放参数上的任何值。如果您使用[out],那么在进行方法调用之前放置在参数上的任何客户端代码都将被永久泄露。

    【讨论】:

    • 感谢您的回复。我没有使用 ATL,只是使用普通的 C++。如果我将 [out] 更改为 [out, ref],应该可以吗?
    • 你真的在实现自己的Invoke吗?这是一个重要的细节。您应该将其添加到问题中。原则上,您可以编写 Invoke 的实现,它采用 vbscript 提供的 VARIANT 并将其转换为您的方法的 int*(如果可以,否则返回错误)。然后,您的 Invoke 将负责获取参数的返回值并将其填充回脚本客户端提供的参数中(如果可以,或者如果不能,则丢弃它)。这是一项非常重要的工作——客户可以传递给您任何东西,您需要为此做好准备。
    • 注意:当我想说[in, out] 时,我的帖子的早期版本使用了[ref] - 一个愚蠢的失误。
    • @Dabbler,使用 [out, ref] 只是声明返回值不能为空。它也不会改变任何东西,因为 VBScript 不知道它。请记住,VBScript 看不到您的类型库;它只看到一个 IDispatch COM 指针。对于 VBScript 来说,这完全是关于“进入的 VARIANT 列表”。 Invoke 必须弥合这些 VARIANT 与实际方法参数之间的差距。如果调用中的一个参数碰巧是一个命名变量,VBScript 将提供一个VT_BYREF VARIANT,并通知 IDispatch 在必要时用新值更新 VARIANT。
    • 非常有趣 - 我以前从未深入研究过 ATL 如何实现 Invoke,但您“让我看起来”。我没有意识到实现的核心是由解析类型库的 COM 提供的 API 提供的(ITypeInfo::Invoke 由加载的类型库提供)——可能与您正在调用的 API 相同。然后我帖子中的信息适用于该标准实现。正如我所提到的,您仍然可以提供自己的 Invoke,但这很可能是矫枉过正。
    【解决方案2】:

    Windows SDK 中VT_xxx 枚举的顶部列出了您将遇到的麻烦较少的类型:

    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,
    

    你看不到INT,对吗? LONG(即VT_I4)将工作得很好,并且在任何地方都得到很好的支持。

    对于脚本环境,您最好使用VARIANTs,如果这使您在 C++ 方面的生活更轻松 - 使用上面提到的简单类型。如果你需要一个数组,VARIANT 也是一个很好的持有者。

    一张不错的表格还暗示了类型的兼容性:

    /*
     * 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
     */
    

    【讨论】:

      【解决方案3】:

      您的问题涉及两种不同的技术:

      • COM 自动化,围绕IDispatch 接口和朋友发展而来(尤其是不需要特定编组代码的受限类型系统,因为 OLEAUT 在获得 TLB 时会自动完成这项工作)
      • VBScript,它是一种特定的 COM 自动化客户端。

      不是 VARIANT 的输出或输入/输出参数通常适用于 COM 自动化,但不适用于 VBScript,因为 VBScript 基本上只知道 VARIANT。

      您将在此处找到有关 Eric Lippert 的 blob 的最终解释:In, Out, In-Out, Make Up Your Mind Already

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-02-03
        • 1970-01-01
        • 2021-03-15
        • 1970-01-01
        • 2014-09-18
        • 1970-01-01
        • 2018-03-03
        • 2015-04-28
        相关资源
        最近更新 更多