使用Ctypes Python <> Fortran DLL时出现访问冲突问题

3
我正在尝试让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,并删除了双指针和byref()的使用。此外,c_char_p已经是一个指针,所以不需要一个POINTER。


你知道,编译器并不会因为变量名更长就向你收取更多的费用。只是说一下... - Stefano Borini
@Stefano Fortran是别人的代码,采用了非常简短的命名传统。在边界的另一侧使用相同的名称是有意义的。 - David Heffernan
@David Heffernan:原始名称非常短,您可以将它们用作前缀。 - Eryk Sun
1
您在编辑问题时指出问题是字符串长度传递的问题。那真的是唯一的错误吗?另外,您不应该更新您的问题并添加“已解决”。您应该接受一个可接受的答案,或者添加您自己的答案并接受它。该网站旨在以问答格式进行。 - David Heffernan
抱歉,我找不到在答案中标记注释的方法,所以我进行了编辑。 - Kols
1个回答

4
我不确定你的Fortran编译器的传统做法,所以我将回答一些通用的点而非具体细节:
  1. 你们的调用约定不匹配。Fortran代码指定了stdcall,而ctypes代码指定了cdecl。你需要让它们匹配起来。例如,改变ctypes使用而不是。
  2. 你确定使用POINTER(c_char_p)是正确的吗?这是指向指向空终止字符串的指针。我认为你在Python端可能有一个额外的间接层,但我不能100%确定。
  3. @eryksun 在评论中指出隐式字符串长度参数是按值传递而非按引用传递。

否则,我看不到任何错误,虽然我对Fortran一无所知,所以我不能保证那方面没问题。

如果我是你,我会将其简化为传递单个int参数的简单函数。然后我会添加int数组并检查是否可以双向传输此类参数的数据。然后转到字符串。不要尝试从头开始构建如此复杂的参数列表,因为这会给自己带来太多潜在的问题,很难知道首先查找哪里。


这不是我的DLL,所以很遗憾,没有更容易使用的函数可用:\ 我将尝试使用windll! POINTER()只是我尝试使其工作的一种方式,因为我看到了一些论坛帖子,其中它是成功的。 - Kols
没问题。编写适当简单的函数的Fortran DLL。同时修复调用约定,看看是否有帮助。 - David Heffernan
@user1064136:字符串的长度是按值传递,而不是按引用传递。 - Eryk Sun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接