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