使用Ctypes与Fortran DLL接口时访问冲突

问题描述 投票:1回答:1

我有一组从Fortran创建的dll,我从python运行。我已经成功创建了一个包装类,并且已经运行了几周的dll。

今天我注意到输入错误并更改了它,但令我惊讶的是,这导致了以下情况:

OSError: exception: access violation reading 0x705206C8

如果看起来某些输入值以某种方式导致我尝试访问非法数据。我创建了以下MCVE,它确实重复了这个问题。特别是当338 < R_o < 361时抛出错误。不幸的是,我不能发布原始的Fortran代码,也不能创建一个复制问题的MCVE,并且已经足够抽象以便我可以共享它。所有变量都在Fortran代码中声明为integerreal(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的价值;有一些值的组合导致相同的错误(看似没有押韵或理由)。上面的Python中是否有任何内容可能会导致访问冲突,具体取决于传递给DLL的值?

Python版本是3.7.2,32位

python-3.x ctypes
1个回答
2
投票

我看到代码有两个问题(可能是第三个问题):

  1. 没有指定argtypes(和restype),如[Python 3]: Specifying the required argument types (function prototypes)中所述。在这种情况下会发生很多例子,其中有两个: [SO]: python ctypes issue on different OSes (@CristiFati's answer) [SO]: Python ctypes cdll.LoadLibrary, instantiate an object, execute its method, private variable address truncated (@CristiFati's answer)
  2. 这可能是前者的结果(或者至少与之密切相关)。我只能猜测没有Fortran(或更好的:C)函数原型,但无论如何肯定有问题。我假设对于输入数据,事情应该与输出数据相同,因此函数将采用2个数组(相同大小),输入的元素将是void *s(因为它们的类型不一致)。然后,你需要类似的东西(虽然我无法想象Fortran将如何知道哪个元素包含一个int以及哪个包含一个double): 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)
  3. 既然你是32位,你也应该考虑调用约定(ctypes.CDLL vs ctypes.WinDLL

但同样,如果没有功能原型,一切都只是猜测。

另外,为什么"MyFunction_".lower()而不是"myfunction_"

© www.soinside.com 2019 - 2024. All rights reserved.