How to use struct pointer from C in Python?

Question:

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
Asked By: Marco Eckstein

||

Answers:

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
Answered By: Mark Tolonen

Basically this other answer, but closer to the question:

import ctypes

class Root(ctypes.Structure):
    _fields_ = [("foo", ctypes.c_bool)]

class Kotlin(ctypes.Structure):
    _fields_ = [("root", Root)]

class Libnative_ExportedSymbols(ctypes.Structure):
    _fields_ = [("kotlin", Kotlin)]

libPath = "path/to/libnative.dll"
dll = ctypes.CDLL(libPath)
dll.libnative_symbols.restype = ctypes.POINTER(Libnative_ExportedSymbols)
libnative = dll.libnative_symbols().contents
print(type(libnative.kotlin.root)) # <class '__main__.Root'>
print(libnative.kotlin.root) # <__main__.Root object at ...

Note that the following approach without POINTER would also work, but may cause errors down the road:

dll.libnative_symbols.restype = Libnative_ExportedSymbols
libnative = dll.libnative_symbols()
print(type(libnative.kotlin.root)) # <class '__main__.Root'>
print(libnative.kotlin.root) # <__main__.Root object at ...
Answered By: Marco Eckstein
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.