Declare (statement)

Syntax

Declare {Sub | Function} name[TypeChar] [CDecl | Pascal | System | StdCall] _
  [Lib "LibName$" [Alias "AliasName$"]] [([ParameterList])] [As type]

Where ParameterList is a comma-separated list of the following (up to 30 parameters are allowed):

[Optional] [ByVal | ByRef] ParameterName[()] [As ParameterType]

Description

Creates a prototype for either an external routine or a Basic Control Engine routine that occurs later in the source module or in another source module.

Comments

Declare statements must appear outside of any Sub or Function declaration.

Declare statements are only valid during the life of the script in which they appear.

The Declare statement uses the following parameters:

 

Parameter

Description

 

name

Any valid script name. When you declare functions, you can include a type-declaration character to indicate the return type.

This name is specified as a normal script keyword—i.e., it does not appear within quotes.

 

TypeChar

An optional type-declaration character used when defining the type of data returned from functions. It can be any of the following characters: #, !, $, @, %, or &. For external functions, the @ character is not allowed.

Type-declaration characters can only appear with function declarations, and take the place of the As type clause.

Note: Currency data cannot be returned from external functions. Thus, the @ type-declaration character cannot be used when declaring external functions.

 

CDecl

Optional keyword indicating that the external subroutine or function uses the C calling convention. With C routines, arguments are pushed right to left on the stack and the caller performs stack cleanup.

 

Pascal

Optional keyword indicating that this external subroutine or function uses the Pascal calling convention. With Pascal routines, arguments are pushed left to right on the stack and the called function performs stack cleanup.

 

System

Optional keyword indicating that the external subroutine or function uses the System calling convention. With System routines, arguments are pushed right to left on the stack, the caller performs stack cleanup, and the number of arguments is specified in the AL register.

 

StdCall

Optional keyword indicating that the external subroutine or function uses the StdCall calling convention. With StdCall routines, arguments are pushed right to left on the stack and the called function performs stack cleanup.

 

LibName$

Must be specified if the routine is external. This parameter specifies the name of the library or code resource containing the external routine and must appear within quotes.

The LibName$ parameter can include an optional path specifying the exact location of the library or code resource.

 

AliasName$

Alias name that must be given to provide the name of the routine if the name parameter is not the routine's real name. For example, the following two statements declare the same routine:

 

 

Declare Function GetCurrentTime Lib "user" () As Integer

Declare Function GetTime Lib "user" Alias "GetCurrentTime" _
 As Integer

 

 

Use an alias when the name of an external routine conflicts with the name of an internal routine or when the external routine name contains invalid characters.

The AliasName$ parameter must appear within quotes.

 

type

Indicates the return type for functions.

For external functions, the valid return types are: Integer, Long, String, Single, Double, Date, Boolean, and data objects.

Note: Currency, Variant, fixed-length strings, arrays, user-defined types, and OLE automation objects cannot be returned by external functions.

 

Optional

Keyword indicating that the parameter is optional. All optional parameters must be of type Variant. Furthermore, all parameters that follow the first optional parameter must also be optional.

If this keyword is omitted, then the parameter being defined is required when calling this subroutine or function.

 

ByVal

Optional keyword indicating that the caller will pass the parameter by value. Parameters passed by value cannot be changed by the called routine.

 

ByRef

Optional keyword indicating that the caller will pass the parameter by reference. Parameters passed by reference can be changed by the called routine. If neither ByVal or ByRef are specified, then ByRef is assumed.

 

ParameterName

Name of the parameter, which must follow the script's naming conventions:

1.   Must start with a letter.

2.   May contain letters, digits, and the underscore character (_). Punctuation and type-declaration characters are not allowed. The exclamation point (!) can appear within the name as long as it is not the last character, in which case it is interpreted as a type-declaration character.

3.   Must not exceed 80 characters in length.

Additionally, ParameterName can end with an optional type-declaration character specifying the type of that parameter (that is, any of the following characters: %, &, !, #, @).

 

()

Indicates that the parameter is an array.

 

ParameterType

Specifies the type of the parameter (e.g., Integer, String, Variant, and so on). The As ParameterType clause should only be included if ParameterName does not contain a type-declaration character.

In addition to the default data types, ParameterType can specify any user-defined structure, data object, or OLE automation object. If the data type of the parameter is not known in advance, then the Any keyword can be used. This forces the compiler to relax type checking, allowing any data type to be passed in place of the given argument.

 

 

Declare Sub Convert Lib "mylib" (a As Any)

 

 

The Any data type can only be used when passing parameters to external routines.

 

Passing Parameters

By default, arguments are passed by reference. Many external routines require a value rather than a reference to a value. The ByVal keyword does this. For example, this C routine

  void MessageBeep(int);

would be declared as follows:

  Declare Sub MessageBeep Lib "user" (ByVal n As Integer)

As an example of passing parameters by reference, consider the following C routine which requires a pointer to an integer as the third parameter:

  int SystemParametersInfo(int,int,int *,int);

This routine would be declared as follows (notice the ByRef keyword in the third parameter):

Declare Function SystemParametersInfo Lib "user" (ByVal action As Integer,_

    ByVal uParam As Integer,ByRef pInfo As Integer,_
    ByVal updateINI As Integer) As Integer

Strings can be passed by reference or by value. When they are passed by reference, a pointer to the internal handle to the string is passed. When they are passed by value, the script passes a 32-bit pointer to a null-terminated string (that is, a C string). If an external routine modifies a passed string variable, then there must be sufficient space within the string to hold the returned characters. This can be accomplished using the Space function, as shown in the following example:

 

Declare Sub GetWindowsDirectory Lib "kernel" (ByVal dirname$,ByVal length%)

    :
  Dim s As String
  s = Space(128)
  GetWindowsDirectory s,128

 

Another alternative to ensure that a string has sufficient space is to declare the string with a fixed length:

 

Declare Sub GetWindowsDirectory Lib "kernel" (ByVal dirname$,ByVal length%)

    :
  Dim s As String * 128   'Declare a fixed-length string.
  GetWindowsDirectory s,len(s) 'Pass it to an external subroutine.

 

Calling Conventions with External Routines

For external routines, the argument list must exactly match that of the referenced routine. When calling an external subroutine or function, the script needs to be told how that routine expects to receive its parameters and who is responsible for cleanup of the stack.

The following table describes which calling conventions are supported on which platform, and indicates what the default calling convention is when no explicit calling convention is specified in the Declare statement.

 

Passing Null Pointers

To pass a null pointer to an external procedure, declare the parameter that is to receive the null pointer as type Any, then pass a long value 0 by value:

  Declare Sub Foo Lib "sample" (ByVal lpName As Any)

  Sub Main()

    Sub Foo "Hello"    'Pass a 32-bit pointer to a null-terminated string
    Sub Foo ByVal 0&   'Pass a null pointer
  End Sub

 

Passing Data to External Routines

The following table shows how the different data types are passed to external routines:

 

Data Type

Is Passes As

 

ByRef Boolean

A 32-bit pointer to a 2-byte value containing –1 or 0.

 

ByVal Boolean

A 2-byte value containing –1 or 0.

 

ByVal Integer

A 32-bit pointer to a 2-byte short integer.

 

ByRef Integer

A 2-byte short integer.

 

ByVal Long

A 32-bit pointer to a 4-byte long integer.

 

ByRef Long

A 4-byte long integer.

 

ByRef Single

A 32-bit pointer to a 4-byte IEEE floating-point value (a float).

 

ByVal Single

A 4-byte IEEE floating-point value (a float).

 

ByRef Double

A 32-bit pointer to an 8-byte IEEE floating-point value (a double).

 

ByVal Double

An 8-byte IEEE floating-point value (a double).

 

ByVal String

A 32-bit pointer to a null-terminated string. With strings containing embedded nulls (Chr$(0)), it is not possible to determine which null represents the end of the string. Therefore, the first null is considered the string terminator.

An external routine can freely change the content of a string. It cannot, however, write beyond the end of the null terminator.

 

ByRef String

A 32-bit pointer to a 2-byte internal value representing the string. This value can only be used by external routines written specifically for the Basic Control Engine.

 

ByRef Date

A 32-bit pointer to an 8-byte IEEE floating-point value (a double).

 

ByVal Date

An 8-byte IEEE floating-point value (a double).

 

ByRef Currency

A 32-bit pointer to an 8-byte integer scaled by 10000.

 

ByVal Currency

An 8-byte integer scaled by 10000.

 

ByRef Variant

A 32-bit pointer to a 16-byte internal variant structure. This structure contains a 2-byte type (the same as that returned by the VarType function), followed by 6 bytes of slop (for alignment), followed by 8 bytes containing the value.

 

ByVal Variant

A 16-byte variant structure. This structure contains a 2-byte type (the same as that returned by the VarType function), followed by 6 bytes of slop (for alignment), followed by 8 bytes containing the value.

 

ByVal Object

For data objects, a 32-bit pointer to a 4-byte unsigned long integer. This value can only be used by external routines written specifically for the Basic Control Engine.

For OLE automation objects, a 32-bit pointer to an LPDISPATCH handle is passed.

 

ByRef Object

For data objects, a 32-bit pointer to a 4-byte unsigned long integer that references the object. This value can only be used by external routines written specifically for the Basic Control Engine.

For OLE automation objects, a 32-bit pointer to a 4-byte internal ID is passed. This value can only be used by external routines written specifically for the Basic Control Engine.

 

User-defined type

A 32-bit pointer to the structure. User-defined types can only be passed by reference.

It is important to remember that structures in Basic Control Engine scripts are packed on 2-byte boundaries, meaning that the individual structure members may not be aligned consistently with similar structures declared in C.

 

Arrays

A 32-bit pointer to a packed array of elements of the given type. Arrays can only be passed by reference.

 

Dialogs

Dialogs cannot be passed to external routines.

 

Only variable-length strings can be passed to external routines; fixed-length strings are automatically converted to variable-length strings.

 

The Basic Control Engine passes data to external functions consistent with that routine's prototype as defined by the Declare statement. There is one exception to this rule: you can override ByRef parameters using the ByVal keyword when passing individual parameters. The following example shows a number of different ways to pass an Integer to an external routine called Foo:

 

  Declare Sub Foo Lib "MyLib" (ByRef i As Integer)

  Sub Main

    Dim i As Integer
    i = 6
    Foo 6      'Passes a temporary integer (value 6) by reference
    Foo i      'Passes variable "i" by reference
    Foo (i)    'Passes a temporary integer (value 6) by reference
    Foo i + 1    'Passes temporary integer (value 7) by reference
    Foo ByVal i  'Passes i by value
  End Sub

 

The above example shows that the only way to override passing a value by reference is to use the ByVal keyword.

Note: Use caution when using the ByVal keyword in this way. The external routine Foo expects to receive a pointer to an Integer—a 32-bit value; using ByVal causes the Basic Control Engine to pass the Integer by value—a 16-bit value. Passing data of the wrong size to any external routine will have unpredictable results.

Example

Declare Function GetProfileString Lib "kernel32" Alias "GetProfileStringA" (ByVal lpAppName As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long) As Long

Sub Main()

    SName$ = "Intl"        'Win.ini section name.

    KName$ ="sCountry"      'Win.ini country setting.

    ret$ = String(255,0)     'Initialize return string.

    myvalue = GetProfileString( SName$, KName$, "", ret$, Len(ret$) )

    If myvalue Then

    MsgBox "Your country setting is: " & ret$

    Else

    MsgBox "There is no country setting in your win.ini file."

    End If

End Sub

See Also

Call (statement), Sub...End Sub (statement), Function...End Function (statement).

Notes

Under Win32, eternal routines are contained in DLLs. The libraries containing the routines are loaded when the routine is called for the first time (that is, not when the script is loaded). This allows a script to reference external DLLs that potentially do not exist.

All the Win32 API routines are contained in DLLs, such as "user32", "kernel32", and "gdi32". The file extension ".exe" is implied if another extension is not given.

 

The Pascal and StdCall calling conventions are identical on Win32 platforms. Furthermore, on this platform, the arguments are passed using C ordering regardless of the calling convention -- right to left on the stack.

 

If the libname$ parameter does not contain an explicit path to the DLL, the following search will be performed for the DLL (in this order):

  1. The directory containing the Basic Control Engine scripts.

  2. The current directory.

  3. The Windows system directory.

  4. The Windows directory.

  5. All directories listed in the path environment variable.

 

If the first character of aliasname$ is #, then the remainder of the characters specify the ordinal number of the routine to be called. For example, the following two statements are equivalent (under Win32, GetCurrentTime is defined as GetTickCount, ordinal 300, in kernel32.dll):

Declare Function GetTime Lib "kernel32.dll" Alias "GetTickCount" () As Long

Declare Function GetTime Lib "kernel32.dll" Alias "#300" () As Long

More information

D