研究学习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可变类型。
The VARIANT type cannot have the VT_BYREF bit set.
VARIANTARG不能直接拷贝,因为它可以是引用类型。
DISPPARAMS中允许使用VT_BYREF因为调用者要保证这些引用是存在的。
VariantCopyInd可以对VARIANTARG解引用,使其变成VARIANT。
VariantCopyInd中的Ind是Indirect的意思。
named argument: An argument specified in a call both by its value and by its DISPID. Named arguments always follow positional arguments.
参考
- VariantInit function (oleauto.h)
- VariantCopyInd
- oleauto.h header
- Windows Protocols: 2.2.33 DISPPARAMS
(更新完)
2022-04-04更新 DISPPARAMS structure (oaidl.h)
在IDispatch::Invoke method (oaidl.h)中有使用到。
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
(更新完)