guides:machine_code:com

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
guides:machine_code:com [2025-03-26 16:21] – [Exporting IDispatch from MCode] Add body text geekguides:machine_code:com [2025-03-28 13:28] (current) – [Using COM with MCode] Link MCode guide geek
Line 1: Line 1:
 ====== Using COM with MCode ====== ====== Using COM with MCode ======
 +
 +Please read the basic [[guides:machine_code|MCode]] guide before this one!
 +
 +If you have not, consider referencing the [[guides:com:start|COM APIs]] guide to learn more about how COM works.
  
 When building integrations for AutoHotkey using MCode, it is often convenient to be able to work with COM objects. Either consuming COM objects in your MCode, or exporting COM objects from your MCode so they can be consumed by AHK. When building integrations for AutoHotkey using MCode, it is often convenient to be able to work with COM objects. Either consuming COM objects in your MCode, or exporting COM objects from your MCode so they can be consumed by AHK.
Line 467: Line 471:
 MsgBox MyObj.FirstAddend " + " MyObj.SecondAddend " = " MyObj.GetSum() MsgBox MyObj.FirstAddend " + " MyObj.SecondAddend " = " MyObj.GetSum()
 </runner> </runner>
 +
 +===== Consuming AHK Objects from MCode =====
 +
 +AutoHotkey objects are, at their basic, IDispatch objects. Just like we can create IDispatch objects using basic C code, we can interact with IDispatch objects using basic C code.
 +
 +<runner ahk2>
 +#Requires AutoHotkey v2.0
 +#include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +#include <stdlib.h>
 +#include <oaidl.h>
 +
 +MCL_IMPORT(HRESULT, OleAut32, VariantChangeType, (VARIANT*, const VARIANT*, USHORT, VARTYPE));
 +
 +static IID IID_NULL = { 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
 +
 +MCL_EXPORT(SumValues, Ptr, Cdecl_Int);
 +int SumValues(IDispatch* pObj) {
 +    // Get the ID of the "Length" property
 +    DISPID dispidLength = DISPID_UNKNOWN;
 +    LPOLESTR strLength = L"Length";
 +    pObj->lpVtbl->GetIDsOfNames(pObj, &IID_NULL, &strLength, 1, 0, &dispidLength);
 +    if (dispidLength == DISPID_UNKNOWN) {
 +        return 0;
 +    }
 +
 +    // Invoke the "Length" property
 +    DISPPARAMS dpLength = { .cArgs = 0, .cNamedArgs = 0 };
 +    VARIANT vLength = { .vt = VT_EMPTY };
 +    if (S_OK != pObj->lpVtbl->Invoke(
 +        pObj, dispidLength, NULL, 0, DISPATCH_PROPERTYGET,
 +        &dpLength, &vLength, NULL, NULL) ||
 +        vLength.vt != VT_I4) {
 +        return 0;
 +    }
 +
 +    // Loop from i = 0 to Length
 +    int out = 0;
 +    for (int i = 0; i < vLength.lVal; i++) {
 +        // Invoke the value property, with argument `i+1`. This is equivalent
 +        // to `object[i+1]`
 +        VARIANT vargGet = { .vt = VT_I4, .lVal = i + 1 };
 +        DISPPARAMS dpGet = { .cArgs = 1, .cNamedArgs = 0, .rgvarg = &vargGet };
 +        VARIANT vGet = { .vt = VT_EMPTY };
 +        if (S_OK != pObj->lpVtbl->Invoke(
 +            pObj, DISPID_VALUE, NULL, 0, DISPATCH_PROPERTYGET,
 +            &dpGet, &vGet, NULL, NULL)) {
 +            return 0;
 +        }
 +
 +        // Coerce to 4-byte integer (LONG)
 +        if (S_OK != VariantChangeType(&vGet, &vGet, 0, VT_I4)) {
 +            return 0;
 +        }
 +
 +        out += vGet.lVal;
 +    }
 +
 +    return out;
 +}
 +)")
 +
 +input := [1, 2, 3]
 +MsgBox lib.SumValues(ObjPtr(input))
 +</runner>
 +
 +==== Implementing QuickSort ====
 +
 +By invoking ''DISPID_VALUE'' with ''DISPATCH_PROPERTYGET'' and ''DISPATCH_PROPERTYPUT'', we can get and set values from an object's item store. This allows us to implement a basic Quicksort algorithm that applies to a provided array:
 +
 +<runner ahk2>
 +#Requires AutoHotkey v2.0
 +#include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +#include <stdlib.h>
 +#include <oaidl.h>
 +
 +#define E_FAIL 0x80004005
 +
 +MCL_IMPORT(HRESULT, OleAut32, VarCmp, (VARIANT* left, VARIANT* right, DWORD lcid, ULONG dwFlags));
 +MCL_IMPORT(HRESULT, OleAut32, VariantClear, (VARIANT* pvarg));
 +
 +HRESULT GetItem(IDispatch* array, ULONG index, VARIANT* pvarResult) {
 +    VARIANT vargGet = { .vt = VT_I4, .lVal = index };
 +    DISPPARAMS dpGet = { .cArgs = 1, .cNamedArgs = 0, .rgvarg = &vargGet };
 +    return array->lpVtbl->Invoke(
 +        array, DISPID_VALUE, NULL, 0, DISPATCH_PROPERTYGET,
 +        &dpGet, pvarResult, NULL, NULL);
 +}
 +
 +HRESULT PutItem(IDispatch* array, ULONG index, VARIANT* pvarItem) {
 +    VARIANT vargIndex = { .vt = VT_I4, .lVal = index };
 +    VARIANT rgvargArgs[] = { *pvarItem, vargIndex };
 +    DISPPARAMS dpPut = { .cArgs = 2, .cNamedArgs = 0, .rgvarg = rgvargArgs };
 +    return array->lpVtbl->Invoke(
 +        array, DISPID_VALUE, NULL, 0, DISPATCH_PROPERTYPUT,
 +        &dpPut, NULL, NULL, NULL);
 +}
 +
 +HRESULT partition(IDispatch *array, ULONG lo, ULONG hi, ULONG* newPartition) {
 +    HRESULT result = E_FAIL;
 +
 +    VARIANT varHi = { .vt = VT_EMPTY };
 +    VARIANT varI = { .vt = VT_EMPTY };
 +    VARIANT varJ = { .vt = VT_EMPTY };
 +
 +    // Use highest index value as pivot
 +    if (S_OK != GetItem(array, hi, &varHi)) goto fail;
 +
 +    ULONG i = lo;
 +    for (ULONG j = lo; j < hi; j++) {
 +        if (S_OK != GetItem(array, j, &varJ)) goto fail;
 +        // lcid = LOCALE_INVARIANT, dwFlags = NORM_IGNORECASE
 +        if (
 +            (varJ.vt != varHi.vt) ? (varJ.vt <= varHi.vt) :
 +            VarCmp(&varJ, &varHi, 0x7F, 0x1) < 2) {
 +            if (S_OK != GetItem(array, i, &varI)) goto fail;
 +            if (S_OK != PutItem(array, i, &varJ)) goto fail;
 +            if (S_OK != PutItem(array, j, &varI)) goto fail;
 +            i = i + 1;
 +            VariantClear(&varI);
 +        }
 +        VariantClear(&varJ);
 +    }
 +
 +    // Center the pivot value
 +    if (S_OK != GetItem(array, i, &varI)) goto fail;
 +    if (S_OK != PutItem(array, i, &varHi)) goto fail;
 +    if (S_OK != PutItem(array, hi, &varI)) goto fail;
 +
 +    *newPartition = i;
 +    result = S_OK;
 +
 +    fail:
 +    VariantClear(&varHi);
 +    VariantClear(&varI);
 +    VariantClear(&varJ);
 +    return result;
 +}
 +
 +MCL_EXPORT(quicksort, Ptr, array, UInt, lo, UInt, hi, CDecl_Int);
 +HRESULT quicksort(IDispatch *array, ULONG lo, ULONG hi) {
 +    if (lo >= hi) return S_OK;
 +    ULONG p = 0;
 +    if (S_OK != partition(array, lo, hi, &p)) return E_FAIL;
 +    if (S_OK != quicksort(array, lo, p - 1)) return E_FAIL;
 +    if (S_OK != quicksort(array, p + 1, hi)) return E_FAIL;
 +    return S_OK;
 +}
 +
 +)")
 +
 +x := ["c", 6, 3.0, 3, 8, "b", 9, 2.0, 4, 5, "a", 1,2, 1.0, 7]
 +
 +MsgBox "Before Sorting: " JSON.Dump(x)
 +
 +lib.quicksort(ObjPtr(x), 1, x.Length)
 +
 +MsgBox "After Sorting: " JSON.Dump(x)
 +</runner>
 +