【问题标题】:How to use loadlibrary and getprocaddress from gfortran?如何使用 gfortran 中的 loadlibrary 和 getprocaddress?
【发布时间】:2013-01-04 22:09:49
【问题描述】:

我正在尝试学习如何从 Windows 上的 fortran 可执行文件调用 fortran dll 中的函数。我在 eclipse 中使用 gfortran 4.7 和 photran。

我的测试 dll 在 hello.f90 中有一个函数:

hello.f90

subroutine hello
    implicit none
    print *, "Hello World!"
end subroutine hello

使用以下生成文件:

all:
    gfortran -Wall -c hello.f90
    gfortran -shared -o hello.dll hello.o

Dependency Walker 确认函数“hello_”已导出。

现在我正在尝试构建一个动态调用它的程序。我根据在网上找到的示例构建了以下内容,但无法编译:

main.f90

program main
    implicit none
    integer :: p
    pointer (q, hello)
    p = loadlibrary("hello.dll")
    q = getprocaddress(p, "hello_")
    call hello
end program main

制作文件

all:
    gfortran -Wall -pedantic -fcray-pointer main.f90 

错误消息是函数 LoadLibrary(和 getprocaddress)没有隐式类型。我怀疑这意味着这些函数没有定义,我需要以某种方式包含它们的标题。是对的吗?我在 c:\mingw\include\winbase.h 中找到了 loadlibrary 的声明

干杯,

马克

【问题讨论】:

  • 我不再使用 Windows,所以我不是 100% 确定,但不应该在 "hello.dll" 和 "hello_" 之后有一个 char(0) 让它结束这一行吗? ?

标签: fortran gfortran


【解决方案1】:

LoadLibrary 和 GetProcAddress 是 Windows API 例程。像任何非内在函数一样,您需要声明这些函数的类型,并且鉴于这些 API 的性质,您还需要进一步提供完整的接口。

如果您正在为 32 位 Windows 进行编译,那么这些 API 使用 stdcall 调用约定。您需要使用 gfortran 源指令扩展来指定它。

(在 64 位 Windows 上 stdcall 被定义为与 C 调用约定相同 - source 指令无效。)

对于调用约定控制,如果我将您的 DLL 代码更改为:

SUBROUTINE hello() BIND(C, NAME='hello')
  IMPLICIT NONE
  PRINT *, 'Hello'
END SUBROUTINE hello

然后以下主程序将加载生成的 DLL 并执行该过程。

PROGRAM Main
  USE, INTRINSIC :: ISO_C_BINDING, ONLY:  &
      C_F_PROCPOINTER, C_FUNPTR, C_INTPTR_T,  &
      C_NULL_CHAR, C_CHAR, C_ASSOCIATED

  IMPLICIT NONE

  INTERFACE 
     FUNCTION LoadLibrary(lpFileName) BIND(C,NAME='LoadLibraryA')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INTPTR_T, C_CHAR
        IMPLICIT NONE 
        CHARACTER(KIND=C_CHAR) :: lpFileName(*) 
        !GCC$ ATTRIBUTES STDCALL :: LoadLibrary 
        INTEGER(C_INTPTR_T) :: LoadLibrary 
     END FUNCTION LoadLibrary 

     FUNCTION GetProcAddress(hModule, lpProcName)  &
         BIND(C, NAME='GetProcAddress')
       USE, INTRINSIC :: ISO_C_BINDING, ONLY:  &
           C_FUNPTR, C_INTPTR_T, C_CHAR
       IMPLICIT NONE
       !GCC$ ATTRIBUTES STDCALL :: GetProcAddress
       TYPE(C_FUNPTR) :: GetProcAddress
       INTEGER(C_INTPTR_T), VALUE :: hModule
       CHARACTER(KIND=C_CHAR) :: lpProcName(*)
     END FUNCTION GetProcAddress      
  END INTERFACE

  ABSTRACT INTERFACE
    SUBROUTINE hello_intf() BIND(C)
      IMPLICIT NONE
    END SUBROUTINE hello_intf
  END INTERFACE

  INTEGER(C_INTPTR_T) :: module_handle
  TYPE(C_FUNPTR) :: proc_address
  PROCEDURE(hello_intf), BIND(C), POINTER :: my_proc

  !****      

  module_handle = LoadLibrary(C_CHAR_'hello.dll' // C_NULL_CHAR)
  IF (module_handle == 0) STOP 'Unable to load DLL'

  proc_address = GetProcAddress( module_handle,  &
      C_CHAR_'hello' // C_NULL_CHAR )
  IF (.NOT. C_ASSOCIATED(proc_address))  &
      STOP 'Unable to obtain procedure address'

  CALL C_F_PROCPOINTER(proc_address, my_proc)

  CALL my_proc

END PROGRAM Main

【讨论】:

  • 还必须链接到 DLL 的静态导入库 (.lib)。
  • @HristoIliev 你指的是哪个 DLL?
  • 包含GetProcAddressALoadLibraryA的那一项,即kernel32.dll。代码必须与 Win32 SDK 中的kernel32.lib 链接。
  • @IanH,非常感谢。它完美地工作。不过我有一个问题,如果你不介意的话。我不清楚 BIND(C,NAME='LoadLibraryA') 是如何工作的。 LoadLibraryA 在哪里定义,因为我没有明确链接任何头文件或库,所以一定有一些隐式链接在某处进行?
  • @IanH,经过仔细检查,我认为您在接口部分声明了允许它编译的函数原型,并且一些神奇的 minGW 优点正在解决运行时的 Windows 系统调用。是对的吗?干杯。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-22
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-10
相关资源
最近更新 更多