Lahey/GNU Fortran

Calling the Windows API

While it is difficult to call a Windows API routine directly from GFortran, it is very easy to make calls through a thin C wrapper that can be statically linked to your Fortran program. This approach does not require use of non-standard Fortran code to import dlls, and with the use of the ISO_C_BINDING intrinsic module and function interfaces, all information needed to call the API can be presented using standard conforming code.

We think it is good practise to use such wrappers, even if the call is made directly from Fortran, as implementation dependent code is isolated from the main body of code, and in theory, the only porting required would be in the wrappers. By using the ISO_C_BINDING intrinsic module, you can define C compatible type parameters for variables that will be passed to or from C. If the API function being called contains arguments that are passed by value, an interface for that procedure will need to be declared and those arguments should be declared with the VALUE attribute. It is also advised to use IMPLICIT NONE to assure arguments are correctly typed, and that you do not inadvertently send an implicitly typed real variable in place of what is thought to be an integer variable.

An example of this approach can be found in the Win32\Examples\Mix_Lang\WINAPI subdirectory, in the files MESSBOX.F90, WINDOWS.F90, MESSAGEBOX.C, and RUNMESSBOX.BAT

Wrapper Function

First, let us examine the wrapper code, MESSAGEBOX.C:

Other than the considerations discussed above, the API function is called straight through and the function result is directly passed back. The wrapper can be compiled using the following command line:

gcc -c messagebox.c
Successful execution of the compile will produce the object file messagebox.o. If you are calling an api with a name ending with 32, the wrapper should be compiled for 32 bit by adding the -m32 option to the above command line, and all Fortran code must be also be compiled for 32 bit.

At this point, if you are using Visual Studio, you can add the object file as a reference, and it will be linked with your project.

Windows_h Module

We will next turn our attention to the Fortran side. The first step is to consider using the Windows parameter module. It contains definitions for various Windows parameters used as arguments or to interpret results for an API function invocation. If you are working with MessageBoxes or Windows event loop messages, you will want to use the module. It does not contain any structure defintions or function interfaces, so if these are required they must be implemented by the user. Compilation of WINDOWS.F90, will produce the module file windows_h.mod. Use the WINDOWS_H module in any procedure that requires knowledge of the Windows API. WINDOWS.F90 is a Fortran translation of the constant definitions in the standard windows header file WINDOWS.H.

Function Interface

The next thing to consider is the argument list for the API function being called. In the example described above, the argument list has two arguments passed by value and two arguments passed by reference. We must define an interface that tells Fortran about the value arguments:

  interface
      function MessageBox(hwnd, message, title, utype)
          use iso_c_binding
          integer(kind=C_LONG) :: MessageBox
          integer(kind=C_LONG), value :: hwnd, utype
          character(kind=C_CHAR, len=*) :: message, title
      end function MessageBox
  end interface
            
Note that we do not make any special declarations concerning importing DLL functions, or worry about whether the function is unicode or ascii. This is because our Fortran interface is for our C wrapper function, which is statically linked with our program when compiled with the companion C compiler, GCC. The details of connecting with the proper DLL function is handled by the linking process, provided the proper import library is supplied. The import libraries that are linked by default can be inspected by compiling with the LGF option -verbose. If you do not see your import library in the linker list, you must add it to the link. Also notice that the Fortran function is not sensitive to use of upper or lowercase letters, the C function will see the function spelled with all lowercase and an underscore appended to the name, provided we havn't done anything special to declare the name, like giving it a dll_import attribute.

Also note that we are using the ISO_C_BINDING intrinsic module and declaring our variables according to their C equivalents. The two arguments that are being passed by value are declared with the VALUE attribute. We are not providing any fixed length in the interface for the CHARACTER arguments.

Invoking the Function

In general no special treatment is needed to call the wrapper function, the information in the interface provides all the relevant information needed to make the call. If passing a character argument, be sure to terminate your character string with the null character C_NULL_CHAR from the ISO_C_BINDING module.

Void API functions must be called as subroutines from Fortran and API functions which return values must be called as functions from Fortran.

Resource files

Resource files can be compiled by specifying the resource file (.rc extension) on the LGF command line. You need to include WINDOWS.H (supplied with LGF in the x86_64-w64-mingw32\include subdirectory) in your resource file. LGF's driver will call RC.EXE (the resource compiler which ships with LGF and with various other Windows compilers) to create a compiled resource file (.res extension). This will then be linked with the other objects and libraries you specified on the command line.

Any new item you create with a #define in your resource file needs to be declared as an INTEGER parameter in your Fortran source so that it is accessible in the scoping unit in which it is referenced. It is cleanest to put all of these parameter declarations in a module.