我正在尝试使Python使用Fortran DLL(通过引用调用).运行Fortran 90代码时,它可以正常运行,但在Python中不起作用;它只会给出“访问冲突”错误或“没有足够的参数调用”.
python代码:
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))
fortran90代码:
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
编辑:
我已经修改了代码,现在不传递字符串长度作为参考.我将cdll切换为windll,并删除了双POINTER和byref()用法.而且,c_char_p已经是一个指针,因此它不需要POINTER.
解决方法:
我不确定您的Fortran编译器的约定是什么,因此我将以一些一般性观点而不是具体性来回答:
>您的通话约定不匹配. Fortran代码指定stdcall,而ctypes代码指定cdecl.您需要使它们匹配.例如,将ctypes更改为使用windll而不是cdll.
>您确定POINTER(c_char_p)是正确的吗?那是一个指向空终止字符串的指针.我认为您可能在Python端有一个额外的间接层,但是我不确定100%.
> @eryksun在注释中指出,隐式字符串长度参数是通过值而不是通过引用传递的.
否则,我看不到任何错误,尽管我对Fortran一无所知,所以我不能为此提供担保.
如果您是我,我会将此权限缩减为一个传递单个int参数的简单函数.然后,我将添加一个int数组,并检查您是否可以为该参数双向传输数据.然后向上移动到一个字符串.不要从头开始尝试这样一个复杂的参数列表,因为您会给自己太多潜在的陷阱,而且很难知道首先要看哪里.