3

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.

8
  • If I replace print(i) with print(repr(i)); I get the expected output '\x01' '\x02' '\x03' on my machine (python2, g++ -std=c++11. __declspec is droped). What does return{} mean in the context of char arr[3] = {1,2,3 }; for a non-static member? Commented Jul 17, 2017 at 17:50
  • @J.F.Sebastian It means using the default constructor to construct a instance . Commented Jul 18, 2017 at 1:48
  • @J.F.Sebastian I tried using print(repr(i)) instead of print(i) ,I still got random values .I'm using python 3.6 and vs2015 on windows 7 . Commented Jul 18, 2017 at 1:54
  • 1
    repr() doesn't change values. It just displays them in a safe way for debugging. On Python 3, I get the expected 1 2 3 (iterating over bytes object yields ints there). Note: in general, you shouldn't return UDT from a function with a C linkage (it should be C ABI compatible i.e., POD) Have you tried to use the dll from a C code? Have you tried to dump the struct to a file (fwrite) and read it using ctypes, struct modules? Commented Jul 18, 2017 at 7:42
  • 1
    My compiler: test.cpp(7) : warning C4190: 'func' has C-linkage specified, but returns UDT 'my_struct' which is incompatible with C. Commented Jul 18, 2017 at 18:39

2 Answers 2

1

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
Sign up to request clarification or add additional context in comments.

2 Comments

@iouvxz My compiler indicates returning a struct is not C compatible (when compiled as C++), so func2 is the correct way to do it. func gave me random values if the return type was not a POINTER(MyStruct), but it may be luck that it prints correctly on my configuration (Windows 10, VS2012 64-bit compiler and Python 3.3 64-bit). It seems to delve into undefined behavior returning a structure. I don't know why you "sometimes" fail to index the array. You'll have to provide a reproducible example of that.
Sorry not m.arr[0],m.arr[1],m.arr[2], it's x.contents.arr[0],x.contents.arr[1],x.contents.arr[2] that pycharm sometimes reminds me "index out of range " ,func2 works fine.
1
+50

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].

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.