Reputation: 83
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
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:
windll
rather than cdll
.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.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