在 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 我想到了什么。