Search code examples
pythonctypeskotlin-native

How to use struct pointer from C in Python?


This is a snippet from a C header for a Windows DLL that was generated by Kotlin Multiplatform/Native:

typedef struct {
  struct {
    struct {
      // ...
    } root;
  } kotlin;
} libnative_ExportedSymbols;
extern libnative_ExportedSymbols* libnative_symbols(void);

From C, you would access the struct root like this:

libnative_ExportedSymbols* lib = libnative_symbols();
lib->kotlin.root. // ...

How can I access it from Python? It seems that all official samples have been removed. So I tried this without success:

import ctypes

libPath = "path/to/libnative.dll"
dll = ctypes.CDLL(libPath)
pythonInt = dll.libnative_symbols()

print(type(pythonInt)) # <class 'int'>
print(pythonInt) # A number, e.g. 1190351680

CPointer = ctypes.POINTER(ctypes.c_long)
cLong = ctypes.c_long(pythonInt)
cAddress = ctypes.addressof(cLong)
cPointer = ctypes.cast(cAddress, CPointer)

print(type(pythonInt) == type(cPointer.contents.value)) # true
print(pythonInt == cPointer.contents.value) # true
try:
    print(cPointer.kotlin.root)
except AttributeError:
    print("AttributeError")  # AttributeError
try:
    print(cPointer.contents.kotlin.root)
except AttributeError:
    print("AttributeError")  # AttributeError
try:
    print(cPointer.contents.value.kotlin.root)
except AttributeError:
    print("AttributeError")  # AttributeError

Solution

  • I don't know anything about Kotlin, but given the C structure described here's a C DLL with that structure and the Python code to call it. The structures need to be derived from ctypes.Structure and the return type of the function needs to be declared via .restype and not left to the default of (usually 32-bit) integer. Casting the return value after-the-fact is too late if it was meant to be a 64-bit pointer.

    test.c

    #ifdef _WIN32
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    typedef struct {
      struct {
        struct {
            int a;
        } root;
      } kotlin;
    } libnative_ExportedSymbols;
    
    libnative_ExportedSymbols g_lib = {7};
    
    API libnative_ExportedSymbols* libnative_symbols(void) {
        return &g_lib;
    }
    

    test.py

    import ctypes as ct
    
    class Root(ct.Structure):
        _fields_ = ('a', ct.c_int),
    
    class Kotlin(ct.Structure):
        _fields_ = ('root', Root),
    
    class Symbols(ct.Structure):
        _fields_ = ('kotlin', Kotlin),
    
    dll = ct.CDLL('./test')
    dll.libnative_symbols.argtypes = ()
    dll.libnative_symbols.restype = ct.POINTER(Symbols)
    
    lib = dll.libnative_symbols()
    print(lib.contents.kotlin.root.a)
    

    Output:

    7