问题描述
我收到了一个用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
...
#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中准备参数的方式(建议使用大量的指针-为什么?)有一个建议。