Invoking a DLL function (original Enhancement)

Internal or external functions and subprograms except DEF can be defined using a DLL.
In that case, the following style of ASSIGN statement is written between the FUNCTION line and the END FUNCTION line or the SUB line and END SUB line.

ASSIGN DLL-file-name , function-name
where DLL-file-name and function-name are string constants.

Example
FUNCTION GetVersion
ASSIGN "kernel32.dll","GetVersion"
END FUNCTION

DLL-file-name indicates the DLL that contains the function we want to use. Function-name indicates the function in the DLL. Function-name is case sensitive.

Numeric arguments are evaluated as 32bit integers cutting off the upper digits to be passed. The arguments are always passed by value even if the procedure is a SUB program.
A string argument is passed a 32bit pointer that points the first character of it. But it is a null string, the null pointer shall be passed, which is not a pointer to a null string.
Arguments are laid into the stack in order of last parameter first. the DLL function to be called must erase the arguments on the stack before it leaves.
When a function is defined as numerical, the result of it shall be the value of EAX register at the time when the controls returns. That value is appreciated as a signed integer.
When a function is defined as a string function, it must be a function that returns a pointer that points a null terminated ASCII (or ANSI) string.

Example 1
DECLARE EXTERNAL FUNCTION MesBox
LET n=MesBox(0,"Hello","BASIC",3)
PRINT n
END
EXTERNAL FUNCTION MesBox(owner,text$,caption$,flag)
ASSIGN "user32.dll","MessageBoxA"
END FUNCTION


We use a string variable for a DLL function's argument which is an address of a variable. We prepare sufficient size of memory that the DLL function requires for the argument by making the length of the string as long as required, using a REPEAT$-function or so. We cannot make that string variable by substituting another string variable for it. And that string variable cannot have been substituted for any other string variable.
We can get the received value using substring operation. A string variable can be used a byte buffer when OPTION CHARACTER BYTE is declared in the program unit where the substring operation shall be done.
Example 2
DECLARE EXTERNAL FUNCTION CurrDir$
PRINT CurrDir$
END
EXTERNAL FUNCTION CurrDir$
OPTION CHARACTER BYTE
FUNCTION GetCurrentDirectory(n,s$)
ASSIGN "kernel32.dll","GetCurrentDirectoryA"
END FUNCTION
LET s$=Repeat$(" ", 200)
LET n=GetCurrentDirectory(200,s$)
LET CurrDir$=s$(1:n)
END FUNCTION



Use a following form of an ASSIGN statement to use a DLL function of which return value is a float.
ASSIGN DLL-file-name, function-name, FPU
This type of ASSIGN statement pops the FPU stack top and let it to be the function result.

<Supplement.> An ASSIGN statement raises an exception of extype -9900 when it receives an OS exception, while exceptions that might be occur in a DLL ought to be handled in the DLL.
<Supplement> An ASSIGN statement does not preserve the FPU flags, the DLL function must restore the FPU flags before the return if it alters them.
<Notice.> The DLL is loaded on compiling. When another DLL linking it occurs a load error, the load error shall be shown with the name of specified in the program.
<Notice.> An error on the number of arguments shall not be checked when the program is compiled and when the program runs. Thus a mistake on the number of parameters causes a miserable result.

<Note.> A function that converts a signed 32 bit integer to the hexadecimal representation of its unsigned evaluation.
BSTR$(MOD(n, 4294967296),16)

<Reference.> ORD-function and CHR$-function, Substring