Hi, I am trying to use a certain COM interface (ITextHost) from Nim. Under the hood, COM is basically C++ objects, so I defined a type for the vtable, added a pointer to the vtable to my object type, and I can pass a pointer to my object to the API. It works as expected - however only under 64 bit Windows. When I compile it for 32 bit, it crashes in strange places with a segfault. To me, it looks like a stack corruption. (I got to use 32 bit, because I want to load a certain 32 bit DLL.)
I've checked that all types I use have the correct size, by comparing them with a plain C++ program. The remaining thing I can think of is the calling convention - there is basically only one on x64, but multiple incompatible ones on x86.
Normally, COM uses stdcall. However, ITextHost strangely uses the C++ default, which I think is thiscall - basically cdecl, but a pointer to this is placed in ECX. Is there any way to use thiscall?
Here is some code so you get an idea what I'm trying to do:
type
ITextHostVtable {.pure.} = object of IUnknownVtable
## Get the DC for the host
TxGetDC: proc(this: ptr ITextHost, ): HDC {.cdecl.}
## Release the DC gotten from the host
TxReleaseDC: proc(this: ptr ITextHost, hdc: HDC): INT {.cdecl.}
## ...
ITextHost {.pure, inheritable.} = object
vtable: ptr ITextHostVtable
This is a perfect job for Nim macros. You can use macros API to construct generated code or generate string of Nim code then parsed by parseStmt, or a mix of both.
With macros, you can generate not only type declaration, but also convenience wrapper at the same time.
Here is an incomplete example, you can study real life implementation from reference section
import macros
proc comImpl64(n: NimNode, methods: NimNode): NimNode {.used.} =
# put your 64 bit implementation here
result = newEmptyNode() # replace this with implementation result
proc comImpl32(n: NimNode, methods: NimNode): NimNode {.used.} =
# put your 32 bit implementation here
result = newEmptyNode() # replace this with implementation result
macro comMacro(n: untyped, methods: untyped): untyped =
when defined(cpu64):
result = comImpl64(n, methods)
else:
result = comImpl32(n, methods)
comMacro(ITextHost):
proc TxGetDC(): HDC
proc TxReleaseDC(hdc: HDC): INT
References: