DECLARE statement

Purpose

Explicitly declare a Sub or Function

Syntax

DECLARE {SUB | FUNCTION} ProcName [BDECL | CDECL | SDECL] [LIB

    "LibName"] [ALIAS "AliasName"] [([arguments])] [AS type]

Remarks

The DECLARE statement has the following parts:

ProcName

The name of the Sub or Function to be declared.  For Functions, a type-specifier may be appended (just like an ordinary variable name) to specify the data type of the Function's return value, in place of the [AS type] clause.

Future versions of PowerBASIC may not support type-specifier symbols for the Function return type, so specify the return data type with an explicit AS type clause in all DECLARE and FUNCTION definitions, to ensure future compatibility.

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention.  When a procedure calls a BDECL procedure, it passes its parameters on the stack from left to right.

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure.  Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

CDECL

Specifies that the declared procedure uses the C calling convention.  When a procedure calls a CDECL procedure, it passes its parameters on the stack from right to left.

In addition, the calling procedure must remove any passed parameters from the stack as part of the return process.  When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event the called procedure is imported or exported, PowerBASIC will automatically create a lowercase ALIAS, prefixed with an underscore.  Any periods in the name are replaced with underscores at the same time.

CDECL may be used for declaring external procedures written in the C language, or another language that follows C calling and parameter-passing conventions.  The following two declarations are equivalent, indicating how the default ALIAS name would be created by PowerBASIC:

DECLARE SUB C_Function CDECL ()

DECLARE SUB C_Function CDECL ALIAS "_c_function" ()

SDECL

This is the default if neither BDECL nor CDECL are specified.  SDECL (and its synonym STDCALL), specifies that the declared procedure uses the "Standard Calling Convention" as defined by Microsoft.  When calling an SDECL procedure, parameters are passed on the stack from right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

LIB

A string literal or equate that specifies the name of the module in which a procedure or function is located.  This allows you to call Subs or Functions that reside in DLLs.  Unlike 16-bit Windows, you must include the .DLL extension in the name of the DLL you wish to access.

DECLARE SUB MySub LIB "ZUSER32.DLL" ()

ALIAS

A string literal that identifies the name and capitalization of the procedure in the external DLL.  This lets you call a procedure or function by a name other than what it was originally named.  This is useful if you want to abbreviate a long name, or if the original name of a function contains characters that are illegal in PowerBASIC.  The AliasName is the routine's actual name, and Name is the title that you can use in PowerBASIC.  For example:

DECLARE SUB ShortName ALIAS "VeryLongProcName"()

DECLARE FUNCTION LegalName ALIAS "Illegal$Name"()

Although a Name must be unique, you may use the same AliasName in multiple declarations.  This is particularly useful for avoiding AS ANY in cases where a procedure is designed to receive several different types of parameters.

DECLARE FUNCTION AddAtom    LIB "KERNEL32.DLL" ALIAS "AddAtomA"

    (lpString AS ASCIIZ) AS WORD

DECLARE FUNCTION AddIntAtom LIB "KERNEL32.DLL" ALIAS "AddAtomA"

    (BYVAL lpString AS DWORD) AS WORD

The ALIAS clause is very important when exporting Subs and Functions from DLLs.  Omitting the ALIAS clause or incorrectly capitalizing the alias name are common causes of DLL load failure problems.  Please refer to the SUB and FUNCTION sections for more information.

 

Passing parameters

arguments

Contains the name(s) or the type of each parameter, in the order they are passed, for up to 32 parameters.  The use of the DECLARE statement in your main program is required for all procedures and functions which your code accesses, if they are located in DLLs called by your code.  They can also be used to allow forward references to subs and functions.

The complete arguments list must be specified for each routine.  Each parameter may be defined in one of three ways:

  •  List only its type name (INTEGER, DOUBLE, etc.)

  • List a variable name with a type-specifier appended (count%, txt$)

  • Use the AS clause to specify the type (count AS INTEGER, text AS STRING * 100, etc.).

Legal type names for arguments include ANY, ASCIIZ, BYTE, CUR, CUX, DOUBLE, DWORD, EXT, INTEGER, LONG, PTR, QUAD, SINGLE, STRING, WORD and ARRAY.  The ARRAY keyword is used in conjunction with one of the other types to specify an entire array of that type.  For example:

DECLARE SUB KerPlunk(INTEGER ARRAY, DOUBLE)

declares a procedure called KerPlunk, which takes an entire Integer array and a Double-precision variable as parameters.  You can also name the parameters using the AS keyword:

DECLARE SUB KerPlunk(iArray() AS INTEGER, dVar AS DOUBLE)

The following four declare statements are equivalent:

DECLARE SUB KerPlunk(x) ' if DEFINT A-Z is in effect

DECLARE SUB KerPlunk(x%)

DECLARE SUB Plunk(x AS INTEGER)

DECLARE SUB KerPlunk(INTEGER)

When parameters are passed by reference (BYREF), the full 32-bit address of the variable passed to the routine is placed on the stack.

Using ANY disables type checking for a given parameter, and passes the full 32-bit address of the variable passed on the stack.  Since the internal format of variables differ greatly by type, you must use caution to be absolutely certain your code knows the data type in each invocation.  Normally, a second parameter is used to specify the actual type of the ANY parameter.

You can use the BYVAL or BYREF keywords to specify that a given parameter should always be passed by value or by reference, respectively.

When a Sub/Function definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead.  While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity.  It is necessary to explicitly declare all pointer parameters as BYVAL (BYVAL x AS BYTE PTR).  Failure to do so will generate compile-time Error 549 ("BYVAL required with pointers").

Additional information on BYVAL/BYREF/BYCOPY parameter passing can be found in the CALL statement topic.

 

Optional parameters

PowerBASIC now supports two syntax formats for optional parameters: the classic optional parameter syntax using brackets "[..]", and the new syntax using the OPTIONAL (or OPT) keyword.  We'll discuss each one in turn.

 

Using OPTIONAL/OPT

DECLARE statements may specify one or more parameters as optional by preceding the parameter with either the keyword OPTIONAL (or the abbreviation OPT).  Optional parameters are only allowed with CDECL or SDECL calling conventions, not BDECL.

When a parameter is declared optional, all subsequent parameters in the declaration are optional as well, whether or not they specify an explicit OPTIONAL or OPT directive.  The following two lines are equivalent, with both second and third parameters being optional:

DECLARE SUB sABC(a&, OPTIONAL BYVAL b&, OPTIONAL BYVAL c&) AS LONG

DECLARE SUB sABC(a&, OPT BYVAL b&, BYVAL c&) AS LONG

When optional parameters are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled, so BYVAL parameters default to the value 0.  String, User-Defined Type (UDT), and Union variables may also be specified as OPTIONAL BYVAL parameters.

However, care should be exercised with OPTIONAL BYREF parameters, as an attempt to reference a missing value will cause a GPF (like trying to use a null pointer).  If OPTIONAL BYREF parameters are used, it is the programmer's responsibility to avoid this situation by passing other information which defines which optional parameters, if any, have actually been passed.

The OPTIONAL directive provides the same functionality as the older syntax using square brackets "[..]".  See below.

Using classic optional parameters

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as optional, using a set of brackets [..]:

DECLARE SUB KerPlunk CDECL (x%, y% [, z%])

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets.  The following calling sequences would then be valid:

CALL KerPlunk (x%, y%)

CALL KerPlunk (x%, y%, z%)

Optional parameters must be the last parameters designated in the list.  The following is invalid:

DECLARE SUB KerPlunk CDECL ([x%,] y%, z%)

Because the SUB or FUNCTION being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

PowerBASIC continues to  support the use of classic optional parameter syntax using brackets ([..]) but this will not be the case in future versions of PowerBASIC.  Existing code should be changed to the new OPTIONAL syntax as soon as possible to ensure compatibility with future versions of PowerBASIC.

AS type

You may specify the type of data returned by a Function to the calling code.  If you do not specify a type, PowerBASIC assumes that the Function returns the data type specified by a DEFtype statement.  However, if no DEFtype or AS type has been specified, a compile-time error is generated.

Therefore, there are two ways to specify the return type of a Function:

  • Include a type-specifier character at the end of ProcName

  • Include the AS type clause as the last part of the DECLARE statement (this is the recommended syntax to ensure compatibility with future versions of PowerBASIC).

For example, the following statements are equivalent:

DECLARE FUNCTION aFunction?()

DECLARE FUNCTION aFunction() AS BYTE

Restrictions

A Sub/Function may be imported and exported within the same module.  That is, a function in the module may be stated as EXPORT, while a DECLARE in the same module specifies it as an imported function by the option LIB "filename.dll", as long as FILENAME.DLL is the name of the module.  This may be particularly valuable when you wish to build an #INCLUDE file with all of the DECLARE statements for a project.

See also

CALL, CALL DWORD, FUNCTION/END FUNCTION, SUB/END SUB

Example

' Main program

DECLARE SUB Calculate LIB "A.DLL" (EXT, CUR, QUAD, INTEGER)

CALL Calculate(w##, x@, y&&, z%)