Kols
Kols

Reputation: 83

Access violation with Ctypes Python <> Fortran DLL

I'm trying to make Python use a Fortran DLL (call by referance). When running Fortran 90 code, it works fine, but will not work in Python; it only gives "access violation" errors or "called with not enough arguments".

The python code:

from ctypes import *
mydll = cdll.LoadLibrary("test.dll")

# This function works.
print mydll.XIT() # prints 0

mydll.GetInfo.argtypes = [POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_int),POINTER(c_char_p)]

rm = c_int()
rf = c_int()
vm = (c_int * 5)()
vf = (c_int * 5)()
np = c_int(14)
p = (c_int * 14)()
filename = "test"
fn = c_char_p(filename)
nc = c_int(len(filename)) # length of string. (Hidden argument in Fortran)

# throws insufucient arguments
print mydll.GetInfo(rm,rf,vm,vf,np,p,fn,nc)

# throws access violation
print mydll.GetInfo(byref(rm),byref(rf),byref(vm),byref(vf),byref(np),byref(p),byref(fn),byref(nc))

The fortran90 code:

program test
  implicit none
  integer, parameter :: np = 14 
  integer :: rm, rf
  integer, dimension(5) :: vm, vf
  integer, dimension(np) :: p
  character(len=80) :: fn
  interface
    integer function GetInfo(rm, rf, vm, vf, np, p, fn)
    !dec$ attributes dllimport, stdcall, reference, decorate, alias:'GetInfo' :: GetInfo
      implicit none
      character(len=*), intent(in) :: fn
      integer, intent(in) :: np
      integer, intent(out) :: rm,rf
      integer, intent(out), dimension(5) :: vm,vf
      integer, intent(out), dimension(np) :: p
    end function GetInfo
  end interface
  fn = "test"
  print *, GetInfo(rm, rf, vm, vf, np, p, fn)
end program test

Edit: I've modified my code to now not pass the string length as reference. I've switched the cdll to windll, and removed the double POINTER and byref() usage. Also, c_char_p is already a pointer, so it doesn't need a POINTER.

Upvotes: 3

Views: 1192

Answers (1)

David Heffernan
David Heffernan

Reputation: 612794

I'm not sure on what the conventions of your Fortran compiler are so I'll answer with some general points rather than specifics:

  1. Your calling conventions don't match. The Fortran code specifies stdcall and the ctypes code specifies cdecl. You need to make them match up. For example, change the ctypes to use windll rather than cdll.
  2. Are you sure that POINTER(c_char_p) is right? That is a pointer to a pointer to null-terminated string. I think you probably have an extra layer of indirection at the Python end, but I'm not 100% sure of that.
  3. @eryksun states in a comment that the implicit string length parameter is passed by value rather than by reference.

Otherwise I can't see anything wrong, although I know nothing whatsoever about Fortran so I can't vouch for that.

If I were you I would cut this right back to a trivial function that passes a single int parameter. Then I would add an int array and check that you can transfer data both ways for such a parameter. Then move up to a string. Don't attempt such a complicated parameter list from scratch because you just give yourself too many potential pitfalls and it's hard to know where to look first.

Upvotes: 4

Related Questions