winim/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

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

Procs

proc `$`(x: variant): string {.inline, ...raises: [VariantConversionError],
                               tags: [].}
proc `<=`(x, y: COMBinary): bool {.borrow, ...raises: [], tags: [].}
proc `<`(x, y: COMBinary): bool {.borrow, ...raises: [], tags: [].}
proc `==`(x, y: COMBinary): bool {.borrow, ...raises: [], tags: [].}
proc `[]`(self: variant; name: string): variant {.
    ...raises: [Exception, VariantConversionError, COMError, COMException],
    tags: [RootEffect].}
proc call(self: com; name: string; vargs: varargs[variant, toVariant]): variant {.
    discardable, inline,
    ...raises: [Exception, COMError, VariantConversionError, COMException],
    tags: [RootEffect].}
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 cmp(x, y: COMBinary): int {.borrow, ...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 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 copy(x: com): com {.inline, ...raises: [Exception], tags: [RootEffect].}
proc copy(x: variant): variant {....raises: [VariantConversionError], tags: [].}
proc CreateObject(progId: string): com {....raises: [Exception, COMError],
    tags: [RootEffect].}
Creates a reference to a COM object.
proc del(x: com) {....raises: [Exception], tags: [RootEffect].}
proc del(x: variant) {....raises: [], tags: [].}
proc desc(e: ref COMError): string {....raises: [], tags: [].}
proc desc(self: com; name: string): string {....raises: [Exception, COMError],
    tags: [RootEffect].}
Gets the description (include name and arguments) for the specified method.
proc disconnect(self: com; cookie: DWORD; riid: REFIID = nil): bool {.
    discardable, ...raises: [Exception, COMError], tags: [RootEffect].}
Disconnect a COM object from a comEventHandler.
proc fromVariant[T](x: variant): T
proc get(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 getCurrentCOMError(): ref COMError {.inline, ...raises: [], tags: [].}
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 high(s: COMBinary): int {.borrow, ...raises: [], tags: [].}
proc isNull(x: variant): bool {.inline, ...raises: [], tags: [].}
proc len(x: COMBinary): int {.borrow, ...raises: [], tags: [].}
proc low(s: COMBinary): int {.borrow, ...raises: [], tags: [].}
proc newCom(file, progId: string): com {.inline, ...raises: [Exception, COMError],
    tags: [RootEffect].}
proc newCom(progId: string): com {.inline, ...raises: [Exception, COMError],
                                   tags: [RootEffect].}
proc newCom(x: ptr IDispatch): com {....raises: [Exception], tags: [RootEffect].}
proc newVariant(x: VARIANT): variant {....raises: [VariantConversionError],
                                       tags: [].}
proc rawType(x: variant): VARTYPE {.inline, ...raises: [], tags: [].}
proc rawTypeDesc(x: variant): string {....raises: [], tags: [].}
proc set(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 setRef(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 substr(s: COMBinary; first = 0): COMBinary {.borrow, ...raises: [], tags: [].}
proc substr(s: COMBinary; first, last: int): COMBinary {.borrow, ...raises: [],
    tags: [].}
proc toVariant(x: bool): variant {....raises: [], tags: [].}
proc toVariant(x: BSTR): variant {....raises: [], tags: [].}
proc toVariant(x: char): variant {....raises: [], tags: [].}
proc toVariant(x: com): variant {....raises: [Exception], tags: [RootEffect].}
proc toVariant(x: COMBinary): variant {....raises: [VariantConversionError],
                                        tags: [].}
proc toVariant(x: FILETIME): variant {....raises: [VariantConversionError],
                                       tags: [].}
proc toVariant(x: pointer): variant {....raises: [], tags: [].}
proc toVariant(x: ptr IDispatch): variant {....raises: [Exception],
    tags: [RootEffect].}
proc toVariant(x: ptr IUnknown): variant {....raises: [Exception],
    tags: [RootEffect].}
proc toVariant(x: ptr SomeInteger | ptr SomeFloat | ptr char | ptr bool |
    ptr BSTR): variant
proc toVariant(x: SomeFloat): variant
proc toVariant(x: SomeInteger | enum): variant
proc toVariant(x: string | cstring | mstring): variant
proc toVariant(x: SYSTEMTIME): variant {....raises: [VariantConversionError],
    tags: [].}
proc toVariant(x: VARIANT): variant {....raises: [VariantConversionError], tags: [].}
proc toVariant(x: variant): variant {....raises: [VariantConversionError], tags: [].}
proc toVariant(x: wstring): variant {....raises: [], tags: [].}
proc toVariant[T](x: openArray[T]; vt: VARENUM = VT_VARIANT): variant
proc unwrap(x: com): ptr IDispatch {.inline, ...raises: [], tags: [].}
proc unwrap(x: variant): VARIANT {.inline, ...raises: [], tags: [].}
proc wrap(x: ptr IDispatch): com {.inline, ...raises: [Exception],
                                   tags: [RootEffect].}
proc wrap(x: VARIANT): variant {.inline, ...raises: [], tags: [].}

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 variantConverterToBool(x: variant): bool {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToChar(x: variant): char {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToCom(x: variant): com {.
    ...raises: [VariantConversionError, Exception], tags: [RootEffect].}
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: [].}
converter variantConverterToCString(x: variant): cstring {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToFILETIME(x: variant): FILETIME {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToFloat32(x: variant): float32 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToFloat64(x: variant): float64 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToIDispatch(x: variant): ptr IDispatch {.
    ...raises: [VariantConversionError, Exception], tags: [RootEffect].}
converter variantConverterToInt(x: variant): int {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToInt8(x: variant): int8 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToInt16(x: variant): int16 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToInt32(x: variant): int32 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToInt64(x: variant): int64 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToIUnknown(x: variant): ptr IUnknown {.
    ...raises: [VariantConversionError, Exception], tags: [RootEffect].}
converter variantConverterToMString(x: variant): mstring {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToPointer(x: variant): pointer {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToString(x: variant): string {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToSYSTEMTIME(x: variant): SYSTEMTIME {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt(x: variant): uint {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt8(x: variant): uint8 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt16(x: variant): uint16 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt32(x: variant): uint32 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToUInt64(x: variant): uint64 {.
    ...raises: [VariantConversionError], tags: [].}
converter variantConverterToVARIANT(x: variant): VARIANT {....raises: [], tags: [].}
converter variantConverterToWString(x: variant): wstring {.
    ...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: variant; name: untyped): variant
template `[]=`(self: com; name: string; v: untyped)
template `[]`(self: com; name: string): variant