包装一个值, 安全数组或 COM 对象, 以供脚本使用或传递给 COM 方法.
ComObj := ComValue(VarType, Value , Flags)
ComValue
本身是一个派生自 Any
的类, 但只用于创建或识别 COM 封装对象.
类型: 整数
表示值类型的整数. 类型列表见 ComObjType.
类型: Any
要包装的值.
如果这是一个纯整数且 VarType 不是 VT_R4, VT_R8, VT_DATE 或 VT_CY, 则直接使用其值; 特别是 VT_BSTR, VT_DISPATCH 和 VT_UNKNOWN 可以用一个指针值来初始化.
在任何其他情况下, 使用与普通 COM 方法调用相同的规则将值复制到临时 VARIANT 中. 如果源变量类型不等于 VarType, 则通过调用 wFlags 值为 0 的 VariantChangeType 来尝试转换. 如果转换失败, 则抛出异常.
类型: 整数
影响包装器对象行为的标志; 有关详情, 请参阅 ComObjFlags.
类型: Object
此函数返回包含变体类型和值或指针的封装对象, 尤其是 ComValue, ComValueRef, ComObjArray 或 ComObject.
该对象有多个用途:
ComValue(0xB, -1)
创建一个表示 COM 布尔值为 true 的对象.如果一个封装对象的 VarType 是 VT_UNKNOWN(13) 或包含 VT_BYREF(0x4000) 或 VT_ARRAY(0x2000) 标志, 则 Ptr
属性可用于检索对象, 类型化变量或安全数组的地址. 这允许将 ComObject 本身传递给任何具有 "Ptr"
类型的 DllCall 或 ComCall 参数, 但也可以显式地使用. 例如, 在这些情况下, ComObj.Ptr
等同于 ComObjValue(ComObj)
.
如果一个封装对象的 VarType 是 VT_UNKNOWN(13) 或 VT_DISPATCH(9) 并且封装指针为 null(0), 则可以使用 Ptr
属性来检索当前的 null 值, 或者为封装对象赋值一个指针. 一旦被赋值(如果非-null), 当封装对象被释放时, 指针将被自动释放. 这可以与类型为 "Ptr*"
或 "PtrP"
的 DllCall 或 ComCall 输出参数一起使用, 以确保指针会被自动释放, 例如当发生错误时. 有关示例, 请参阅 ComObjQuery.
如果一个包装器对象的 VarType 是 VT_DISPATCH(9) 并且为 null(0) 指针值赋值为非-null 指针值, 其类型从 ComValue
变为 ComObject
. 包装对象的属性和方法变得可用, 而 Ptr
属性变为不可用.
如果包装器对象的 VarType 包含 VT_BYREF(0x4000) 标志, 可以用空的方括号对 []
来读写引用的值.
当创建一个引用, Value 须为变量的内存地址, 或足够存储指定类型的值的缓冲. 例如, 下面的代码可以用来创建一个 VBScript 函数可以写入的变量:
vbuf := Buffer(24, 0) vref := ComValue(0x400C, vbuf.ptr) ; 0x400C 为 VT_BYREF 与 VT_VARIANT 组合而得. vref[] := "in value" sc.Run("Example", vref) ; sc 应像下面例子一样进行初始化. MsgBox vref[]
请注意, 尽管在通过 vref[]
或 COM 方法分配新值时, 任何先前的值都会被释放, 但最终的值不会被自动释放. 释放该值需要知道它是哪种类型. 因为在这种情况下它是 VT_VARIANT, 它可以通过用 DllCall 调用 VariantClear 来释放, 或者使用更简单的方法: 分配一个整数, 如 vref[] := 0
.
如果方法接受如上所示的 VT_BYREF 和 VT_VARIANT 的组合, 可以使用 VarRef 代替. 例如:
some_var := "in value" sc.Run("Example", &some_var) MsgBox some_var
然而, 有些方法需要更特定的变体类型, 如 VT_BYREF | VT_I4
. 在这种情况下, 必须使用上面所示的第一种方法, 用适当的变体类型替换 0x400C.
当此函数用于包装 IDispatch 或 IUnknown 接口指针(以整数形式传递) 时, 包装器对象负责在适当的时候自动释放指针. 因此, 如果脚本打算在调用这个函数之后使用指针, 它必须首先调用 ObjAddRef(DispPtr)
. 相比之下, 如果 Value 本身是 ComValue 或 ComObject, 则没有必要这样做.
从 VT_UNKNOWN 到 VT_DISPATCH 的转换会导致对 IUnknown::QueryInterface 的调用, 这可能会产生一个不同于原来的接口指针, 并且如果对象没有实现 IDispatch, 会抛出异常. 相反, 如果 Value 是整数, 且 VarType 为 VT_DISPATCH, 则直接使用该值, 因此必须是一个 idispatch 兼容的接口指针.
可以使用 ComObjType 检索包装器对象的 VarType.
可以使用 ComObjValue 检索包装器对象的 Value.
已知限制: 每次包装 COM 对象时, 都会创建新的包装器对象. 如 obj1 == obj2
和 arr[obj1] := value
这样的比较和赋值运算, 将这两个包装器对象视为唯一的, 即使它们包含相同的变体类型和值.
ComObjFromPtr, ComObject, ComObjGet, ComObjConnect, ComObjFlags, ObjAddRef/ObjRelease, ComObjQuery, GetActiveObject (Microsoft Docs)
#Requires AutoHotkey v2 32-bit ; 32-bit for ScriptControl. code := " ( Sub Example(Var) MsgBox Var Var = "out value!" End Sub )" sc := ComObject("ScriptControl"), sc.Language := "VBScript", sc.AddCode(code) ; 示例: 传递 VARIANT ByRef 至 COM 方法. var := ComVar() var[] := "in value" sc.Run("Example", var.ref) MsgBox var[] ; 同样的事情, 但更直接: variant_buf := Buffer(24, 0) ; 使 VARIANT 的缓冲足够大. var := ComValue(0x400C, variant_buf.ptr) ; 引用 VARIANT. var[] := "in value" sc.Run("Example", var) ; 传递 VT_BYREF ComValue 本身, 没有 [] 或 .ref. MsgBox var[] ; 如果 VARIANT 包含一个字符串或对象, 必须显式地释放它 ; 通过调用 VariantClear 或赋值一个纯数字值: var[] := 0 ; 当方法接受 VT_BYREF|VT_VARIANT 时的最简单方法: var := "in value" sc.Run("Example", &var) MsgBox var ; ComVar: 一个可以用来传递一个值 ByRef 的对象. ; this[] 检索值. ; this[] := Val 设置值. ; this.ref 检索一个 ByRef 对象, 用于传递给一个 COM 方法. class ComVar { __new(vType := 0xC) { ; 为 VARIANT 分配内存, 以保存我们的值. ; 即使在 vType != VT_VARIANT 时也会使用 VARIANT, 这样 VariantClear 就可以被 __delete 使用. this.var := Buffer(24, 0) ; 创建一个可以用来传递变量 ByRef 的对象. this.ref := ComValue(0x4000|vType, this.var.ptr + (vType=0xC ? 0 : 8)) ; 存储 VariantClear 的变体类型(如果不是 VT_VARIANT). if vType != 0xC NumPut "ushort", vType, this.var } __item { get => this.ref[] set => this.ref[] := value } __delete() { DllCall("oleaut32\VariantClear", "ptr", this.var) } }