ComValue

Wrappt einen Wert, ein SafeArray oder ein COM-Objekt, damit er/es vom Skript verwendet oder an eine COM-Methode übergeben werden kann.

ComObj := ComValue(VarTyp, Wert , Flags)

ComValue selbst ist eine Klasse, die von Any abgeleitet ist, aber nur zum Erstellen oder Identifizieren von COM-Wrapper-Objekten verwendet wird.

Parameter

VarTyp

Typ: Integer

Ein Integer, der den Typ des Wertes angibt. Eine Auflistung aller Typennummern finden Sie unter ComObjType.

Wert

Typ: Beliebig

Ein Wert, der gewrappt werden soll.

Wenn dies ein reiner Integer ist und wenn VarTyp nicht VT_R4, VT_R8, VT_DATE oder VT_CY ist, wird dessen Wert direkt verwendet; insbesondere können VT_BSTR, VT_DISPATCH und VT_UNKNOWN mit einem Pointer-Wert initialisiert werden.

In allen anderen Fällen wird der Wert in eine temporäre VARIANT kopiert, wobei dieselben Regeln wie bei normalen COM-Methodenaufrufen gelten. Wenn der Quellvariantentyp ungleich VarTyp ist, wird die Umwandlung durch Aufruf von VariantChangeType mit einem wFlags-Wert von 0 versucht. Es wird eine Ausnahme ausgelöst, wenn die Umwandlung scheitert.

Flags

Typ: Integer

Flags haben Einfluss auf das Verhalten des Wrapper-Objekts; siehe ComObjFlags für weitere Informationen.

Rückgabewert

Typ: Objekt

Diese Funktion gibt ein Wrapper-Objekt zurück, das einen Variantentyp und einen Wert oder Pointer enthält, also ComValue, ComValueRef, ComObjArray oder ComObject.

Dieses Objekt kann vielseitig verwendet werden:

  1. Einige COM-Methoden können bestimmte Wertetypen benötigen, die kein direktes Äquivalent in AutoHotkey haben. Diese Funktion ermöglicht es, den Typ eines Wertes anzugeben, wenn er an eine COM-Methode übergeben wird. Zum Beispiel erstellt ComValue(0xB, true) ein Objekt, das den booleschen COM-Wert True repräsentiert.
  2. Das Wrappen eines COM-Objekts oder SafeArrays ermöglicht dem Skript eine natürlichere Interaktion mit diesem mittels Objektsyntax. Die meisten Skripte müssen dies jedoch nicht manuell tun, da ComObject, ComObjArray, ComObjActive, ComObjGet und andere COM-Methoden, die ein Objekt zurückgeben, automatisch ein Wrapper-Objekt erstellen.
  3. Durch das Wrappen eines COM-Interface-Pointers kann das Skript von der automatischen Referenzzählung profitieren. Ein Interface-Pointer kann sofort gewrappt werden, nachdem er an das Skript zurückgegeben wurde (typischerweise von ComCall oder DllCall), wodurch die Notwendigkeit einer expliziten Freigabe zu einem späteren Zeitpunkt vermieden wird.

Ptr

Wenn der VarTyp eines Wrapper-Objekts VT_UNKNOWN (13) ist oder den VT_BYREF- (0x4000) oder VT_ARRAY-Flag (0x2000) enthält, kann mit der Ptr-Eigenschaft die Adresse des Objekts, die typisierte Variable oder das SafeArray abgerufen werden. Auf diese Weise kann das ComObject selbst an einen DllCall- oder ComCall-Parameter vom Typ "Ptr" übergeben oder explizit verwendet werden. Zum Beispiel ist ComObj.Ptr in solchen Fällen äquivalent zu ComObjValue(ComObj).

Wenn der VarTyp eines Wrapper-Objekts VT_UNKNOWN (13) oder VT_DISPATCH (9) ist und der gewrappte Pointer Null (0) ist, kann mit der Ptr-Eigenschaft der aktuelle Null-Wert abgerufen oder dem Wrapper-Objekt ein Pointer zugewiesen werden. Wenn der Pointer einmal zugewiesen wurde (falls ungleich Null), wird er beim Freigeben des Wrapper-Objekts automatisch freigegeben. Dies kann zusammen mit DllCall- oder ComCall-Ausgabeparametern vom Typ "Ptr*" oder "PtrP" verwendet werden, um sicherzustellen, dass der Pointer automatisch freigegeben wird, z.B. wenn ein Fehler auftritt. Ein Beispiel dazu finden Sie unter ComObjQuery.

Wenn einem Wrapper-Objekt mit VarTyp VT_DISPATCH (9) und einem Null-Pointer (0) ein Pointer-Wert ungleich Null zugewiesen wird, ändert sich dessen Typ von ComValue zu ComObject. Die Eigenschaften und Methoden des gewrappten Objekts werden verfügbar, während die Ptr-Eigenschaft nicht mehr verfügbar ist.

ByRef

Wenn der VarTyp eines Wrapper-Objekts das VT_BYREF-Flag (0x4000) enthält, können leere eckige Klammern [] zum Lesen oder Schreiben des referenzierten Wertes verwendet werden.

Beim Erstellen einer Referenz muss Wert die Speicheradresse einer Variable oder eines Pufferspeichers mit ausreichender Kapazität zur Aufbewahrung eines Wertes bestimmten Typs sein. Das folgende Beispiel erstellt eine Variable, die von einer VBScript-Funktion beschrieben werden kann:

vbuf := Buffer(24, 0)
vref := ComValue(0x400C, vbuf.ptr)  ; 0x400C ist eine Kombination von VT_BYREF und VT_VARIANT.

vref[] := "Eingabewert"
sc.Run("Beispiel", vref)  ; sc sollte wie im Beispiel unten initialisiert werden.
MsgBox vref[]

Beachten Sie, dass, obwohl alle vorherigen Werte freigegeben werden, wenn ein neuer Wert mittels vref[] oder der COM-Methode zugewiesen wird, der finale Wert nicht automatisch freigegeben wird. Um den Wert freizugeben, muss man wissen, um welchen Typ es sich handelt. Da es sich in diesem Fall um VT_VARIANT handelt, kann der Wert durch Aufruf von VariantClear mit DllCall oder durch Nutzung einer einfacheren Methode freigegeben werden: weisen Sie einen Integer zu, z.B. vref[] := 0.

Wenn die Methode eine Kombination von VT_BYREF und VT_VARIANT akzeptiert, wie oben gezeigt, kann stattdessen eine VarRef verwendet werden. Zum Beispiel:

eine_var := "Eingabewert"
sc.Run("Beispiel", &eine_var)
MsgBox eine_var

Einige Methoden erfordern jedoch einen spezifischeren Variantentyp, wie z.B. VT_BYREF | VT_I4. In solchen Fällen muss die erste oben gezeigte Vorgehensweise verwendet und 0x400C mit dem entsprechenden Variantentyp ersetzt werden.

Allgemeine Bemerkungen

Wenn diese Funktion zum Wrappen eines IDispatch- oder IUnknown-Interface-Pointers (wenn als Integer übergeben) verwendet wird, ist das Wrapper-Objekt für die Freigabe des Pointers verantwortlich, falls erforderlich. Wenn das Skript also den Pointer nach dem Aufruf dieser Funktion weiter verwenden will, muss es zuerst ObjAddRef(DispPtr) aufrufen. Dies ist jedoch nicht notwendig, wenn Wert selbst ein ComValue oder ComObject ist.

Eine Umwandlung von VT_UNKNOWN nach VT_DISPATCH führt dazu, dass IUnknown::QueryInterface aufgerufen wird und dabei ein Interface-Pointer erzeugt werden kann, der vom Original abweicht, und dass eine Ausnahme ausgelöst wird, wenn das Objekt IDispatch nicht implementiert. Wenn dagegen Wert ein Integer und VarTyp VT_DISPATCH ist, wird der Wert direkt verwendet, d.h. der Wert muss ein IDispatch-kompatibler Interface-Pointer sein.

Der VarTyp eines Wrapper-Objekts kann mit ComObjType abgerufen werden.

Der Wert eines Wrapper-Objekts kann mit ComObjValue abgerufen werden.

Bekannte Einschränkung: Jedes Mal, wenn ein COM-Objekt gewrappt wird, wird ein neues Wrapper-Objekt erstellt. Vergleiche und Zuweisungen wie obj1 == obj2 und arr[obj1] := Wert behandeln die beiden Wrapper-Objekte als nicht identisch, auch wenn sie denselben Variantentyp und Wert enthalten.

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

Beispiele

Übergibt eine VARIANT ByRef an eine COM-Funktion.

#Requires AutoHotkey v2 32-bit ; 32-Bit für ScriptControl.
code := "
(
Sub Beispiel(Var)
    MsgBox Var
    Var = "Ausgabewert!"
End Sub
)"
sc := ComObject("ScriptControl"), sc.Language := "VBScript", sc.AddCode(code)


; Beispiel: Eine VARIANT ByRef an eine COM-Methode übergeben.
var := ComVar()
var[] := "Eingabewert"
sc.Run("Beispiel", var.ref)
MsgBox var[]

; Dasselbe noch einmal, aber direkter:
variant_buf := Buffer(24, 0)  ; Einen Puffer anlegen, der groß genug für ein VARIANT ist.
var := ComValue(0x400C, variant_buf.ptr)  ; Referenz auf ein VARIANT machen.
var[] := "Eingabewert"
sc.Run("Beispiel", var)  ; VT_BYREF ComValue selbst übergeben, kein [] oder .ref.
MsgBox var[]
; Wenn ein VARIANT eine Zeichenkette oder ein Objekt enthält, muss es explizit freigegeben
; werden, indem VariantClear aufgerufen oder ein rein numerischer Wert zugewiesen wird:
var[] := 0

; Der einfachste Weg, wenn die Methode VT_BYREF|VT_VARIANT akzeptiert:
var := "Eingabewert"
sc.Run("Beispiel", &var)
MsgBox var


; ComVar: Ein Objekt, mit dem ein Wert ByRef übergeben werden kann.
;   this[] ruft den Wert ab.
;   this[] := Val setzt den Wert.
;   this.ref ruft ein ByRef-Objekt zur Übergabe an eine COM-Methode ab.
class ComVar {
    __new(vType := 0xC) {
        ; Speicher für ein VARIANT reservieren, damit unser Wert hineinpasst. VARIANT wird auch dann
        ; verwendet, wenn vType != VT_VARIANT, damit VariantClear von __delete verwendet werden kann.
        this.var := Buffer(24, 0)
        ; Ein Objekt erstellen, mit dem die Variable ByRef übergeben werden kann.
        this.ref := ComValue(0x4000|vType, this.var.ptr + (vType=0xC ? 0 : 8))
        ; Variantentyp für VariantClear speichern (wenn nicht 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)
    }
}