Calling external functions and procedures

As an extension to Standard Pascal, Irie Pascal supports calling functions and procedures in Windows DLLs, using the external directive. NOTE: Only 32-bit DLLs are currently supported.

A number of include files, with declarations for many of the Windows API functions, are distributed with Irie Pascal. You can use these include files to gain access to many pre-written declarations of Windows API functions without having to create your own declarations for these functions.

However if you decide to create your own declarations for external functions and procedures, you will need to understand a little bit about the calling convention used by Windows DLLs. Windows DLLs support two calling conventions, "stdcall" and "cdecl". The most popular calling convention is "stdcall".

By default Irie Pascal will assume that the calling convention is "stdcall" unless you specifiy "cdecl".

Unfortunately, calling conventions are not implemented in exactly the same way by all Windows compilers. So if you want to call a function or procedures in a DLL, you not only need to know the calling convention it uses, you may need to know which compiler was used to create it (and also what compiler options were used).

This document does not describe the various ways different compilers implement the "stdcall" and "cdecl" calling conventions. Instead what this document does is describe how Irie Pascal implements these conventions.

Details of how Irie Pascal calls external functions/procedures

Itemstdcallcdecl
Direction of parameter passing. Left to right. Left to right.
Who is responsible for removing parameters from the stack. The function/procedure being called. The caller.
First, search the DLL for the function/procedure, using the name you specified when you made the call,  but if the function/procedure is not found then prefix "_" to the name and search again.  NOTE: This is done because some compilers automatically add a "_" prefix to function/procedure names but other compilers don't. Yes Yes
Function results of ordinal type Returned in the EAX register. Returned in the EAX register.
Function results of pointer type Returned in the EAX register. Returned in the EAX register.
Function results of address type Returned in the EAX register. Returned in the EAX register.
Function results of real type Returned on the math coprocessor stack. Returned on the math coprocessor stack.
Function results of single type Returned on the math coprocessor stack. Returned on the math coprocessor stack.
Function results of double type Returned on the math coprocessor stack. Returned on the math coprocessor stack.
Function results of record type or array type are returned using a pointer passed by the caller. The caller is expected to pass a pointer to the memory location where the function should put the return value. Yes Yes
Function results of dir type Not supported. Not supported.
Function results of file type Not supported. Not supported.
Function results of list type Not supported. Not supported.
Function results of object type Not supported. Not supported.
Function results of set type Not supported. Not supported.
Function results of string type Not supported. Not supported.
Passing by value: expressions of ordinal type Yes Yes
Passing by value: expressions of pointer type Yes Yes
Passing by value: expressions of record type Yes Yes
Passing by value: expressions of address type Yes Yes
Passing by value: expressions of real type Yes Yes
Passing by value: expressions of single type Yes Yes
Passing by value: expressions of double type Yes Yes
Passing by value: expressions of all other types. No No
Passing by reference: variables of array type Yes Yes
Passing by reference: variables of ordinal type Yes Yes
Passing by reference: variables of pointer type Yes Yes
Passing by reference: variables of record type Yes Yes
Passing by reference: variables of string type Yes Yes
Passing by reference: variables of address type Yes Yes
Passing by reference: variables of double type Yes Yes
Passing by reference: variables of single type Yes Yes
Passing by reference: variables of real type Yes Yes
Passing by reference: functions/procedures No No

How Irie Pascal stores values

In order to successfully call external functions and procedures you may need to understand a little bit about how values of the Irie Pascal types are stored. Below is a brief description of the what values of the Irie Pascal types are stored:

First the simple types:

Next the complex types:

Converting between C types and Irie Pascal types

Most functions in Windows DLLs are written in C, so you will usually be converting back of forth between C types and Irie Pascal types.

NOTE: The C types in all capitals like BOOL, WORD, and DWORD are not actually defined as part of the C language, but are common types declared in C programs.

Using BOOL Parameters

The C languages does not define a boolean type so it is common for C programs to define one, however there are two popular ways this can be done. One way is to define the boolean type as char or unsigned char, in which case the nearest Irie Pascal type is byte. The other common way to define the boolean type is as a enumerated type with the values FALSE, and TRUE. However sometimes C compilers treat enumerated types with less than 256 elements as unsigned char, and at other times as int (the C name for the integer type). So if boolean type is defined as an enumerated type then it can either be treated as unsigned char, in which case the nearest Irie Pascal type is byte, or as int, in which case the nearest Irie Pascal type is boolean.

Using int And long Parameters

With most C compilers, the types int and long are signed 32-bit integral types so the nearest Irie Pascal type is integer.

Using DWORD, unsigned int And unsigned long Parameters

With most C compilers, the types DWORD, unsigned int, and unsigned long are unsigned 32-bit integral types so the nearest Irie Pascal type is word.

Using short Parameters

With most C compilers, the type short is a signed 16-bit integral type so the nearest Irie Pascal type is shortint.

Using unsigned short Parameters

With most C compilers, the type unsigned short is a unsigned 16-bit integral type so the nearest Irie Pascal type is shortword.

Using Array Parameters

Passing multi-dimensional arrays between Irie Pascal and C requires care since the ordering used by Pascal and C for the dimensions is opposite (i.e.

   a : array[1..4][1..2] of integer;

is equivalent to

   int a[2][4];

Using pointer Parameters

  1. The C language does not support call by reference (only call by value). So when you pass an argument to a C function, a copy of the argument is passed. The function can modify the copy of the argument but the modifications are not visible to the caller, because the caller is using the argument itself and not the copy.

    This can be a problem if the function has to return more than one value. C programs commonly solve this problem by passing a pointer to the argument instead of the argument itself. The function will receive a copy of the pointer, but because the copy points at the argument the C function can modify the argument my modifying what the pointer is pointing to.

    Internally, Irie Pascal implements call be reference in the same way, so you can use call by reference in cases where the C function expects a pointer to a variable.

  2. The C language also does not support passing arrays to functions, however the close relationship between arrays and pointers in C means that passing a pointer to the first element in an array is almost equivalent to passing the array. Irie Pascal supports passing arrays, so in this case you should pass an array by reference to the C function or procedure.
  3. A special case of passing arrays, is passing C strings (arrays of null-terminated chars). In this case you may want to pass a cstring by reference.
  4. In cases where the pointer is pointing to a variable whose type is not fixed, or a null value can be passed, or the pointer is pointing to a variable whose size is not fixed then you may want to pass the operating system address of the variable directly.
When you want to call a C function that expects a pointer parameter it is up to you to determine (perhaps by reading a description of the function) which one of the four scenarios just described in being used.

The sample program winhello.pas is an example of how to call a function in a Windows DLL. NOTE: This program uses the winuser.inc include file, distributed with Irie Pascal, to gain access to a pre-written declaration for the Windows API function MessageBox.