The Fortran STDCALL statement
The STDCALL declaration allows the Fortran programmer to call Win32 API routines
written in C++.
The syntax of the declaration for a STDCALL function is as follows:
STDCALL name ['alias'] [(desc , ... )] [:restype]
is the name by which it will be called in the Fortran program.
is the C++ Windows API name (or the required __stdcall function name ).
Note that this appears in single quotes and is case-sensitive.
is an argument descriptor, and is either REF, VAL, STRING, INSTRING, or
STRING, INSTRING and OUTSTRING may be followed optionally
by an integer in parentheses. This integer specifies the maximum length for the
corresponding argument in the C routine, in each case where the length of the
corresponding Fortran character object cannot be determined (i.e. the actual
argument is CHARACTER*(*) ). If the integer is not specified, then a
default value of 256 (bytes) is assumed for the maximum length of the string.
is the type of the function. If this does not appear then the function does not
return a result (equivalent to the C type void). Valid types are INTEGER, REAL,
DOUBLE PRECISION, COMPLEX, DOUBLE COMPLEX, LOGICAL and STRING. INTEGER, REAL
and LOGICAL may be followed by a length specifier of the form "*n". If
the function result is declared to be of type STRING then the function result
should be assigned to a variable of type CHARACTER.
Some examples of valid STDCALL declarations are:
STDCALL SUB1, SUB2
STDCALL SUB2 'GetAttr':INTEGER
STDCALL SUB4 'GetSize' (REF,VAL,VAL,INSTRING,OUTSTRING(100))
If no argument specifiers are specified, then default argument linkage is
assumed. This is as follows:
by reference (i.e. as a pointer)
INTEGER and REAL scalars:
by value, as an integer of the appropriate length, 1 representing .TRUE. and 0
Copied to a compiler defined temporary variable. A trailing null is added to
the end of the significant length of the string (i.e. there are no
trailing spaces). The temporary variable is then passed by reference. In
addition, if the actual argument is a scalar or array element, the result in
the temporary variable is copied back, and padded to the right with blanks if
necessary. This is equivalent to the STRING linkage descriptor described below.
Where linkage descriptors are specified, the number of arguments in each call
must agree with the number of descriptors specified. The various categories of
objects which may correspond to particular argument descriptors are as follows:
Numeric and LOGICAL scalars:
Value or reference (VAL or REF)
Arrays, externals, dummy procedures:
Reference or string (REF, STRING, INSTRING or OUTSTRING)
The three variants of the STRING descriptor are as follows:
The corresponding argument is both input and output, and is copied to a
temporary variable on entry to the routine (with a trailing null inserted at
the end of the significant length), and if the argument is a scalar or array
element, is copied back to the actual argument, blank padded to the right if
The corresponding argument is an input argument with respect to the external
routine. The argument is only copied to the temporary variable, and not copied
The argument is returned by the external routine. The temporary variable is set
up to be the length of the corresponding scalar or array element plus one, or
of specified or default (256) length if the corresponding argument is
CHARACTER*(*), but the value is only copied out. Obviously, this descriptor is
only appropriate where the actual argument is a scalar or array element.
When it is required to pass a NULL pointer to a string, the value 0 (zero)
should be used.
Some Windows API functions allow a particular argument to take two different
types in different circumstances. For example, an LPSTR in some circumstances
and an integer in others. This is outside the scope of the STDCALL mechanism.
If this feature affects you then you should copy the STDCALL statement for the
relevant API and modify it to have a different Fortran name and argument list,
but keeping the same called name.
A common example is the Windows API function LoadCursor which is used to
load either a cursor defined in the program resource or a predefined system
cursor. This has the following definition:
HCURSOR WINAPI LoadCursorA(HANDLE hInstance,LPSTR lpCursorName)
When used to load a cursor from the program resource, hInstance is the
instance handle of the application. lpCursorName is a character string
containing the name of the cursor in the program resource. This form of the
function will have the STDCALL declaration :
STDCALL LOADCURSOR 'LoadCursorA' (VAL, INSTRING):INTEGER*4
When used to load a predefined system cursor, the first argument hInstance
is set to zero and the second argument lpCursorName is an integer
containing one of a number of predefined values which specifies the cursor to
be loaded. This form of the function will have the STDCALL declaration :
STDCALL LOADCURSOR 'LoadCursorA' (VAL, VAL):INTEGER*4
However, having two differing STDCALL statements for the same Fortran function
is not allowed. The solution is to change the Fortran name. For example
STDCALL LOADCURSOR1 'LoadCursorA' (VAL, INSTRING):INTEGER*4
STDCALL LOADCURSOR2 'LoadCursorA' (VAL, VAL):INTEGER*4
The same situation arises with some other functions that have the form as LoadCursor,
for example LoadBitmap and LoadIcon.
A further example is given by the Windows API printer Escape function
which can take many different forms. One of these has the following form:
int WINAPI Escape(hdc, GETTECHNOLOGY, NULL, NULL,lpTechnology)
lpTechnology is an LPSTR (long pointers to strings) so the STDCALL
declaration for this form of the function for use in a Fortran program would
STDCALL ESCAPE 'Escape' (VAL, VAL, VAL,INSTRING,
A second form of this Escape function is:
int Escape(hdc, SETCOPYCOUNT, sizeof(int),
lpNumCopies, and lpActualCopies are both LPINT (long pointers to
integers) so the STDCALL declaration for this form of the function would be:
STDCALL ESCAPE 'Escape' (VAL, VAL, VAL, REF, REF):INTEGER*4
As before, having two differing STDCALL statements for the same Fortran function
is not allowed and the solution is to change the Fortran name. For example
STDCALL ESCAPE1 'Escape' (VAL, VAL, VAL,INSTRING,
STDCALL ESCAPE2 'Escape' (VAL, VAL, VAL, REF,REF):INTEGER*4