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.

iouvxz
  • 89
  • 9
  • 27
  • 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? – jfs Jul 17 '17 at 17:50
  • @J.F.Sebastian It means using the default constructor to construct a instance . – iouvxz Jul 18 '17 at 01: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 . – iouvxz Jul 18 '17 at 01: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](https://stackoverflow.com/q/17244488/4279)? – jfs Jul 18 '17 at 07:42
  • 1
    My compiler: `test.cpp(7) : warning C4190: 'func' has C-linkage specified, but returns UDT 'my_struct' which is incompatible with C`. – Mark Tolonen Jul 18 '17 at 18:39
  • @J.F.Sebastian Thank you for your advice .I edited my question .This time I use pure c ,not c++ ,but still get random values . – iouvxz Jul 19 '17 at 02:39
  • @J.F.Sebastian I tried using c++ to load the dll at run time ,it also works totally fine .I tried use python 2.7 ,I tried install python on a new windows ,it still give me random values . – iouvxz Jul 19 '17 at 05:56
  • @J.F.Sebastian .Seems as if the returned value no longer exists when I'm accessing it . – iouvxz Jul 19 '17 at 06:13

2 Answers2

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
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • @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. – Mark Tolonen Jul 19 '17 at 06:19
  • 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. – iouvxz Jul 19 '17 at 06:31
1

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

Aloy A Sen
  • 764
  • 5
  • 9