1

I have a DLL with one single function That gets five doubles and one int:

__declspec(dllexport)  struct res ITERATE(double z_r,double z_i,double c_r, double c_i, int iterations, double limit)

It retuns a custom struct caled res which consists of a three-double array:

struct res {
   double arr[3];
};

To return the values I do this:

struct res result;      /*earlier in the code */

result.arr[0] = z_real; /*Just three random doubles*/
result.arr[1] = z_imag;
result.arr[2] = value;
return result;

I've compiled it with MinGW and I'm trying to use it in python to do something like this:

form ctypes import *

z = [0.0,0.0]
c = [1.0,1.0]
M = 2.0

MiDLL = WinDLL("RECERCATOOLS.dll")
MiDLL.ITERATE.argtypes = [c_double, c_double, c_double, c_double,c_int,c_double]
MiDLL.ITERATE(z[0],z[1],c[0],c[1],100,M) #testing out before assigning the result to anything.

But, whenever I try call the function with those values, it wil throw this to me:

WindowsError: exception: access violation writing 0x00000000

I also don't know how to catch the custom structure I declared and convert each of it's elements into Python floating points. I've looked into this PyDocs link but to no avail.

Thank you in advance.

EDIT:

This is the original (modified according to suggestions) header used ("mydll.h"):

#ifndef MYDLL_H
#define MYDLL_H

extern "C" __declspec(dllexport)
#define EXPORT_DLL __declspec(dllexport)

EXPORT_DLL void ITERATE(struct res*, double z_r,double z_i,double c_r, double c_i, int iterations, double limit)


#endif

And, in case something might be wrong with it, the code file (it's very short, just one function):

#include <stdio.h>
#include <complex.h>

struct res {
   double arr[3];
};

void __declspec(dllexport) ITERATE(struct res* result,double z_r,double z_i,double c_r, double c_i, int iterations, double limit)
{
/* The purpose of this function is, given two complex numbers,
   an iteration number and a limit, apply a formula to these
   two numbers for as many iterations as specified.

   If at any iteration the result of the formula is bigger than
   the limit, stop and return the number and the iteration it reached.

   If after iterating they are still inside the limit, return the
   number after all the iterations and the number of iterations
   it has gone through.

   Complex numbers are composed of a real part and an imaginary part,
   and they must be returned separately.
*/
double complex z = z_r + z_i*I;
double complex c = c_r + c_i*I;
int actual_iter;

for (actual_iter = 1; actual_iter <= iterations; actual_iter++)
    {
    z = z*z + c;
    if (cabs(z) > limit)
        {
        double value = actual_iter;
        double z_real = creal(z);
        double z_imag = cimag(z);
        result.arr[0] = z_real;
        result.arr[1] = z_imag;
        result.arr[2] = value;
        }
    }
double value = iterations;
double z_real = creal(z);
double z_imag = cimag(z);
result.arr[0] = z_real;
result.arr[1] = z_imag;
result.arr[2] = value;
}

int main()
{
return 0;
}
Daniel Torramilans
  • 295
  • 2
  • 3
  • 8
  • Now that you've made this change you also need to modify your python program to pass a pointer to that struct as the first parameter. Look at the link you've posted as to how you do that. If that doesn't wortk post your Python code that matches the modified C code. – Guy Sirton Jul 17 '11 at 21:35
  • Yes, I've been able to do that by looking at the PyDocs and the people at SO, I've finally got it to work. :D – Daniel Torramilans Jul 18 '11 at 00:26
  • you can use my answer: http://stackoverflow.com/questions/7586504/python-accessing-dll-using-ctypes/13167362#13167362 – Reza Ebrahimi Jan 19 '13 at 13:58

1 Answers1

2

There is a problem with returning structs like that. Not all compilers return such structures the same way. I'd rather change the function declaration to this:

void __declspec(dllexport) ITERATE(struct res* result, double z_r,double z_i, 
    double c_r, double c_i, int iterations, double limit);

That way the struct is in the user's memory, and there is no ambiguity on how the struct will be returned.

Of course, as David said, you may have to use a different calling convention.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • I don't fully understand what does adding a pointer to the struct in the args mean (As you can see, I'm not any type of C dev, I don't have much experience in C). What you are saying is that I should provide a pointer to a `struct res` object so that the function is able to manage it and modify it, but without the function actually returning anything? Ok, it makes a lot of sense, but, how do I initialize a python object that I can pass as an `struct res` object? Something like this: `Arr3 = c_double*3` (in Python) and then pass that as the first argument of the function? – Daniel Torramilans Jul 17 '11 at 20:49
  • `Arr3 = (c_double*3)()` and then pass it with `byref(arr)`, IIRC – David Heffernan Jul 17 '11 at 20:53
  • Different compilers have different ways to return a struct like that. Fact is that they must be passed (invisibly to you, the compiler will do that) a pointer to the struct they must return, so they can write the contents to that piece of memory. But HOW this pointer is passed to the C function may differ between compilers. So while you think a function compiled by compiler A is the same as the same function compiled by compiler B, they may have a difference in how the pointer is passed. It is better to explicitly pass it yourself, so there is no ambiguity. That is what my change does. – Rudy Velthuis Jul 17 '11 at 20:55
  • FWIW, I have been bitten by this a few times when I had to use such a function returning a struct (or array, same problem) in another language than C (in my case, Delphi). I learned that one must always be careful when a C function returns a struct or array and that it is best if the situation is avoided. – Rudy Velthuis Jul 17 '11 at 20:57
  • +1 I think this has a very good chance of explaining the problem. – David Heffernan Jul 17 '11 at 21:11
  • But C won't let me assing the values (when ending the function) like so: `result.arr[0] =z_real;`, it gives me an error `request for nember arr in something not a structure or union`. I've updated the code so you see how I've put it. – Daniel Torramilans Jul 17 '11 at 21:13
  • `result->arr[0] =z_real` is what you need because result is now a pointer to a struct – David Heffernan Jul 17 '11 at 21:24
  • FWIW, MSVC++ 32 bit even returns such a struct, if it is no larger than 8 bytes, in registers EAX and EDX. Returning structs from a function is something DLLs should never do, although not many C programmers are aware of this, ISTM. – Rudy Velthuis Jul 17 '11 at 21:27
  • Why the downvote? I wish that all downvotes would come with comments. – Rudy Velthuis Jul 18 '11 at 21:07