# winim/com

Dark Mode

- Github Page
- winim
- winstr
- utils
- com
- clr

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]

    COMArray1D = 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