iouvxz
iouvxz

Reputation: 163

Python Failed to read a structure containing a char array using Python's ctypes

I wrote a tiny dll in C ,this is my .c file .

struct my_struct
{
    char arr[3];
};
__declspec(dllexport) struct my_struct func()
{
    struct my_struct m;
    m.arr[0] = 1;
    m.arr[1] = 2;
    m.arr[2] = 3;
    return m;
};
//compiled to testdll.dll

I tried to call the exported c function using python .This is my .py file.

from ctypes import *


class MyStruct(Structure):
    _fields_ = [("arr", c_char * 3)]


f = cdll.testdll.func
f.restype = MyStruct

for i in f().arr:
    print(i)

When I tried to read the array in the returned c struct ,I always got random values .

But if I use int arrays instead of char arrays in the .cpp and the .py files ,I can get right values as expected . Why?

Error when using ctypes module to acess a DLL written in C Related question here,I guess I should not return structs by value here ,because how structs are returned is implementation defined.

Upvotes: 3

Views: 937

Answers (2)

Aloy A Sen
Aloy A Sen

Reputation: 764

You will face a problem with Interfacing c/c++ DLL's with python if you wouldn't use Visual Studio. At times you might get off with petty walkabout, of your codes The problem faced with MinGW [ I have not used Clang ] is that the DLL is formed but the Encoding of it is Different from that expected .

https://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx

Here is the link of the Windows Portable Executable (PE) Header Structure

Only Visual Studio [as of now] is able to generate Windows Executable Shared Libraries .

The problem won't persist if Linux systems are used. I don't know about Macintosh.

As much the Code is concerned you need to add the Pointer to the Structure MyStruct which you would be using for calculation on the Dll side [ this is avoiding the problem with a workabout].

Upvotes: 1

Mark Tolonen
Mark Tolonen

Reputation: 177715

I was able to get the correct values by declaring the return type as POINTER(MyStruct), so it seems Python treats returning a structure as returning a pointer to that structure. A more natural way of returning a structure would be to return it as an output parameter. I give examples of both below.

As you stated, using func.restype = MyStruct worked correctly for c_int * 3 as the structure member, but I found only func.restype = POINTER(MyStruct) worked for both c_char * 3 and c_int * 3 members when the struct is used as a return value.

test.c

struct my_struct
{
    char arr[3];
};

__declspec(dllexport) struct my_struct func()
{
    struct my_struct m = {1,2,3};
    return m;
};

__declspec(dllexport) void func2(struct my_struct* m)
{
    m->arr[0] = 4;
    m->arr[1] = 5;
    m->arr[2] = 6;
};

test.py

from ctypes import *

class MyStruct(Structure):
    _fields_ = ('arr',c_char * 3),

dll = CDLL('test')

func = dll.func
func.argtypes = None
func.restype = POINTER(MyStruct)

func2 = dll.func2
func2.argtypes = POINTER(MyStruct),
func2.restype = None

x = func()
print(x.contents.arr[0],x.contents.arr[1],x.contents.arr[2])

m = MyStruct()
func2(m)
print(m.arr[0],m.arr[1],m.arr[2])

Output

1 2 3
4 5 6

Upvotes: 1

Related Questions