com

    Dark Mode

This module add windows COM support to Winim. So that we can use Nim to interact with COM object like a script language. For example:

comScript:
  var dict = CreateObject("Scripting.Dictionary")
  dict.add("a", "the")
  dict.add("b", item:="quick")
  dict.add(item:="fox", key:="c")
  dict.item(key:="c") = "dog"
  for key in dict:
    echo key, " => ", dict.item(key)

This module introduce two new types to deal with COM objects: "com" and "variant". In summary, CreateObject() or GetObject() returned a "com" type value, and any input/ouput of COM method should be a "variant" type value.

Most Nim's data type and Winim's string type can convert to/from "variant" type value. The conversion is usually done automatically. However, specific conversion is aslo welcome.

proc toVariant[T](x: T): variant
proc fromVariant[T](x: variant): T
  
  # Supported type:
  #   char|string|cstring|mstring|wstring|BSTR
  #   bool|enum|SomeInteger|SomeReal
  #   com|variant|VARIANT|ptr IUnknown|ptr IDispatch|pointer
  #   SYSTEMTIME|FILETIME
  #   1D~3D array|seq|COMBinary

COMBinary type can help to deal with binary data. For example:

var input = "binary\0string\0test\0"
var v = toVariant(COMBinary input)
var output = string fromVariant[COMBinary](v)
assert input == output

Types

COMError = object of CatchableError
  hresult*: HRESULT
COMException = object of COMError
VariantConversionError = object of ValueError
com = ref object
  disp: ptr IDispatch
variant = ref object
  raw: VARIANT
COMArray = seq[variant]
COMArray1D = seq[variant]
COMArray2D = seq[seq[variant]]
COMArray3D = seq[seq[seq[variant]]]
COMBinary = distinct string
comEventHandler = proc (self: com; name: string; params: varargs[variant]): variant

Procs

proc len(x: COMBinary): int {...}{.borrow.}
proc high(s: COMBinary): int {...}{.borrow.}
proc low(s: COMBinary): int {...}{.borrow.}
proc cmp(x, y: COMBinary): int {...}{.borrow.}
proc `==`(x, y: COMBinary): bool {...}{.borrow.}
proc `<=`(x, y: COMBinary): bool {...}{.borrow.}
proc `<`(x, y: COMBinary): bool {...}{.borrow.}
proc substr(s: COMBinary; first, last: int): COMBinary {...}{.borrow.}
proc substr(s: COMBinary; first = 0): COMBinary {...}{.borrow.}
proc getCurrentCOMError(): ref COMError {...}{.inline, raises: [], tags: [].}
proc desc(e: ref COMError): string {...}{.raises: [], tags: [].}
proc del(x: com) {...}{.raises: [Exception], tags: [RootEffect].}
proc del(x: variant) {...}{.raises: [], tags: [].}
proc COM_FullRelease() {...}{.raises: [Exception], tags: [RootEffect].}

Clean up all COM objects and variants.

Usually, we let garbage collector to release the objects. However, sometimes the garbage collector can't release all the object even we call GC_fullCollect(). Some object will create a endless process in this situation. (for example: Excel.Application). So we need this function.

Use -d:notrace to disable this function.

proc rawType(x: variant): VARTYPE {...}{.inline, raises: [], tags: [].}
proc rawTypeDesc(x: variant): string {...}{.raises: [], tags: [].}
proc newCom(x: ptr IDispatch): com {...}{.raises: [Exception], tags: [RootEffect].}
proc copy(x: com): com {...}{.inline, raises: [Exception], tags: [RootEffect].}
proc wrap(x: ptr IDispatch): com {...}{.inline, raises: [Exception], tags: [RootEffect].}
proc unwrap(x: com): ptr IDispatch {...}{.inline, raises: [], tags: [].}
proc unwrap(x: variant): VARIANT {...}{.inline, raises: [], tags: [].}
proc isNull(x: variant): bool {...}{.inline, raises: [], tags: [].}
proc newVariant(x: VARIANT): variant {...}{.raises: [VariantConversionError], tags: [].}
proc copy(x: variant): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant(x: string | cstring | mstring): variant
proc toVariant(x: wstring): variant {...}{.raises: [], tags: [].}
proc toVariant(x: BSTR): variant {...}{.raises: [], tags: [].}
proc toVariant(x: bool): variant {...}{.raises: [], tags: [].}
proc toVariant(x: SomeInteger | enum): variant
proc toVariant(x: SomeFloat): variant
proc toVariant(x: char): variant {...}{.raises: [], tags: [].}
proc toVariant(x: pointer): variant {...}{.raises: [], tags: [].}
proc toVariant(x: ptr IDispatch): variant {...}{.raises: [Exception], tags: [RootEffect].}
proc toVariant(x: com): variant {...}{.raises: [Exception], tags: [RootEffect].}
proc toVariant(x: ptr IUnknown): variant {...}{.raises: [Exception], tags: [RootEffect].}
proc toVariant(x: SYSTEMTIME): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant(x: FILETIME): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant(x: ptr SomeInteger | ptr SomeFloat | ptr char | ptr bool | ptr BSTR): variant
proc toVariant(x: VARIANT): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant(x: variant): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant(x: COMBinary): variant {...}{.raises: [VariantConversionError], tags: [].}
proc toVariant[T](x: openArray[T]; vt: VARENUM = VT_VARIANT): variant
proc fromVariant[T](x: variant): T
proc `$`(x: variant): string {...}{.inline, raises: [VariantConversionError], tags: [].}
proc desc(self: com; name: string): string {...}{.raises: [Exception, COMError],
                                       tags: [RootEffect].}
Gets the description (include name and arguments) for the specific method.
proc call(self: com; name: string; vargs: varargs[variant, toVariant];
         kwargs: openArray[(string, variant)] = []): variant {...}{.discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc call(self: com; name: string; vargs: varargs[variant, toVariant]): variant {...}{.
    discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc set(self: com; name: string; vargs: varargs[variant, toVariant];
        kwargs: openArray[(string, variant)]): variant {...}{.discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc set(self: com; name: string; vargs: varargs[variant, toVariant]): variant {...}{.
    discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc setRef(self: com; name: string; vargs: varargs[variant, toVariant];
           kwargs: openArray[(string, variant)]): variant {...}{.discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc setRef(self: com; name: string; vargs: varargs[variant, toVariant]): variant {...}{.
    discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc get(self: com; name: string; vargs: varargs[variant, toVariant];
        kwargs: openArray[(string, variant)]): variant {...}{.discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc get(self: com; name: string; vargs: varargs[variant, toVariant]): variant {...}{.
    discardable, inline,
    raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
proc `[]`(self: variant; name: string): variant {...}{.
    raises: [Exception, VariantConversionError, COMError, COMException],
    tags: [RootEffect].}
proc CreateObject(progId: string): com {...}{.raises: [Exception, COMError],
                                     tags: [RootEffect].}
Creates a reference to a COM object.
proc GetObject(file: string; progId: string = ""): com {...}{.raises: [Exception, COMError],
    tags: [RootEffect].}
Retrieves a reference to a COM object from an existing process or filename.
proc newCom(progId: string): com {...}{.inline, raises: [Exception, COMError],
                               tags: [RootEffect].}
proc newCom(file, progId: string): com {...}{.inline, raises: [Exception, COMError],
                                    tags: [RootEffect].}
proc connect(self: com; handler: comEventHandler; riid: REFIID = nil): DWORD {...}{.
    discardable, raises: [Exception, COMError], tags: [RootEffect].}
Connect a COM object to a comEventHandler. Return a cookie to disconnect (if needed). Handler is a user defined proc to receive the COM event. comEventHandler is defined as:
type comEventHandler = proc(self: com, name: string, params: varargs[variant]): variant
proc disconnect(self: com; cookie: DWORD; riid: REFIID = nil): bool {...}{.discardable,
    raises: [Exception, COMError], tags: [RootEffect].}
Disconnect a COM object from a comEventHandler.

Iterators

iterator items(x: com): variant {...}{.raises: [Exception, COMError, VariantConversionError],
                              tags: [RootEffect].}
iterator items(x: variant): variant {...}{.raises: [Exception, VariantConversionError,
    COMError], tags: [RootEffect].}

Converters

converter variantConverterToString(x: variant): string {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToCString(x: variant): cstring {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToMString(x: variant): mstring {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToWString(x: variant): wstring {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToChar(x: variant): char {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToBool(x: variant): bool {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToCom(x: variant): com {...}{.
    raises: [VariantConversionError, Exception], tags: [RootEffect].}
converter variantConverterToIDispatch(x: variant): ptr IDispatch {...}{.
    raises: [VariantConversionError, Exception], tags: [RootEffect].}
converter variantConverterToIUnknown(x: variant): ptr IUnknown {...}{.
    raises: [VariantConversionError, Exception], tags: [RootEffect].}
converter variantConverterToPointer(x: variant): pointer {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToInt(x: variant): int {...}{.raises: [VariantConversionError],
    tags: [].}
converter variantConverterToUInt(x: variant): uint {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToInt8(x: variant): int8 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt8(x: variant): uint8 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToInt16(x: variant): int16 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt16(x: variant): uint16 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToInt32(x: variant): int32 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt32(x: variant): uint32 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToInt64(x: variant): int64 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt64(x: variant): uint64 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToFloat32(x: variant): float32 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToFloat64(x: variant): float64 {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToFILETIME(x: variant): FILETIME {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToSYSTEMTIME(x: variant): SYSTEMTIME {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToVARIANT(x: variant): VARIANT {...}{.raises: [], tags: [].}
converter variantConverterToCOMArray1D(x: variant): COMArray1D {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToCOMArray2D(x: variant): COMArray2D {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToCOMArray3D(x: variant): COMArray3D {...}{.
    raises: [VariantConversionError], tags: [].}
converter variantConverterToCOMBinary(x: variant): COMBinary {...}{.
    raises: [VariantConversionError], tags: [].}

Macros

macro `.`(self: com; name: untyped; vargs: varargs[untyped]): untyped
macro `.=`(self: com; name: untyped; vargs: varargs[untyped]): untyped
macro comScript(x: untyped): untyped
Nim's dot operators .= only allow "a.b = c". With this macro, "a.b(c, d) = e" is allowed. Some assignment needs this macro to work. Moreover, this macro also translates named arguments to table constructor syntax which methods/properties related functions can accept (here we use := as assignment to avoid syntax conflict). For example:
comScript:
  dict.item("c") = "dog"
  dict.add(item:="fox", key:="c")
  excel.activeSheet.cells(1, 1) = "text"
  excel.activeSheet.range(cell1:="A1") = "text"

Templates

template `[]`(self: com; name: string): variant
template `[]=`(self: com; name: string; v: untyped)
template `.`(self: variant; name: untyped): variant