使用Ctypes时与Fortran DLL的访问冲突

3 投票
1 回答
1171 浏览
提问于 2025-04-17 07:00

我正在尝试让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))

这是Fortran 90代码:

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,并去掉了双指针和byref()的用法。此外,c_char_p本身就是一个指针,所以不需要再用指针。

1 个回答

4

我不太清楚你使用的Fortran编译器的具体规则,所以我会给出一些一般性的建议,而不是具体的细节:

  1. 你的调用约定不一致。Fortran代码使用的是stdcall,而ctypes代码使用的是cdecl。你需要让它们一致。例如,可以把ctypes改成用windll,而不是cdll
  2. 你确定POINTER(c_char_p)是对的吗?这个是指向指向以空字符结尾的字符串的指针。我觉得你可能在Python那边多了一层指针,但我不能百分之百确定。
  3. @eryksun在评论中提到,隐式字符串长度参数是按值传递的,而不是按引用传递的。

除此之外,我看不出有什么问题,虽然我对Fortran一无所知,所以不能保证这些。

如果我是你,我会先把代码简化到一个只传递一个整数参数的简单函数。然后再添加一个整数数组,检查一下你能否在这种参数下双向传输数据。接着再尝试字符串。不要一开始就尝试这么复杂的参数列表,因为那样会给自己带来很多潜在的问题,而且很难知道从哪里入手。

撰写回答