使用 Ctypes 与 Fortran DLL 交互时出现访问冲突
Access Violation when using Ctypes to Interface with Fortran DLL
我有一组从 Fortran 创建的 dll,我是 运行 来自 python。我已经成功地创建了一个包装器 class 并且 运行 dll 已经好几个星期了。
今天我发现输入错误并进行了更改,但令我惊讶的是,这导致了以下问题:
OSError: exception: access violation reading 0x705206C8
似乎某些输入值以某种方式导致我尝试访问非法数据。我创建了以下 MCVE,它确实重复了这个问题。具体来说,当 338 < R_o < 361
时会抛出一个错误。不幸的是,我不能发布原始 Fortran 代码,也不能创建一个 MCVE 来复制问题并且足够抽象以便我可以共享它。所有变量在 Fortran 代码中都声明为 integer
或 real(8)
类型。
import ctypes
import os
DLL_PATH = "C:\Repos\CASS_PFM\dlls"
class wrapper:
def __init__(self,data):
self.data = data
self.DLL = ctypes.CDLL(os.path.join(DLL_PATH,"MyDLL.dll"))
self.fortran_subroutine = getattr(self.DLL,"MyFunction_".lower())
self.output = {}
def run(self):
out = (ctypes.c_longdouble * len(self.data))()
in_data = []
for item in self.data:
item.convert_to_ctypes()
in_data.append(ctypes.byref(item.c_val))
self.fortran_subroutine(*in_data, out)
for item in self.data:
self.output[item.name] = item.convert_to_python()
class FortranData:
def __init__(self,name,py_val,ctype,some_param=True):
self.name = name
self.py_val = py_val
self.ctype = ctype
self.some_param = some_param
def convert_to_ctypes(self):
ctype_converter = getattr(ctypes,self.ctype)
self.c_val = ctype_converter(self.py_val)
return self.c_val
def convert_to_python(self):
self.py_val = self.c_val.value
return self.py_val
def main():
R_o = 350
data = [
FortranData("R_o",R_o,'c_double',False),
FortranData("thick",57.15,'c_double',False),
FortranData("axial_c",100,'c_double',False),
FortranData("sigy",235.81,'c_double',False),
FortranData("sigu",619.17,'c_double',False),
FortranData("RO_alpha",1.49707,'c_double',False),
FortranData("RO_sigo",235.81,'c_double',False),
FortranData("RO_epso",0.001336,'c_double',False),
FortranData("RO_n",6.6,'c_double',False),
FortranData("Resist_Jic",116,'c_double',False),
FortranData("Resist_C",104.02,'c_double',False),
FortranData("Resist_m",0.28,'c_double',False),
FortranData("pressure",15.51375,'c_double',False),
FortranData("i_write",0,'c_int',False),
FortranData("if_flag_twc",0,'c_int',),
FortranData("i_twc_ll",0,'c_int',),
FortranData("i_twc_epfm",0,'c_int',),
FortranData("i_err_code",0,'c_int',),
FortranData("Axial_TWC_ratio",0,'c_double',),
FortranData("Axial_TWC_fail",0,'c_int',),
FortranData("c_max_ll",0,'c_double',),
FortranData("c_max_epfm",0,'c_double',)
]
obj = wrapper(data)
obj.run()
print(obj.output)
if __name__ == "__main__": main()
这不仅仅是 R_o
值;有一些值的组合会导致相同的错误(似乎没有韵律或原因)。根据传递给 DLL 的值,上述 Python 中是否有任何内容可能导致访问冲突?
Python 版本为 3.7.2,32 位
我看到代码有 2 个问题(还有一个潜在的 3rd):
argtypes(和 restype)未指定,如 [Python 3]: Specifying the required argument types (function prototypes) 中所述。在这种情况下发生的事情有很多例子,这里有两个:
这可能是前者的后果(或者至少与前者密切相关)。 我只能猜测没有 Fortran(或更好:C)函数原型,但无论如何有肯定有问题。我假设对于输入数据,事情应该与输出数据相同,所以该函数将采用 2 个数组(相同大小),并且输入的元素将是 void *
s(因为它们的类型不一致) .然后,你需要类似的东西(虽然我无法想象 Fortran 怎么知道哪个元素包含一个 int 而哪个元素包含 双):
in_data (ctypes.c_void_p * len(self.data))()
for idx, item in enumerate(self.data):
item.convert_to_ctypes()
in_data[index] = ctypes.addressof(item.c_val)
由于您使用的是 32 位,因此您还应该考虑调用约定(ctypes.CDLL
vs ctypes.WinDLL
)
但同样,如果没有函数原型,一切都只是猜测。
此外,为什么 "MyFunction_".lower()
而不是 "myfunction_"
?
我有一组从 Fortran 创建的 dll,我是 运行 来自 python。我已经成功地创建了一个包装器 class 并且 运行 dll 已经好几个星期了。
今天我发现输入错误并进行了更改,但令我惊讶的是,这导致了以下问题:
OSError: exception: access violation reading 0x705206C8
似乎某些输入值以某种方式导致我尝试访问非法数据。我创建了以下 MCVE,它确实重复了这个问题。具体来说,当 338 < R_o < 361
时会抛出一个错误。不幸的是,我不能发布原始 Fortran 代码,也不能创建一个 MCVE 来复制问题并且足够抽象以便我可以共享它。所有变量在 Fortran 代码中都声明为 integer
或 real(8)
类型。
import ctypes
import os
DLL_PATH = "C:\Repos\CASS_PFM\dlls"
class wrapper:
def __init__(self,data):
self.data = data
self.DLL = ctypes.CDLL(os.path.join(DLL_PATH,"MyDLL.dll"))
self.fortran_subroutine = getattr(self.DLL,"MyFunction_".lower())
self.output = {}
def run(self):
out = (ctypes.c_longdouble * len(self.data))()
in_data = []
for item in self.data:
item.convert_to_ctypes()
in_data.append(ctypes.byref(item.c_val))
self.fortran_subroutine(*in_data, out)
for item in self.data:
self.output[item.name] = item.convert_to_python()
class FortranData:
def __init__(self,name,py_val,ctype,some_param=True):
self.name = name
self.py_val = py_val
self.ctype = ctype
self.some_param = some_param
def convert_to_ctypes(self):
ctype_converter = getattr(ctypes,self.ctype)
self.c_val = ctype_converter(self.py_val)
return self.c_val
def convert_to_python(self):
self.py_val = self.c_val.value
return self.py_val
def main():
R_o = 350
data = [
FortranData("R_o",R_o,'c_double',False),
FortranData("thick",57.15,'c_double',False),
FortranData("axial_c",100,'c_double',False),
FortranData("sigy",235.81,'c_double',False),
FortranData("sigu",619.17,'c_double',False),
FortranData("RO_alpha",1.49707,'c_double',False),
FortranData("RO_sigo",235.81,'c_double',False),
FortranData("RO_epso",0.001336,'c_double',False),
FortranData("RO_n",6.6,'c_double',False),
FortranData("Resist_Jic",116,'c_double',False),
FortranData("Resist_C",104.02,'c_double',False),
FortranData("Resist_m",0.28,'c_double',False),
FortranData("pressure",15.51375,'c_double',False),
FortranData("i_write",0,'c_int',False),
FortranData("if_flag_twc",0,'c_int',),
FortranData("i_twc_ll",0,'c_int',),
FortranData("i_twc_epfm",0,'c_int',),
FortranData("i_err_code",0,'c_int',),
FortranData("Axial_TWC_ratio",0,'c_double',),
FortranData("Axial_TWC_fail",0,'c_int',),
FortranData("c_max_ll",0,'c_double',),
FortranData("c_max_epfm",0,'c_double',)
]
obj = wrapper(data)
obj.run()
print(obj.output)
if __name__ == "__main__": main()
这不仅仅是 R_o
值;有一些值的组合会导致相同的错误(似乎没有韵律或原因)。根据传递给 DLL 的值,上述 Python 中是否有任何内容可能导致访问冲突?
Python 版本为 3.7.2,32 位
我看到代码有 2 个问题(还有一个潜在的 3rd):
argtypes(和 restype)未指定,如 [Python 3]: Specifying the required argument types (function prototypes) 中所述。在这种情况下发生的事情有很多例子,这里有两个:
这可能是前者的后果(或者至少与前者密切相关)。 我只能猜测没有 Fortran(或更好:C)函数原型,但无论如何有肯定有问题。我假设对于输入数据,事情应该与输出数据相同,所以该函数将采用 2 个数组(相同大小),并且输入的元素将是
void *
s(因为它们的类型不一致) .然后,你需要类似的东西(虽然我无法想象 Fortran 怎么知道哪个元素包含一个 int 而哪个元素包含 双):in_data (ctypes.c_void_p * len(self.data))() for idx, item in enumerate(self.data): item.convert_to_ctypes() in_data[index] = ctypes.addressof(item.c_val)
由于您使用的是 32 位,因此您还应该考虑调用约定(
ctypes.CDLL
vsctypes.WinDLL
)
但同样,如果没有函数原型,一切都只是猜测。
此外,为什么 "MyFunction_".lower()
而不是 "myfunction_"
?