在Fortran中调用外部DLL并使用gfortran进行编译时出现区分大小写的问题

时间:2017-11-16 17:28:50

标签: dll fortran 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"?

(我对该问题的扩展谷歌搜索没有让我更接近解决方案)

1 个答案:

答案 0 :(得分:0)

有三种方法可以做到这一点:让我们制作一个示例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的usefule,因此上面是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的方法不再有效,在运行时失败,因为它&# 39;试图找到符号' f'在main1.exe而不是dll1.dll。在这一点上无法弄清楚原因。

编辑:嗯,我找到了一种方法,让第一种方法与上面创建的dll1.dll和main1.f90一起工作,但我不是真的{{ 3}}所以我等了几天才看看别人是否想出了一个更可口的解决方案。如果在那段时间之后,这还没有发生,有人仍然感兴趣,他应该提醒我,我可以发布我想出的内容。