Calling Conventions in BCX

C_DECLARE statement

Purpose: The BCX statement C_DECLARE provides a function declaration specification that allows the function to be used with the _cdecl calling convention.


 Syntax 1:

 C_DECLARE [ SUB | FUNCTION ] ProcName(Paramlist)


 Syntax 2:

 C_DECLARE [ SUB | FUNCTION ] ProcName$ _
                        LIB "DllName.Dll" _
                         ALIAS "ProcName" _
                             (Paramlist)

Remarks:

C_DECLARE is used to expose SUBs and FUNCTIONs that have been exported in an External Dynamic Link Library(DLL) as well as SUBs and FUNCTIONs that have been compiled and placed in .obj files or libraries.

The Paramlist follows the same rules as when declaring variables in a normal SUB or FUNCTION declaration. The variables used in the Paramlist are dummies but must communicate the data type, either by using its abbreviated type identifier($%!#) or using the AS clause.

_cdecl is the default C calling convention. The stack is cleaned up by the calling function, so vararg functions can be used. Arguments are pushed on the stack from right to left.

C_DECLARE adds the prototypes to the C code, plus it allows you to declare the function *type* AND its arguments using BASIC syntax, instead of C.

C_DECLARE is not compatible with LoadLibrary If LoadLibrary is to be used then DECLARE must be used as the declaration specification for the functions in the DLL.

For example ...


 C_DECLARE FUNCTION FOO(A AS UINT) AS INTEGER

translates to C code


 int __cdecl FOO(UINT);

which will not work with LoadLibrary.

Instead, you would need to inline:


 int(*FOO)(UINT);

Optional arguments are allowed, although not all compilers support optional arguments for syntax 2. The OPTIONAL keyword is not needed and should not be used.

Both Syntax 1 and Syntax 2 of C_DECLARE support variable arguments(...).

BCX Console Sample Programs using C_DECLARE statement. S123.bas Also study the many examples in the DLL_Demo folder of the BCX distribution.

DECLARE statement

Purpose: The BCX statement DECLARE provides a function declaration specification that allows the function to be used with the _stdcall calling convention.


 Syntax 1:

 DECLARE [ SUB | FUNCTION ] ProcName(Paramlist)


 Syntax 2:

 DECLARE [ SUB | FUNCTION ] ProcName$ _
                      LIB "DllName.Dll" _
                       ALIAS "ProcName" _
                           (Paramlist)

Remarks:

Optional arguments are allowed, although not all compilers support optional arguments for syntax 2. The OPTIONAL keyword is not needed and should not be used.

Once you C_DECLARE or DECLARE a SUB or FUNCTION -correctly- inside your program, you can call it from any other SUB or FUNCTION. In a -CONSOLE- app, you can place DECLARE statements anywhere in your source program.

In a -GUI- program, you must place it a SUB or FUNCTION so that the initialization code has some place valid to run. The smart place to put your DECLARE in a GUI is inside your WinMain FUNCTION -- or if you are using the simplified BCX_XXXXX GUI commands, inside the FormLoad SUB.

In a -DLL- app, you should probably place your DECLARE inside DllMain.

Here is a complete example which calls a DLL function which returns a string.
The example is in three sections, the DLL, a program to call the function from the DLL and a batch file to compile the programs.

The first section is the BCX DLL code. Cut and save this as RESPOND.BAS


 $DLL STDCALL

 FUNCTION Respond(Buf$) EXPORT
  Buf$ = Buf$ & " The Eagle has landed"
  FUNCTION = LEN(Buf$)
 END FUNCTION

The second section is the program which will call the function in the DLL. Cut and save this as TESTDLL.BAS


 DIM a$, l

 DECLARE FUNCTION Respond LIB "respond.dll" ALIAS "Respond"(foo$) AS INTEGER

 a$ = "Boy Howdy"
 l = LEN(a$)
 ? l;" ";a$

 l = Respond(a$)
 ? l;" ";a$

 getchar()

Here, in the third section, is a batch file to translate, compile and link the RESPOND.BAS and TESTDLL.bas.

If Pelle's C is used as the compiler, cut and save the following as BUILD.BAT


 BC Respond
 POCC /Ze /Gn Respond.c
 POLINK /dll Respond.obj
 BC TestDLL
 POCC /Gd /Ze TestDLL.c
 POLINK TestDLL.obj

The PLAYWAV example also demonstrates the use of DECLARE and, as well, how calls are made to a DECLAREd function.

LIB statement

Purpose: BCX provides a universal method for calling both _stdcall and _cdecl DLL functions irrespective of the differences between the two calling conventions.


 Syntax:

 [RetVal =] DLLFunction(LIB "DLLName[.dll]" [, Parameters, ...])

 Parameters:

  • RetVal [OPTIONAL] return value from function being called.
  • DLLFunction Name of the function being called. The name should appear as it is exported from the DLL according to its specifications. Explicit ALIASes as used with DECLARE constructs are not permitted.
  • DLLName Name of the DLL containing the function being called(the extension part is optional). Contrary to DECLARE constructs which load DLLs into memory the moment DLLFunction is declared, this method loads them into memory only if the function is actually called.
  • Parameters [OPTIONAL] parameter list for function being called. Nested calls to other DLL functions may also be used directly as parameters (see Example 2 below).
Remarks:

Unlike the conventional DECLARE and C_DECLARE statements, the dynamic DLL function calling engine provides the following advantages:

To be able to ensure this non-language-specific flexibility, the DLLs being called should meet the following reasonable requirements:

The overwhelming majority of commonly used Windows API, VisualBasic, Delphi, and third-party DLLs are based on these simple rules.

Note well that while the DECLARE/C_DECLARE convention is still supported by BCX for backward compatibility reasons, both conventions should not be used simultaneously in the same code because this will render the dynacall engine's memory management facilities ineffective.

Example 1 :


 MessageBox(LIB "user32", NULL, "Your Title", "Your Msg", MB_OK)

Example 2 :


 SetWindowText(LIB "user32", FindWindow(LIB "user32", "Notepad", 0L), "Hello = !")

BCX_DYNACALL procedure

Purpose: BCX_DYNACALL is a user accessible runtime variant of the of the BCX translator internally used BCX_DynaCall function. This user accessible variant provides, for the user, an easier runtime execution of functions not known at compile time.


 Syntax:

 [RetVal =] BCX_DYNACALL(DLL_Name$, DLLFunction$, NumberOFArgs%, ArgArray)

 Parameters:

  • RetVal [OPTIONAL] return value from function being called.
  • DLL_Name$ Name of the DLL containing the function being called (the extension part is optional). Contrary to the DECLARE constructs which load DLLs into memory at the moment that the DLLFunction is declared, this method loads them into memory only if the function is actually called.
  • DLLFunction$ Name of the function being called. The name should appear as it is exported from the DLL according to its specifications. Explicit ALIASes as used with DECLARE constructs are not permitted.
  • NumberOFParams% Number of DLLFunction$ parameters.
  • ArgArray Array to hold the arguments of the DLLFunction$ parameters. This array can be definedas equal or greater than the maximum number of arguments that a function may use.

Remarks: This variation is mainly to allow a user's program to have the ability to setup functions at runtime. For instance, the functions could be read from a configuration file. The easiest way is just to define an array that is equal or greater than the maximum number of arguments that a function may use. The arguments are then, and must be, cast as integer.

Example:

 DIM AnyType[4] AS INTEGER
 
 AnyType[0] = (INTEGER) NULL
 AnyType[1] = (INTEGER) "Your Title"
 AnyType[2] = (INTEGER) "Your Msg"
 AnyType[3] = (INTEGER) MB_OK
 
 BCX_DynaCall("user32", "MessageBox", 4, AnyType)

STDCALL statement

Purpose: STDCALL is a reserved BCX keyword used in two different contexts.

Remarks:

Win32 API functions and user-defined callbacks use the _stdcall calling convention. _stdcall is used as well by Visual BASIC, and Delphi. When _stdcall is used, the stack is cleaned up by called function, so compiler makes vararg functions cdecl, and a function prototype is required. Arguments pushed on stack right to left.


 Syntax 1:

 $DLL STDCALL


 Syntax 2:

 SUB Foo(Bloof AS DOUBLE) STDCALL