Silverfrost Logo About Us | Contact Us
 

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]

where:

name
is the name by which it will be called in the Fortran program.

alias
is the C++ Windows API name (or the required __stdcall function name ). Note that this appears in single quotes and is case-sensitive.

desc
is an argument descriptor, and is either REF, VAL, STRING, INSTRING, or OUTSTRING.

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.

restype
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
 STCALLL CSUB3(REF,STRING(20)):STRING
 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:

Arrays:
by reference (i.e. as a pointer)

INTEGER and REAL scalars:
by value

LOGICAL:
by value, as an integer of the appropriate length, 1 representing .TRUE. and 0 representing .FALSE.

CHARACTER objects:
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 (REF)

CHARACTER objects:
Reference or string (REF, STRING, INSTRING or OUTSTRING)

The three variants of the STRING descriptor are as follows:

STRING
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 necessary.

INSTRING
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 back.

OUTSTRING
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 be:

 STDCALL ESCAPE 'Escape' (VAL, VAL, VAL,INSTRING,
+       OUTSTRING):INTEGER*4

A second form of this Escape function is:

 int Escape(hdc, SETCOPYCOUNT, sizeof(int),
             lpNumCopieslpActualCopies)

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,
+  OUTSTRING):INTEGER*4
 STDCALL ESCAPE2 'Escape' (VAL, VAL, VAL, REF,REF):INTEGER*4

 

 

Copyright © 1999-2017 Silverfrost Limited