ComValue

包装一个值, 安全数组或 COM 对象, 以供脚本使用或传递给 COM 方法.

ComObj := ComValue(VarType, Value , Flags)

ComValue 本身是一个派生自 Any, 但只用于创建或识别 COM 封装对象.

参数

VarType

类型: 整数

表示值类型的整数. 类型列表见 ComObjType.

Value

类型: 整数

要包装的值. 当前仅支持整数和指针值.

Flags

类型: 整数

影响包装器对象行为的标志; 有关详情, 请参阅 ComObjFlags.

返回值

类型: ComValue, ComValueRef, ComObjArrayComObject

返回包含变体类型和值或指针的封装对象.

该对象有两个用途:

  1. 某些 COM 方法可能需要特定类型的值, 这些值在 AutoHotkey 中没有直接等效的值. 此函数允许在将值传递给 COM 方法时指定值的类型. 例如, ComValue(0xB, -1) 创建一个表示 COM 布尔值为 true 的对象.
  2. 通过封装 COM 对象或 SafeArray, 脚本可以使用对象语法更自然地与其交互. 但是, 大多数脚本不需要手动执行此操作, 因为封装对象是由 ComObject, ComObjArray, ComObjActive, ComObjGet 和任何返回对象的 COM 方法自动创建的.

Ptr

如果一个封装对象的 VarType 是 VT_UNKNOWN(13) 或包含 VT_BYREF(0x4000) 或 VT_ARRAY(0x2000) 标志, 则 Ptr 属性可用于检索对象, 类型化变量或安全数组的地址. 这允许将 ComObject 本身传递给任何具有 "Ptr" 类型的 DllCallComCall 参数, 但也可以显式地使用. 例如, 在这些情况下, ComObj.Ptr 等同于 ComObjValue(ComObj).

如果一个封装对象的 VarType 是 VT_UNKNOWN(13) 或 VT_DISPATCH(9) 并且封装指针为 null(0), 则可以使用 Ptr 属性来检索当前的 null 值, 或者为封装对象赋值一个指针. 一旦被赋值(如果非-null), 当封装对象被释放时, 指针将被自动释放. 这可以与类型为 "Ptr*""PtrP"DllCallComCall 输出参数一起使用, 以确保指针会被自动释放, 例如当发生错误时. 有关示例, 请参阅 ComObjQuery.

如果一个包装器对象的 VarType 是 VT_DISPATCH(9) 并且为 null(0) 指针值赋值为非-null 指针值, 其类型从 ComValue 变为 ComObject. 包装对象的属性和方法变得可用, 而 Ptr 属性变为不可用.

ByRef

如果包装器对象的 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).

可以使用 ComObjType 检索包装器对象的 VarType.

可以使用 ComObjValue 检索包装器对象的 Value.

已知限制: 每次包装 COM 对象时, 都会创建新的包装器对象. 如 obj1 == obj2array[obj1] := value 这样的比较和赋值运算, 将这两个包装器对象视为唯一的, 即使它们包含相同的变体类型和值.

ComObjFromPtr, ComObject, ComObjGet, ComObjConnect, ComObjFlags, ObjAddRef/ObjRelease, ComObjQuery, GetActiveObject (MSDN)

示例

传递 VARIANT ByRef 给 COM 函数.

; 条件 - ScriptControl 需 32 位版本的 AutoHotkey.
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 Type != 0xC
            NumPut "ushort", vType, this.var
    }
    __item {
        get => this.ref[]
        set => this.ref[] := value
    }
    __delete() {
        DllCall("oleaut32\VariantClear", "ptr", this.var)
    }
}
unixetc