# Linking a windows app in gfortran

Hi,

I am newbie to this Forum and trying a first post…

I have a windows application that I have build for many years in Ifort. Before that many years ago it started life on VAX/VMS Fortran but has made its way to the current day (Windows/Ifort) via a number of different compilers and systems. In my experience changing OS and vendor has always had some level of pain.

I am happy with IFORT but for amusement and to stress test compiler portability I decided to attempt a gfortan build. For the record all my windows bindings are my own and all using bind(c, name=…)
I added !GCC$ATTRIBUTES STDCALL :: to go with the existing !DEC$ ATTRIBUTES STDCALL :: in a bulk edit of a large number of interfaces.

Using GNU Fortran (GCC) 10.2.0 in CMD window via a batch file I compiled all the source files OK ( gfortran -c .f90 )
I compiled the resource (.res) file ( windres -i .res -o .o )

So far so good…?

The last step is a problem, not knowing how to set up library paths for the SDK I dumped the windows lib files needed in my working folder and as a first test and then did:
gfortran .o .0 … .lib .lib … -o thing.exe

This resolved all externals and made thing.exe but it is not a windows exe with the correct stub, I get “This APP can’t run on your PC”

It would seem to me that a need some compile / link options and other magic to make it work but I am struggling to find useful information online.
Some help or pointers would be great.

Best Regards
Andrew

1 Like

Welcome, glad you have joined here in addition to the Intel forum!

From your comments, it appears you have Windows SDK installed and perhaps also Microsoft Windows C / C++ compiler via Microsoft Visual Studio application. Is that correct? If yes, a quick workaround you can use is the “command prompt” shortcuts made available via the Visual Studio installer. You likely know of them via Start -> Programs and the folder for Visual Studio 201X. You can pick one of the prompts corresponding to your target platform, say 'x64 Native Tools Command Prompt for VS 201X". Will that be an option? If yes, things can be made much simpler by using the Microsoft Linker ‘link.exe’.

The usual libraries needed are ‘libcmt.lib’ and perhaps ‘user32.lib’.

Here’s a ‘Hello World’ example built with gfortran and Microsoft linker:

module IWinAPI_m

use, intrinsic :: iso_c_binding, only : c_char, c_null_char, c_int, c_ptr, C_NULL => c_null_ptr, c_loc

interface
function MessageBox(hWnd, lpText, lpCaption, uType) result(RetVal) &
bind(C, name="MessageBoxA")
!DIR$ATTRIBUTES STDCALL :: MessageBox ! Microsoft Windows API function prototype ! https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox ! int MessageBox( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); import :: c_ptr, c_int ! Argument List type(c_ptr), intent(in), value :: HWnd type(c_ptr), intent(in), value :: lpText type(c_ptr), intent(in), value :: lpCaption integer(c_int), intent(in), value :: uType !.. Function result integer(kind=c_int) :: RetVal !GCC$ ATTRIBUTES STDCALL :: MessageBox
end function MessageBox
end interface

! Mnemonics
type :: MsgBoxFlags
integer(c_int) :: ABORTRETRYIGNORE  = int(Z"00000002", kind=c_int)
integer(c_int) :: CANCELTRYCONTINUE = int(Z"00000006", kind=c_int)
integer(c_int) :: HELP              = int(Z"00004000", kind=c_int)
integer(c_int) :: OK                = int(Z"00000000", kind=c_int)
integer(c_int) :: OKCANCEL          = int(Z"00000001", kind=c_int)
integer(c_int) :: RETRYCANCEL       = int(Z"00000005", kind=c_int)
integer(c_int) :: YESNO             = int(Z"00000004", kind=c_int)
integer(c_int) :: YESNOCANCEL       = int(Z"00000003", kind=c_int)
integer(c_int) :: ICONEXCLAMATION   = int(Z"00000030", kind=c_int)
end type
type(MsgBoxFlags), parameter, public :: MB = MsgBoxFlags()

contains

function WinMain( hInstance, hPrevInstance, lpCmdLine, nShowCmd ) result(RetVal) &
bind(C, name="WinMain")
!DIR$ATTRIBUTES STDCALL :: WinMain !GCC$ ATTRIBUTES STDCALL :: WinMain

! the entry point for any Windows program
! https://docs.microsoft.com/en-us/windows/win32/learnwin32/winmain--the-application-entry-point
! int WINAPI WinMain(HINSTANCE hInstance,
!                    HINSTANCE hPrevInstance,
!                    LPSTR lpCmdLine,
!                    int nShowCmd)

! Argument List
type(c_ptr), intent(in), value :: hInstance
type(c_ptr), intent(in), value :: hPrevInstance
character(kind=c_char, len=1), intent(in) :: lpCmdLine(*)
integer(c_int), intent(in), value         :: nShowCmd
!.. Function result
integer(kind=c_int) :: RetVal

! Local variables
integer(c_int) :: uType
character(kind=c_char,len=:), allocatable, target :: Text
character(kind=c_char,len=:), allocatable, target :: Caption

Caption = c_char_"Fortran WinMain Function" // c_null_char
Text = c_char_"Hello World!" // c_null_char
uType = ior( MB%ICONEXCLAMATION, MB%OK)

RetVal = MessageBox( C_NULL, c_loc(Text), c_loc(Caption), uType )

return

end function WinMain

end module IWinAPI_m


Compilation and link steps:

** Visual Studio 2019 Developer Command Prompt v16.6.4

[vcvarsall.bat] Environment initialized for: ‘x64’

C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional>cd\temp

C:\Temp>gfortran -c -std=f2018 p.f90

C:\Temp>link p.o libcmt.lib user32.lib /subsystem:windows /out:p.exe
Microsoft ® Incremental Linker Version 14.26.28806.0

C:\Temp>p.exe

C:\Temp>

Here’s the program output:

1 Like

Hi FF thanks for the help which was a very useful starting point. Using link has me on more familiar ground.

Your sample code links and runs fine which is a good start. My application has a lot of unresolved symbol now due to gfortan libraries.

I added a couple of lines to your sample code to define a text string end then did an ADJUSTL on it to add a Fortran intrinsic. I then get “unresolved external symbol _gfortran_adjustl referenced in function WinMain”

If I add the gcc lib libgfortran.a then that error goes away but there is then a huge number of unresolveds due to mixing the different C library stuff I think.

I guess I need to ditch libcmt.lib and use some gcc lib(s). I have had a bit of a play but haven’t figured it out yet.

Hmm… you can consider going the other way. Use the gfortran executable itself as the linker and build your app up until you start getting errors? Note my example was rather basic and the gfortran linker would work with it just as well, in fact in a simpler fashion because it does all the “heavy lifting” with its libraries. I only mentioned the Microsoft linker thinking you had all the libraries lined up plus you wanted to bring more of other libraries for your app.

With the following change to the example code in my earlier post:

      Caption = c_char_"Fortran WinMain Function" // c_null_char
Text = adjustl(c_char_"  Hello World!")
Text = Text // new_line(c_char_"") // c_char_"Message was left-justified" // c_null_char
uType = ior( MB%ICONEXCLAMATION, MB%OK)


one can simply do it in one step with gfortran p.f90 -o p.exe, or in 2 steps too:

The key for a Windows app, I think, is to have a WinMain with the right signature. You can simplify your program to a console app with a Fortran main program if you don’t really need to employ any Windows APIs.

It has to be a windows application, it has many dialogs and API interfaces and also includes both GDI and OGL graphics.

OK I have done some more learning. I find GCC versions of the SDK libs at gcc\x86_64-w64-mingw32\lib. Having messed and failed to get gfortran library paths to work for now as a quick and dirty test I got my batch script to copy the 8 or so API libs locally and included them explicitly in the list of things to link. That produces a “working” windows exe file . The only API that failed was HtmlHelp as there is no GCC stub library for that so I commented that out and I guess I can loaded it myself via Loadlibrary/GetProcAddress.

Thanks for the help and pointers all I need to do now is to sort out a more sensible way of finding the libs. Maybe my environment needs some more settings.

I think gfortran accepts -llibrary for the archive library name (e.g., -lfoo for libfoo.a) as well as -Ldir (e.g., -L"C:\bar\foo bar") for search directory specifications for library paths.

I sorted the lib search paths, an opaque error message didn’t help. Having the .a on the lib names was one of my main problems . D’oh!