研究学习COM的VARIANT类型。

Wikipedia的页面:Variant type (COM)

VB中,下面两个定义等价:

Dim A
Dim A as Variant

占据16字节?前8个字节中就用了两个字节表示类别,数据保存至后8个字节中。

类别那是相当多,过一眼就得了。

OLE Automation的意思是不是在运行时发现类型信息?

Win32 API 文档 VARIANT structure (oaidl.h)

When a variant refers to another variant by using the VT_VARIANT | VT_BYREF vartype, the variant being referred to cannot also be of type VT_VARIANT | VT_BYREF. VARIANTs can be passed by value, even if VARIANTARGs cannot.

COM and Unicode Guidelines VARIANT Structure

Microsoft Active Accessibility只使用以下类型:

  • VT_I4, IVal
  • VT_DISPATCH, pdispVal
  • VT_BSTR, bstrVal
  • VT_EMPTY, none

通过https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/oleauto/nf-oleauto-variantinit初始化,通过https://docs.microsoft.com/en-us/previous-versions/windows/desktop/api/oleauto/nf-oleauto-variantclear清除之。

wil RAII resource wrappers

WIL(Windows Implementation Library)为处理VARIANT提供了一些方便包装。

为超过100多种不同的资源提供RAII包装。目标是提供

  • 统一且一致的RAII给有关的Windows控把、资源,和锁类型
  • 各种机制用于创建新的、统一的RAII包装
  • 为系统概念提供方面包装

Usage

#include <wil/resource.h>

记得在签名包含被封装的资源的头文件。resource.h可以被包含多次,每次都会生成对新进类型的包装。

要创建新的智能指针类型,WIL依赖于下列头文件

  • wil::weak_*wil::shared_*依赖<memory>
  • wil::make_*_nothrow依赖于<new>

Resource Patterns

WIL的RAII的建模参考了std::unique_ptr

  • get()访问资源
  • reset()释放资源并替换之
  • release()释放资源

WIL的资源管理样板

  • wil::unique_any,管理暗箱控把类型(HANDLE、HKEY、PSECURITY_DESCRIPTOR等)
  • wil::unique_any_array_ptr,管理一个串列,其中包含需要管理的本生类型(DWORD、HANDLE、IFOO*等)
  • wil::unique_struct,管理一个有清理需求的本生结构(VARIANT、PROCESS_INFORMATION等)
  • wil::unique_ptr,管理一个任意的,按类型的分配
  • wil::unique_com_token,管理一个COM接口而来的控把或号牌(HENDPOINT、DWORD订阅号牌等)
  • wil::unique_com_call,管理一个特定COM接口(比如ICloseable::Close)的调用
  • wil::unique_call,管理一个全局方法(CoUninitialze、CoRevertToSelf)的调用
  • wil::scope_exit,管理一个调用者提供的lambda

wil::unique_struct

针对PROCESS_INFORMATION的使用示例:

namespace details
{
    inline void __stdcall CloseProcessInformation(PROCESS_INFORMATION* info)
    {
        CloseHandle(info->hProcess);
        CloseHandle(info->hThread);
    }
}

using unique_process_information = unique_struct<PROCESS_INFORMATION, decltype(&details::CloseProcessInformation), details::CloseProcessInformation>;

reset_and_addressof()可以清除现有内容。默认会使用ZeroMemory来清零内存,如果某些成员不可为零,则需要一个自定义的初始化函数。

wil::unique_any的关键区别:

  • 智能指针类型从本生结构类型派生,而不是包含之。
  • &不释放管理的资源,而是要通过reset_and_addressof()

预定义的unique_struct比较少,本地定义的unique_struct比较常见。 unique_variant就是预定义的unique_struct之一。

Resource Helpers

许多只能指针允许访问指针内部的地址,通常是通过重载operator&来实现的,或者是通过类似于addressof的方法。如果某指针类型(比如wistd::unique_ptr)不提供对内部地址的直接访问,可以使用out_param来绕一下。

wil::detach_to_opt_param从智能指针分离出内部地址,并存到一个外部变量中。

Wrappers and helpers for specific resources

一个目录包含到特定资源包装器的链接。

BSTR

TODO

其他参考

(完)

2022-04-03更新:COM的BSTR

BSTR由以下部分组成:

  • Length prefix,四字节长度,在字符串之前,不包含结尾的0x0000
  • Data string,unicode字符串,可能包含多个嵌入的null
  • Terminator,0x0000

必须通过SysAllocString和SysFreeString来分配和释放。

(更新完)

2022-04-03更新:What’s the difference between VARIANT and VARIANTARG?

VARIANT和VARIANTARG定义其实是一样的,但是使用上有不同的规则。

VARIANTARG 描述的是DISPPARAMS中的参数,而VARIANT描述的是不能以引用方式传递的可变类型数据。

VARIANTs可以按值传递,VARIANTARGs 不行。

VT_VARIANT | VT_BYREF只能引用另一个VT_VARIANT | VT_BYREF可变类型。

VARIANT and VARIANTARG提到

The VARIANT type cannot have the VT_BYREF bit set.

VARIANTARG不能直接拷贝,因为它可以是引用类型。

DISPPARAMS中允许使用VT_BYREF因为调用者要保证这些引用是存在的。

Variant­Copy­Ind可以对VARIANTARG解引用,使其变成VARIANT。

Variant­Copy­Ind中的Ind是Indirect的意思。

named argument: An argument specified in a call both by its value and by its DISPID. Named arguments always follow positional arguments.

参考

(更新完)

2022-04-04更新 DISPPARAMS structure (oaidl.h)

IDispatch::Invoke method (oaidl.h)中有使用到。

Passing Parameters中提到

The arguments are passed in the array rgvarg[ ], with the number of arguments passed in cArgs. The arguments in the array should be placed from last to first, so rgvarg[0] has the last argument and rgvarg[cArgs -1] has the first argument.

Building Automation Clients中举了positional argument的例子,以及提到如果忽略某参数,要设成:

VariantInit(&SumArgsOpt[0]);
SumArgsOpt[0].vt = VT_ERROR;
SumArgsOpt[0].scode = DISP_E_PARAMNOTFOUND; // normally y

(更新完)