从Fortran调用特定的C ++ DLL

问题描述

我收到了一个用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程序是这样写的。这是我在CONTAINS之前的代码的一部分。

    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,TARGET,DIMENSION(9)    :: DLLname
        CHaraCTER(KIND=C_CHAR,POINTER                 :: PoInputfile,PoOutputfile,PoStateControllerName    
        CHaraCTER(KIND=C_CHAR,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,PoInputfile,PoStateControllerName,InitState)

解决方法

C函数的特性与相关的Fortran接口主体描述的特性不匹配。

在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接口还包含gfortran以外的一系列编译器(今天由Intel Fortran表示)的编译器指令(!DEC$ ATTRIBUTES...)。该指令可能会以与C函数匹配的方式更改该系列编译器在Fortran方面的参数行为。但是gfortran不理解该指令-它认为它只是一个注释。

gfortran有其自己的等效指令,但是使用此类指令反映了该语言标准不支持C互操作性的时间。

由于编译器开始支持Fortran 2003,因此最好使用标准语言功能来管理互操作性-将VALUE添加到需要它的参数中(也是InitState),然后删除指令。

问题中未显示Windows API的LoadLibrary和GetProcAddress的详细信息。我假设已经为所有可能使用的编译器正确地描述了它们。

关于在调用C代码的要求方面的其他误解,在Fortran中准备参数的方式(建议使用大量的指针-为什么?)有一个建议。