Purpose |
|
Syntax |
DECLARE {SUB | FUNCTION} ProcName [BDECL | CDECL | SDECL] [LIB "LibName"] [ALIAS "AliasName"] [([arguments])] [AS type] DECLARE CALLBACK FUNCTION ProcName [[()]
AS LONG] |
Remarks |
The DECLARE statement has the following parts: |
CALLBACK |
When declaring a Callback Function, the CALLBACK keyword follows the DECLARE keyword, and no parameters should be specified. Parentheses and the AS LONG return type may be added for clarity. Callback Functions are reserved for use with Dynamic Dialog Technology (DDT) functions. |
THREAD |
When declaring a Thread function, the THREAD keyword follows the DECLARE keyword. It must take exactly one Long-integer or Double-word (DWORD) parameter by value (BYVAL), and must return a Long-integer or Double-word value. |
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
Future versions of PowerBASIC may not support type-specifier symbols for the Function return type. 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 |
|
Passing parameters |
Arguments |
Contains the name(s) or the type of each parameter, in the order they are passed, for up to 32 parameters. If you wish to call a SUB or FUNCTION in a DLL, you must describe the target SUB or FUNCTION with an explicit DECLARE statement. The DECLARE must physically precede any reference to the target procedure. Previous versions of PowerBASIC required that you create an explicit DECLARE statement if you wished to execute a SUB or function which did not physically precede the reference to it. This extra work is no longer required, as PowerBASIC resolves all forward references to internal procedures automatically. DECLARE statements for a Sub/Function imported from a DLL must still precede any reference to the procedure. The complete arguments list must be specified for each routine. Each parameter may be defined in one of three ways:
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 VARIANT variables are particularly well suited for use as an optional parameter. If the calling code omits an optional VARIANT parameter, (BYVAL or BYREF), PowerBASIC (and most other compilers) substitute a variant of type %VT_ERROR which contains an error value of %DISP_E_PARAMNOTFOUND (&H80020004). In this case, you can check for this value directly, or use the ISMISSING() function to determine whether the parameter was physically passed or not. When optional parameters (other than VARIANT) are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled. If the parameter is defined as a BYVAL parameter, it will have the value zero. For TYPE or UNION variables passed BYVAL, the compiler will pass a string of binary zeroes of length SIZEOF(Type_or_union_var). If the parameter is defined as a BYREF parameter, VARPTR (Varname) will equal zero; when this is true, any attempt to use Varname in your code will result in a General Protection Fault or memory corruption. You should use the ISMISSING() function first to determine whether it is safe to access the parameter. 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:
For example, the following statements are equivalent: DECLARE FUNCTION aFunction?() DECLARE FUNCTION aFunction() AS BYTE While most FUNCTION calling conventions are fairly well defined throughout the industry, there are a few exceptions. In the case of functions which return a Quad Integer value, some programming languages (including PowerBASIC) return the quad value in the FPU, while others return it in EDX:EAX. PowerBASIC automatically detects the method used by imported functions and adjusts accordingly for you, but that's not a feature found in other compilers. Therefore, we recommend that you do not EXPORT QUAD FUNCTIONS unless they will only be accessed by PowerBASIC programs. A simple equivalent functionality would be to return the quad-integer value to the caller in a BYREF QUAD parameter. |
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, ISMISSING, SUB/END SUB |
Example |
' Main program DECLARE SUB Calculate LIB "A.DLL" (EXT, CUR, QUAD, INTEGER) … CALL Calculate(w##, x@, y&&, z%) |