c++ - 从 Fortran 调用特定的 C++ DLL

标签 c++ dll fortran gfortran fortran-iso-c-binding

我收到了一个用 C++ 制作的 DLL,我正在构建一个 Fortran 程序来调用 C++ DLL。
我的编译器 (gfortran) 没有显示任何警告,但它在运行时崩溃,描述如下:

    forrtl: severe (157): Program Exception - access violation
    Image              PC        Routine            Line        Source
    MainDLL_v10.dll    0B7A6B01  Unknown               Unknown  Unknown
    MainDLL_v10.dll    0B7A1BEF  Unknown               Unknown  Unknown
    ...
我想我的调用参数有问题。
C++ DLL 包括以下内容:
    #include <DllClasses.h>
   ...
   extern "C"
   {
   ...
     __declspec(dllexport) RESULT __cdecl Initialize(char *DllNames[], int NumberOfDlls, char *InputFile,  char *OutputFile, char *CtrlVersion, int InitState)
     {
     ...
     }
   ...
   } // extern C
我的 Fortran 程序是这样写的。这是我包含之前的代码的一部分。
    ABSTRACT INTERFACE
        SUBROUTINE Initialize(PDLLNames, NumberOfDLLs, PInputfile, Poutputfile, CtrlVersion, InitState) BIND(C)
            USE, INTRINSIC :: ISO_C_Binding
            IMPLICIT NONE
            !DEC$ ATTRIBUTES C :: Initialize
            CHARACTER(KIND=C_CHAR), INTENT(IN   ), DIMENSION(9) :: PDLLNames    
            INTEGER(C_INT),         INTENT(IN   )               :: NumberOfDLLs   
            CHARACTER(KIND=C_CHAR), INTENT(IN   )               :: PInputfile  
            CHARACTER(KIND=C_CHAR), INTENT(INOUT)               :: Poutputfile 
            CHARACTER(KIND=C_CHAR), INTENT(IN   )               :: CtrlVersion 
            INTEGER(C_INT),         INTENT(IN   )               :: InitState
        END SUBROUTINE Initialize
        SUBROUTINE MainDll(InputSignals, OutputSignals, PErrorMessage) BIND(C)
            USE, INTRINSIC :: ISO_C_Binding
            IMPLICIT NONE
            !DEC$ ATTRIBUTES C :: MainDll
            REAL(C_DOUBLE),          INTENT(IN   )   :: InputSignals   (*) 
            REAL(C_DOUBLE),          INTENT(  OUT)   :: OutputSignals  (*)
            CHARACTER(KIND=C_CHAR),  INTENT(  OUT)   :: PErrorMessage  (*)
        END SUBROUTINE MainDll
    END INTERFACE
这是我在程序中的一部分 Fortran 代码。
        ! Variables for dll interface
        PROCEDURE(Initialize), BIND(C), POINTER                 :: Initialize_proc
        INTEGER(C_INT)                                          :: NumberOfDLLs=9, InitState
        CHARACTER(KIND=C_CHAR, LEN=56), TARGET                  :: MainDll, DLLInputfile, DLLOutputfile, StateControllerName  
        CHARACTER(KIND=C_CHAR, LEN=56), TARGET, DIMENSION(9)    :: DLLname
        CHARACTER(KIND=C_CHAR, LEN=56), POINTER                 :: PoInputfile, PoOutputfile, PoStateControllerName    
        CHARACTER(KIND=C_CHAR, LEN=56), POINTER, DIMENSION(9)   :: PoDLLname(:)           
        PoInputfile => DLLInputfile
        PoOutputfile => DLLOutputfile
        PoStateControllerName => StateControllerName
        PoDLLname(1:) => DLLname(1:9)
    ...    
        ! Load DLL
        module_handle = LoadLibrary(MainDll // C_NULL_CHAR)
        proc_address = GetProcAddress( module_handle, C_CHAR_'Initialize' // C_NULL_CHAR )
        ! Call Initialize function in DLL
        CALL C_F_PROCPOINTER(proc_address, Initialize_proc)        
        CALL Initialize_proc(PoDLLname, NumberOfDLLs, PoInputfile, PoOutputfile, PoStateControllerName, InitState)

最佳答案

您的 C 函数的特征与相关 Fortran 接口(interface)主体所描述的特征不匹配。
在 Fortran 中调用可互操作过程(具有 BIND(C) 的过程)时,不带 VALUE 属性的标量参数通过引用传递给 C++ 函数。如果希望参数按值传递,则需要在 Fortran 端添加 VALUE 属性。
例如,在 C++ 片段中:

__declspec(dllexport) RESULT __cdecl Initialize(... int NumberOfDlls
NumberOfDlls 是按值传递的,但 Fortran 显示:
SUBROUTINE Initialize(... NumberOfDLLs, ...) BIND(C)
  ...
  INTEGER(C_INT), INTENT(IN) :: NumberOfDLLs   
无值属性 - Fortran 参数对应于 int *NumberOfDlls 的 C++ 参数.对于按值传递,请使用 Fortran 参数声明:
  INTEGER(C_INT), INTENT(IN), VALUE :: NumberOfDLLs   
您的 Fortran 接口(interface)还包括用于 gfortran 之外的一系列编译器(今天由 Intel Fortran 代表)的编译器指令 (!DEC$ ATTRIBUTES...)。该指令将更改该系列编译器的 Fortran 端参数的行为,可能以与 C 函数匹配的方式。但 gfortran 不理解该指令 - 它认为它只是一个注释。
gfortran 有自己的等效指令,但使用类似的指令反射(reflect)了语言标准没有 C 互操作性支持的时代。
由于编译器开始支持 Fortran 2003,您最好使用标准语言功能来管理互操作性 - 将 VALUE 添加到需要它的参数(也包括 InitState),并删除指令。
您的问题中未显示有关 Windows API 的 LoadLibrary 和 GetProcAddress 的细节。我假设它们已针对所有可能使用的编译器进行了正确描述。
在 Fortran 中准备参数的方式有一个建议(使用了很多指针 - 为什么??)关于调用 C 代码的要求的其他误解。

关于c++ - 从 Fortran 调用特定的 C++ DLL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64281930/

相关文章:

c++ - Armadillo C++ : How to modify multiple array elements of a matrix using multiple elements from another matrix, 专门在立方体结构中?

c# - 托管 C dll 调用 C# dll,FileNotFoundException

arrays - 从 Fortran 中的数组中删除索引值之后的所有行

c++ - GCC -Cmake 中的 Bprefix

dll - 加载时动态链接和运行时动态链接的区别

arrays - 错误 : The part-name to the right of a part-ref with nonzero rank has the ALLOCATABLE attribute

c++ - Qt - 小部件 - 更新

c++ - 'LIBCMT' 与使用其他库 + 未解析的外部符号冲突

c++ - 关于异常的问题

dll - 旧的 DLL 文件继续被使用