Как использовать loadlibrary и getprocaddress от gfortran?

Я пытаюсь узнать, как вызвать функцию в dll fortran из исполняемого файла fortran в Windows. Я работаю с гфортраном 4.7 и фотраном в затмении.

Моя тестовая dll имеет единственную функцию в hello.f90:

hello.f90

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

со следующим make-файлом:

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

makefile

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

Сообщение об ошибке заключается в том, что функция LoadLibrary (и getprocaddress) не имеет типа IMPLICIT. Я подозреваю, что это означает, что эти функции не определены, и мне нужно как-то включить их заголовки. Это правильно? Я нашел объявление для loadlibrary в c: \ mingw \ include \ winbase.h

ваше здоровье,

Марк


person marcp    schedule 04.01.2013    source источник
comment
Я больше не использую Windows, поэтому я не уверен на 100%, но разве не должно быть char(0) после hello.dll и hello_, чтобы он заканчивал строку ??   -  person Kyle Kanos    schedule 05.01.2013


Ответы (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
person IanH    schedule 05.01.2013
comment
Также необходимо связать со статической библиотекой импорта (.lib) библиотеки DLL. - person Hristo Iliev; 05.01.2013
comment
@HristoIliev Какую DLL вы имеете в виду? - person IanH; 05.01.2013
comment
Тот, который содержит GetProcAddressA и LoadLibraryA, а именно kernel32.dll. Код должен быть связан с kernel32.lib из Win32 SDK. - person Hristo Iliev; 05.01.2013
comment
@IanH, спасибо большое. Работает отлично. Но у меня есть вопрос, если вы не возражаете. Я не понимаю, как работает BIND (C, NAME = 'LoadLibraryA'). Где определяется LoadLibraryA, поскольку я не связываю файлы заголовков или библиотеки явно, где-то должно быть какое-то неявное связывание? - person marcp; 05.01.2013
comment
@IanH, при более внимательном рассмотрении, я думаю, вы объявляете прототипы функций в разделе интерфейса, который позволяет ему компилировать, и некоторые волшебные качества minGW разрешают системные вызовы Windows во время выполнения. Это правильно? ваше здоровье. - person marcp; 05.01.2013
comment
@marcp, LoadLibrary имеет две версии: строковую версию ANSI LoadLibraryA и версию Unicode (широкую) LoadLibraryW. Оба экспортируются kernel32.dll. В основном все программы Windows связаны с kernel32.lib из Win32 SDK, который является статической библиотекой экспорта kernel32.dll. Обычно простые программы на C (без явных вызовов Win32 API), а также программы на Фортране связываются только со своими библиотеками времени выполнения, но похоже, что версия MinGW компилятора gfortran также неявно связывается с kernel32.lib. - person Hristo Iliev; 06.01.2013
comment
@marcp, в этом случае блоки интерфейса описывают только аргументы функции, а BIND(C,NAME="...") дает правильное имя функции (одно без специального изменения имени компилятора Fortran), поэтому компилятор Fortran может сгенерировать правильную последовательность вызовов и ссылаться на правильный внешний символ . Это Fortran-эквивалент прототипов функций C, которые можно найти в файлах заголовков. В этом нет ничего волшебного :) - person Hristo Iliev; 06.01.2013
comment
@HristoIliev Я подумал, что вы имеете в виду статическую библиотеку импорта hello.dll. Да - дополнение -lkernel32 (и некоторые другие системные библиотеки) к строке ссылок жестко запрограммировано в драйвере gcc для Windows (программы на C тоже получают его, если вы не делаете что-то особенное). - person IanH; 06.01.2013