ComValue

スクリプトで使用するため、またはCOMメソッドに渡すために、値、SafeArrayまたはCOMオブジェクトをラップします。

ComObj := ComValue(VarType, Value , Flags)

ComValue自体はAnyから派生したクラスですが、COMラッパーオブジェクトを作成または識別するためにのみ使用されます。

パラメータ

VarType

型:整数

値の種類を示す整数。タイプの一覧はComObjTypeをご覧ください。

Value

型:任意

ラップする値。

これが純粋な整数で、VarTypeが VT_R4、VT_R8、VT_DATE、VT_CY でない場合、その値が直接使われます。特に VT_BSTR、VT_DISPATCH、VT_UNKNOWN はポインタ値で初期化できる。

それ以外の場合は、通常のCOMメソッド呼び出しと同じルールで、一時的なVARIANTに値がコピーされます。ソースバリアントタイプがVarTypeと等しくない場合、wFlagsの値0を持つVariantChangeTypeを呼び出すことで変換が試みられます。変換に失敗した場合は、例外が発生します。

Flags

型:整数

ラッパーオブジェクトの動作に影響を与えるフラグ。詳細はComObjFlagsを参照してください。

戻り値

型:オブジェクト

This function returns a wrapper object containing a variant type and value or pointer, specifically ComValue, ComValueRef, ComObjArray or ComObject.

このオブジェクトは複数の用途:

  1. COMのメソッドの中には、AutoHotkey内で直接同等のものがない、特定のタイプの値を必要とするものがあります。COM メソッドに値を渡す際に、値の型を指定することができる機能です。例えば、ComValue(0xB, true)は、COM のブール値trueを表すオブジェクトを作成します。
  2. COMオブジェクトやSafeArrayをラッピングすることで、スクリプトがオブジェクトの構文を使用して、より自然に操作することができます。ただし、ComObjectComObjArrayComObjActiveComObjGet、およびオブジェクトを返すCOMメソッドでは、ラッパーオブジェクトが自動的に作成されるため、大半のスクリプトでは手動で行う必要はない。
  3. COMインターフェイスポインターをラップすることで、スクリプトが自動参照カウントを利用することができます。インターフェイスポインターは、スクリプトに返されるとすぐにラップされるため(通常はComCallまたはDllCallから)、後の時点で明示的にリリースする必要がありません。

Ptr

もし、ラッパーオブジェクトの VarTypeが VT_UNKNOWN (13) であるか、VT_BYREF (0x4000) フラグまたは VT_ARRAY (0x2000) フラグを含む場合、Ptrプロパティを使用してオブジェクト、型付き変数または SafeArray のアドレスを取得することができる。これにより、ComObject自体を"Ptr"型を持つDllCallComCallのパラメータに渡すことができ、また、明示的に使用することもできる。例えば、ComObj.Ptrは、これらの場合、ComObjValue(ComObj)と同等です。

もし、ラッパーオブジェクトの VarTypeがVT_UNKNOWN(13)またはVT_DISPATCH(9)で、ラップされたポインタがヌル(0)の場合、Ptrプロパティを使って現在のヌル値を取得するか、ラッパーオブジェクトへのポインタを割り当てることができる。一度割り当てられると(nullでない場合)、ラッパー・オブジェクトが解放されるときに、ポインタは自動的に解放されます。DllCallComCall"Ptr*""PtrP"型の出力パラメータで使用することで、エラー発生時などにポインタが自動的に解放されるようにすることができます。例として、ComObjQueryを参照してください。

VarTypeVT_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なので、DllCallVariantClearを呼び出すか、もっと簡単な方法で解放することができます: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互換のインターフェースポインタである必要があります。

ラッパーオブジェクトのVarTypeComObjTypeで取得することができます。

ラッパーオブジェクトのは、ComObjValueを使用して取得することができます。

既知の制限事項:COMオブジェクトがラップされるたびに、新しいラッパーオブジェクトが作成されます。obj1 == obj2arr[obj1] := valueのような比較や代入は、同じバリアント型や値を含んでいても、2つのラッパーオブジェクトをユニークなものとして扱います。

ComObjFromPtrComObjectComObjGetComObjConnectComObjFlagsObjAddRef/ObjReleaseComObjQueryGetActiveObject (Microsoft Docs)

COM関数にVARIANT ByRefを渡します。

; 前文 - ScriptControlはAutoHotkeyの32ビット版を必要とします。
code := "
(
Sub Example(Var)
    MsgBox Var
    Var = "out value!"
End Sub
)"
sc := ComObject("ScriptControl"), sc.Language := "VBScript", sc.AddCode(code)


; 例:COMメソッドにVARIANT ByRefを渡す。
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 は COM メソッドに渡すための ByRef オブジェクトを取得します。
class ComVar {
    __new(vType := 0xC) {
        ; VARIANTのメモリを確保し、値を保持します。VARIANTは、次のような場合でも使用されます。
        ; VariantClearを__deleteで使用できるように、vType != VT_VARIANTのとき ;。
        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)
    }
}