【问题标题】:can I call windows api functions (64 bit) when compiling with gcc (gfortran)?使用 gcc (gfortran) 编译时可以调用 windows api 函数(64 位)吗?
【发布时间】:2013-08-28 22:41:55
【问题描述】:

我正在尝试编译一个子例程 sendmsg.f90,我们之前使用 lf90 作为 32 位现在使用 gcc 作为 64 位编译。目的是将它与其他用 gcc 编译的 64 位子程序链接起来。

subroutine UpdateDisplay(WINHAND,NTPS,QCANCEL,NITER)
  use win32mod
  implicit none
  integer WINHAND, NTPS, MESSNUM, NITER, QCANCEL
!                SendMessageA is a Windows function
!                  WINHAND = handle of window (from ISDev code)
!                  MESSNUM = message number (assigned by ISDev)
!                  NTPS = number of year iteration currently on (WParam)
!                  DUM = 0 (LParam)  

  MESSNUM = 1114
  QCANCEL = SendMessageA(carg(WINHAND),carg(MESSNUM),carg(NTPS), carg(NITER))

end subroutine

我们有 Lahey Fortran 7.5,它有 lf90 编译器和 gcc(它使用 gfortran)和一个叫做 lgf 的东西(我想它使用 gcc,对吗?)。

我是 Windows 编程(和 Fortran)的新手。使用 gcc 编译的代码可以调用 Windows API 吗?我应该使用 ISO_C_BINDING 吗?

我应该这样做还是应该尝试将 lf90 创建的 .obj 文件与 gcc 创建的 .o 文件链接?

感谢您的帮助!

更新: 我尝试使用以下方法调用 Windows API:

MODULE SND_C
    interface
        integer(C_LONG) FUNCTION SendMessage  &
            (WINHAND,MESSNUM,NTPS, NITER)  &
            bind(C,Name='SendMessage')
            use ISO_C_BINDING
            implicit NONE
            integer(C_LONG), VALUE :: WINHAND
            integer(C_LONG), VALUE :: MESSNUM
            integer(C_LONG), VALUE :: NTPS
            integer(C_LONG), VALUE :: NITER
        end function SendMessage
    end interface
END MODULE SND_C


subroutine UpdateDisplay(WINHAND,NTPS,QCANCEL,NITER)    
  USE ISO_C_BINDING, ONLY: C_LONG
  USE SND_C
  implicit none
  integer(C_LONG) WINHAND, NTPS, MESSNUM, NITER, QCANCEL
!                SendMessageA is a Windows function
!                  WINHAND = handle of window (from ISDev code)
!                  MESSNUM = message number (assigned by ISDev)
!                  NTPS = number of year iteration currently on (WParam)
!                  DUM = 0 (LParam)  

!GCC$ ATTRIBUTES DLLEXPORT :: UpdateDisplay
  MESSNUM = 1114
  QCANCEL = SendMessage(WINHAND,MESSNUM,NTPS, NITER)

end subroutine

但是当我尝试使用“gcc -m64 sendmsg.f90”进行编译时,我得到了错误:

C:\Users\StephanieJ\Documents\Lahey-Fujitsu Fortran>gcc -m64 sendmsg.f90
c:/progra~2/lahey-~1/v7.5/gcc-gf~1/bin/../lib/gcc/x86_64-w64-mingw32/4.7.4/../..
/../../x86_64-w64-mingw32/lib/../lib/crt2.o: In function `__tmainCRTStartup':
c:\gccbuild\mingw-crt\build32-64\mingw-w64-crt/../../mingwsvn/mingw-w64-crt/crt/
crtexe.c:323: undefined reference to `__laheypause'
C:\Users\STEPHA~2\AppData\Local\Temp\ccWjjo5b.o:sendmsg.f90:(.text+0x3e): undefi
ned reference to `SendMessage'
c:/progra~2/lahey-~1/v7.5/gcc-gf~1/bin/../lib/gcc/x86_64-w64-mingw32/4.7.4/../..
/../../x86_64-w64-mingw32/lib/../lib/libmingw32.a(lib64_libmingw32_a-crt0_c.o):
In function `main':
c:\gccbuild\mingw-crt\build32-64\mingw-w64-crt/../../mingwsvn/mingw-w64-crt/crt/
crt0_c.c:18: undefined reference to `WinMain'
collect2.exe: error: ld returned 1 exit status

这种方法有意义吗?为什么我对 SendMessage 的引用未定义?

【问题讨论】:

  • 您可能可以从 Fortran 调用 Windows 64 位 API,但请注意 API 已更改(以及 ABI),因此您将需要一个特定的接口(我的意思是,相当于 windows .h).
  • 在 64 位上,调用 API 例程实际上更容易,因为您不必担心 STDCALL。您所需要的只是正确的接口,您可以使用 BIND(C) 来指定混合大小写的名称。 ISO_C_BINDING 可以帮助提高实物价值,但可能不多。英特尔 Fortran 为数千个 Windows API 例程提供声明——其他一些 Windows 编译器也提供。
  • 也许应该是 SendMessageA?在 Windows API 中,处理字符的函数通常有两个版本,一个以 A(ANSI 字符集)结尾,另一个以 W(宽,即 unicode)结尾。在 C 中,名称是自动选择的(可能是 windows.h 中的 macris),而不是从另一种语言调用时。
  • @arbautjc 你说得对,我们需要一个 API 接口。 Lahey-Fortran 的 Lahey/Fujitsu 编译器似乎有这样的东西,但 Lahey/GNU Fortran 没有。但是,解决方法是将 windows.h 包含在 c 包装器中,并将 .o 文件与从 Fortran 编译的其他 .o 文件链接。
  • @Steve Lionel 是的,好点。您的 post 很好地解释了这一点。

标签: winapi gcc fortran gfortran fortran-iso-c-binding


【解决方案1】:

调用 API 的最简单方法可能是通过 C 包装函数,如下所示:

#include <windows.h>
void updatedisplay_(  HWND *winhand, WPARAM  *ntps, LRESULT *qcancel, LPARAM *niter)
{
  //  integer NTPS, MESSNUM, NITER, QCANCEL
  /*
  !                  WINHAND = handle of window (from ISDev code)
  !                  MESSNUM = message number (assigned by ISDev)
  !                  NTPS = number of year iteration currently on (WParam)
  !                  DUM = 0 (LParam)  
  */
  UINT messnum = 1114;
  *qcancel = SendMessage(*winhand,messnum,*ntps,*niter);
}

小写字母和尾随下划线是为了匹配 Fortran 默认函数命名。 如果假设文件名为updatedisplay.c,则使用命令编译:

gcc -c updatedisplay.c

在 Fortran 端,不要发送零作为窗口句柄,而是使用 ISO_C_BINDING 中的 C_NULL_PTR。 然后,您可以将目标文件 updatedisplay.o 与最初调用同名 Fortran 函数的 Fortran 代码链接,它应该可以工作。

要摆脱 laheypause 的问题,请从正确的 lib 目录中链接文件 lgfpstub.o,对于 x86_64 或 x86_64-w64-mingw32\lib 或对于 x86 的 x86_64-w64-mingw32\lib32。

希望这会有所帮助!

【讨论】:

  • 上面的代码可以正常工作。我们没有意识到我们可以链接从 fortran 编译的 .o 文件和从 c 编译的 .o 文件。
猜你喜欢
  • 1970-01-01
  • 2018-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-04
  • 2011-10-09
  • 2022-01-13
相关资源
最近更新 更多