在 Fortran 中调用外部 DLL 并使用 gfortran 编译时的区分大小写问题
Case sensitiveness issue when calling an external DLL in Fortran and compiling with gfortran
我正在尝试从使用 gfortran 编译的 Fortran 代码调用 DLL。我正在导入 DISCON_32.dll:
!GCC$ ATTRIBUTES DLLIMPORT :: DISCON
并编译:
mingw32-gfortran "DISCON_32.dll" -cpp -ffree-line-length-none -fno-automatic -Wall -fdefault-real-8 -fno-underscoring -static BladedDLLInterface.o -o "my_program"
我收到错误:
BladedDLLInterface.o:BladedDLLInterface.f90(.text+0x6cd): undefined reference to 'discon'
请注意,入口点 "discon" 在这里称为小写。使用 Dependency Walker 查看 DLL 时,入口点是 "DISCON"(大写)。
我无法更改 DLL 的大小写。有没有办法告诉我的 Fortran 代码寻找 "DISCON" 入口点而不是 "discon"?
(我对问题的扩展谷歌搜索并没有让我更接近解决方案)
有 3 种方法可以做到这一点:让我们制作一个示例 DLL 和 Fortran main 来演示问题。
! dll1.f90
! gfortran dll1.f90 -shared -odll1.dll -Wl,--out-implib,libdll1.a
module not_used
use ISO_C_BINDING
implicit none
contains
function F(x) bind(C,name='F')
!GCC$ ATTRIBUTES DLLEXPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) ::x
F = x**2
end function F
end module not_used
用于构建 dll1.dll 的命令行显示在评论中。
现在我们有一个 Fortran 主程序:
! main1.f90
! fails:
! gfortran main1.f90 dll1.dll -omain1
! gfortran main1.f90 -L. -ldll1 -omain1
! works:
! gfortran main1.f90 dll1.dll -L. -ldll2 -omain1
! gfortran main1.f90 -L. -ldll1 -ldll2 -omain1
module mod1
implicit none
interface
function F(x)
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
double precision F
double precision, intent(in) :: x
end function F
end interface
end module mod1
program main1
use mod1
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main1
上面显示的前两个 gfortran 命令无法构建 main1.exe。后两个工作,但我们需要一个 libdll2.a 文件,我们可以用 dlltool.exe 创建它。我们从 dll2.def
开始
; dlltool -z dll2.def --export-all-symbol dll1.dll
; dlltool -d dll2.def -l libdll2.a
LIBRARY dll1.dll
EXPORTS
f = F
上面的第一行注释显示了如何 dlltool.exe 为 dll2.def 创建起点。输出在我们的案例中不是很有用,所以我们大多使用文本编辑器来修改起点。 dll2.def 文件将创建一个 libdll2.a 文件,该文件可用于与 dll1.dll 链接,因此是上面的 LIBRARY 行。导出的符号是 'F',但 gfortran 将寻找 'f',因此我们在导出部分重命名它。
然后我们通过上面的第二行注释使用 dlltool.exe 创建 libdll2.a 。有一个libdll2.a意味着我们可以根据第6行或第7行的注释编译main1.f90。
第二种方法使用函数的绑定名称。这要求该函数是可互操作的,但如果不是,那么您可能无法以任何简单的方式调用它,因为它可能不是由 gfortran 编译的。这里我们不需要额外的dll2.lib,只是在main2.f90中函数的不同声明:
! main2.f90
! gfortran main2.f90 dll1.dll -omain2
! gfortran main2.f90 -L. -ldll1 -omain2
module mod2
use ISO_C_BINDING
implicit none
interface
function F(x) bind(C,name='F')
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
end module mod2
program main2
use mod2
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main2
这可以使用评论中给出的任一 gfortran 命令进行编译。
最后,您可以像 main3.f90:
那样使用动态链接
! main3.f90
! gfortran main3.f90 dll1.dll -omain3
! gfortran main3.f90 -L. -ldll1 -omain3
module mod3
use ISO_C_BINDING
use ISO_C_BINDING, HANDLE => C_INTPTR_T
use ISO_C_BINDING, C_INTPTR_T => C_INTPTR_T
implicit none
abstract interface
function F(x) bind(C)
import
implicit none
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
interface
function LoadLibrary(lpFileName) bind(C,name='LoadLibraryA')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: LoadLibrary
integer(HANDLE) :: LoadLibrary
character(kind=C_CHAR) lpFIleName(*)
end function LoadLibrary
function GetProcAddress(hModule,lpProcName) bind(C,name='GetProcAddress')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: GetProcAddress
type(C_FUNPTR) GetProcAddress
integer(HANDLE), value :: hModule
character(kind=C_CHAR) lpProcName(*)
end function GetProcAddress
end interface
end module mod3
program main3
use mod3, F1 => F
implicit none
double precision x, y
type(C_FUNPTR) ptr
procedure(F1), pointer :: F
integer(HANDLE) hModule
hModule = LoadLibrary('dll1.dll'//C_NULL_CHAR)
ptr = GetProcAddress(hModule,'F'//C_NULL_CHAR)
call C_F_PROCPOINTER(ptr,F)
x = 13
y = f(x)
write(*,*) y
end program main3
评论中的任一 gfortran 命令都成功构建 main3.exe。
注意:重新测试时,main2.f90 和 main3.f90 方法有效,但由于某些原因,main1.f90 和 dll2.def 方法不再有效,在运行时失败,因为它试图在 main1.exe 而不是 dll1.dll 中找到符号 'f'。无法弄清楚为什么在这一点上。
编辑:好吧,我找到了一种方法使第一种方法与上面创建的 dll1.dll 和 main1.f90 一起工作,但我不知道真的 like 所以我会等几天,看看是否有人想出了一个更可口的解决方案。如果在那次之后,这还没有发生并且有人仍然感兴趣,他应该提醒我,我可能 post 我想到了什么。
我正在尝试从使用 gfortran 编译的 Fortran 代码调用 DLL。我正在导入 DISCON_32.dll:
!GCC$ ATTRIBUTES DLLIMPORT :: DISCON
并编译:
mingw32-gfortran "DISCON_32.dll" -cpp -ffree-line-length-none -fno-automatic -Wall -fdefault-real-8 -fno-underscoring -static BladedDLLInterface.o -o "my_program"
我收到错误:
BladedDLLInterface.o:BladedDLLInterface.f90(.text+0x6cd): undefined reference to 'discon'
请注意,入口点 "discon" 在这里称为小写。使用 Dependency Walker 查看 DLL 时,入口点是 "DISCON"(大写)。
我无法更改 DLL 的大小写。有没有办法告诉我的 Fortran 代码寻找 "DISCON" 入口点而不是 "discon"?
(我对问题的扩展谷歌搜索并没有让我更接近解决方案)
有 3 种方法可以做到这一点:让我们制作一个示例 DLL 和 Fortran main 来演示问题。
! dll1.f90
! gfortran dll1.f90 -shared -odll1.dll -Wl,--out-implib,libdll1.a
module not_used
use ISO_C_BINDING
implicit none
contains
function F(x) bind(C,name='F')
!GCC$ ATTRIBUTES DLLEXPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) ::x
F = x**2
end function F
end module not_used
用于构建 dll1.dll 的命令行显示在评论中。
现在我们有一个 Fortran 主程序:
! main1.f90
! fails:
! gfortran main1.f90 dll1.dll -omain1
! gfortran main1.f90 -L. -ldll1 -omain1
! works:
! gfortran main1.f90 dll1.dll -L. -ldll2 -omain1
! gfortran main1.f90 -L. -ldll1 -ldll2 -omain1
module mod1
implicit none
interface
function F(x)
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
double precision F
double precision, intent(in) :: x
end function F
end interface
end module mod1
program main1
use mod1
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main1
上面显示的前两个 gfortran 命令无法构建 main1.exe。后两个工作,但我们需要一个 libdll2.a 文件,我们可以用 dlltool.exe 创建它。我们从 dll2.def
开始; dlltool -z dll2.def --export-all-symbol dll1.dll
; dlltool -d dll2.def -l libdll2.a
LIBRARY dll1.dll
EXPORTS
f = F
上面的第一行注释显示了如何 dlltool.exe 为 dll2.def 创建起点。输出在我们的案例中不是很有用,所以我们大多使用文本编辑器来修改起点。 dll2.def 文件将创建一个 libdll2.a 文件,该文件可用于与 dll1.dll 链接,因此是上面的 LIBRARY 行。导出的符号是 'F',但 gfortran 将寻找 'f',因此我们在导出部分重命名它。
然后我们通过上面的第二行注释使用 dlltool.exe 创建 libdll2.a 。有一个libdll2.a意味着我们可以根据第6行或第7行的注释编译main1.f90。
第二种方法使用函数的绑定名称。这要求该函数是可互操作的,但如果不是,那么您可能无法以任何简单的方式调用它,因为它可能不是由 gfortran 编译的。这里我们不需要额外的dll2.lib,只是在main2.f90中函数的不同声明:
! main2.f90
! gfortran main2.f90 dll1.dll -omain2
! gfortran main2.f90 -L. -ldll1 -omain2
module mod2
use ISO_C_BINDING
implicit none
interface
function F(x) bind(C,name='F')
import
implicit none
!GCC$ ATTRIBUTES DLLIMPORT :: F
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
end module mod2
program main2
use mod2
implicit none
double precision x, y
x = 13
y = f(x)
write(*,*) y
end program main2
这可以使用评论中给出的任一 gfortran 命令进行编译。
最后,您可以像 main3.f90:
! main3.f90
! gfortran main3.f90 dll1.dll -omain3
! gfortran main3.f90 -L. -ldll1 -omain3
module mod3
use ISO_C_BINDING
use ISO_C_BINDING, HANDLE => C_INTPTR_T
use ISO_C_BINDING, C_INTPTR_T => C_INTPTR_T
implicit none
abstract interface
function F(x) bind(C)
import
implicit none
real(C_DOUBLE) F
real(C_DOUBLE), intent(in) :: x
end function F
end interface
interface
function LoadLibrary(lpFileName) bind(C,name='LoadLibraryA')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: LoadLibrary
integer(HANDLE) :: LoadLibrary
character(kind=C_CHAR) lpFIleName(*)
end function LoadLibrary
function GetProcAddress(hModule,lpProcName) bind(C,name='GetProcAddress')
import
implicit none
!GCC$ ATTRIBUTES STDCALL :: GetProcAddress
type(C_FUNPTR) GetProcAddress
integer(HANDLE), value :: hModule
character(kind=C_CHAR) lpProcName(*)
end function GetProcAddress
end interface
end module mod3
program main3
use mod3, F1 => F
implicit none
double precision x, y
type(C_FUNPTR) ptr
procedure(F1), pointer :: F
integer(HANDLE) hModule
hModule = LoadLibrary('dll1.dll'//C_NULL_CHAR)
ptr = GetProcAddress(hModule,'F'//C_NULL_CHAR)
call C_F_PROCPOINTER(ptr,F)
x = 13
y = f(x)
write(*,*) y
end program main3
评论中的任一 gfortran 命令都成功构建 main3.exe。
注意:重新测试时,main2.f90 和 main3.f90 方法有效,但由于某些原因,main1.f90 和 dll2.def 方法不再有效,在运行时失败,因为它试图在 main1.exe 而不是 dll1.dll 中找到符号 'f'。无法弄清楚为什么在这一点上。
编辑:好吧,我找到了一种方法使第一种方法与上面创建的 dll1.dll 和 main1.f90 一起工作,但我不知道真的 like 所以我会等几天,看看是否有人想出了一个更可口的解决方案。如果在那次之后,这还没有发生并且有人仍然感兴趣,他应该提醒我,我可能 post 我想到了什么。