我有一个 Fortran 90 程序,它重复调用 Delphi 函数。该函数被多次调用,但最终程序因段错误而退出。
我有一个用 IBM fortran 编译的代码的可执行文件,它工作得很好,我必须使用 gfortran 重新编译整个代码
我有主程序的源代码,但没有函数的源代码,它位于随代码提供的 DLL 中,以及可能支持 Delphi 位的 borlndmm.dll
DLL 是 32 位的,我正在 Windows 7 系统上工作,使用 32 位 gfortran 编译器,规范如下:
COLLECT_GCC=C:\program files (x86)\gcc\bin\gfortran.exe COLLECT_LTO_WRAPPER=c:/program files (x86)/gcc/bin/../libexec/gcc/i686-pc-mingw32/4.7.2/lto-wrapper.exe Target: i686-pc-mingw32 Configured with: ../gcc-4.7.2-mingw/configure --host=i686-pc-mingw32 --build=x86_64-unknown-linux-gnu --target=i686-pc-mi ngw32 --prefix=/home/gfortran/gcc-home/binary/mingw32/native/x86_32/gcc/4.7.2 --with-gcc --with-gnu-as --with-gnu-ld --wi th-cloog=/home/gfortran/gcc-home/binary/mingw32/native/x86_32/cloog --with-gmp=/home/gfortran/gcc-home/binary/mingw32/nat ive/x86_32/gmp --with-mpfr=/home/gfortran/gcc-home/binary/mingw32/native/x86_32/mpfr --with-mpc=/home/gfortran/gcc-home/b inary/mingw32/native/x86_32/mpc --enable-cloog-backend=ppl --with-sysroot=/home/gfortran/gcc-home/binary/mingw32/cross/x8 6_32/gcc/4.7.2 --disable-shared --disable-nls --disable-tls --disable-win32-registry --enable-libquadmath-support --enabl e-libquadmath --enable-languages=c,c++,fortran --enable-libgomp --enable-threads=win32 --enable-lto --enable-static --ena ble-shared=lto-plugin --enable-plugins --with-host-libstdcxx='-lstdc++ -lsupc++ -lm' --with-ppl=/home/gfortran/gcc-home/b inary/mingw32/native/x86_32/ppl --enable-ld=yes Thread model: win32 gcc version 4.7.2 (GCC)
I've previously posted a question about this code (see Segmentation fault when calling a C function from Fortran repeatedly) but that was only the first stumbling block.
I compile the code with
>> gfortran -o cmod cmod.f90 -fbounds-check -ffree-line-length-none -dH -mrtd -g -L. Clues.dll
But I can't even get a dump output when it fails. I am thinking that I have a problem with memory alignment when calling the Delphi function, which eventually kills me. Or I may not be compiling correctly for 32 bit on a 64 bit system, I don't have any experience with this. Any idea on how to proceed would be most welcome.
The function definition in the original fortran code for IBM fortran is:
module overseer
use kernel32
interface
function CluesOvr(scenario,region,soilorder,topography,rainfall,ASoildepth,Snumdairy,Snumsheep,Snumbeef,Snumdeer,AdditionalNitrogen,Supplementrate,SupplementType,Nloss,Ploss,ErrStr)
!DEC$ ATTRIBUTES VALUE :: scenario,region,soilorder,topography,rainfall,ASoildepth,Snumdairy,Snumsheep,Snumbeef,Snumdeer,AdditionalNitrogen,Supplementrate,SupplementType
!DEC$ ATTRIBUTES REFERENCE :: Nloss,Ploss,ErrStr
LOGICAL CluesOvr
integer*4 scenario,region,soilorder,topography,ASoildepth
real*8 rainfall,Snumdairy,Snumsheep,Snumbeef,Snumdeer
real*8 AdditionalNitrogen,Supplementrate
Integer*4 SupplementType
real*8 Nloss,Ploss
character ErrStr(40)
end function CluesOvr
end interface
end module
我已经翻译成:
INTERFACE
LOGICAL (C_BOOL) FUNCTION CluesOvr(scenario,region,soilorder,topography,rainfall, &
ASoildepth,Snumdairy,Snumsheep,Snumbeef,Snumdeer,AdditionalNitrogen, &
Supplementrate, SupplementType,Nloss,Ploss, &
ErrStr) BIND (C, name='CluesOvr')
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
INTEGER (C_INT), INTENT(IN), VALUE :: scenario,region,soilorder,topography,ASoildepth
REAL (C_DOUBLE), INTENT(IN), VALUE :: rainfall,Snumdairy,Snumsheep,Snumbeef,Snumdeer
REAL (C_DOUBLE), INTENT(IN), VALUE :: AdditionalNitrogen,Supplementrate
INTEGER (C_INT), INTENT(IN), VALUE :: SupplementType
REAL (C_DOUBLE), INTENT(OUT) :: Nloss,Ploss
CHARACTER(C_CHAR), INTENT(OUT) :: ErrStr(*)
END FUNCTION CluesOvr
END INTERFACE
IBM 代码也使用
pointer (q,CluesOvr)
p = loadlibrary("CluesOvr.dll")
q = getprocaddress(p, "CluesOvr")
访问该功能。我不会用 gfortran 这样做。
最佳答案
borlandmm.dll 的存在表明您的任务几乎是不可能的。该 DLL 用于允许不同的模块(例如可执行文件和 DLL)共享公共(public)的 Delphi 内存管理器。这允许一个模块(例如可执行文件)分配一个 Delphi 字符串,并将其传递给另一个模块(例如 DLL),后者又可以释放该字符串。
除非两个模块共享相同的堆,否则这样的架构无法工作。 borlandmm.dll
库使跨模块的堆共享成为可能。任何希望使用其主机的 Delphi 内存管理器的 DLL 都包含 Sharemem
单元,该单元又使用 borlandmm.dll
库来实现内存管理器共享。
现在,您的 Fortran 主机不可能满足所需的契约(Contract)。唯一可以提供Delphi内存管理器的是Delphi主机。将会发生的情况是,您调用的 DLL 认为它负责释放它传递的内存。 DLL 可能接收堆分配的 Delphi string
变量。当 DLL 尝试释放内存时,该内存已在 Fortran 主机进程中分配。这种不匹配很可能导致访问违规。这些不一定会在每次调用该函数时发生。
这个 DLL 的设计使用 borlandmm.dll
是合理的,前提是它预计只能从 Delphi 主机调用。如果 DLL 的开发人员知道他们在做什么,那么他们就会意识到该限制。事实上,您没有 DLL 的文档,这表明您已从另一个程序中提取了 DLL,并试图以未设计的方式使用它。你成功的机会非常低。
关于delphi - 从 Fortran 调用 Delphi 函数时出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16683391/