SMEXEC Example Using SBL
In this section we will examine a source code program in SBL that makes calls to a SIMPOL
library called jpeglib.sml
. The complete SBL source
code for this program is called SMEXEC.SBP
and is included in the
samples/SBL
directory.
The program below begins by declaring a few variables that simply hold the location of the various paths for parts of SIMPOL. It also stores the current directory so that it can be restored later and prepares Superbase for using API calls.
Note | |
---|---|
The following program is written in the Superbase Basic Language (SBL). To fit it into the available space, lines have occasionally need to be continued on the following line. The SIMPOL line continuation character, the backslash, has been used for this. However, SBL does not have a line continuation character, so wherever this character is used the following line must be rejoined to the line containing the character and the character must be removed! |
SUB main() DIM cd$,simpolpath$,simpolbin$,simpollib$ simpolpath$ = FN spath("C:\Program Files\SIMPOL\") simpolbin$ = simpolpath$ + "BIN\" simpollib$ = simpolpath$ + "LIB\" cd$ = DIRECTORY REGISTER CLEAR
The next line of the program does a change of directory to the SIMPOL
bin
directory. This is necessary because the
SMEXEC32.DLL
is statically linked to the SMPOL32.DLL
and if it is not in the current directory then the operating system won't be able to find it
unless it is added to the path. SIMPOL is not installed such that anything has to be added
to the path and therefore it is better to make this change. In the future the change should not
be necessary, but to use the functionality at the time of writing, it is necessary to be in the
location of the DLLs.
DIRECTORY simpolbin$
It is also necessary to use the fully qualified path name when laoding the DLL on Windows NT,
2000, and XP because there is no guarantee that on those OS's that the operating system will
look for the DLL in the current directory. The format used in this example will work on all
operating systems and is therefore recommended. If you put the SIMPOL
bin
directory into the path, then the full path names
are not required. In the standard installation the directory is not added to the path.
' * SBUINT UTILFUNC ' SMExec_LoadSMPModule(PSBUBYTE pmodname, ' * SBRAMSIZE modnamecharcount, ' * SBRAMSIZE bytesperchar, ' * PPVOID ppmodinfo) REGISTER simpolbin$ + "SMEXEC32.DLL",\ "SMExec_LoadSMPModule","JCJJM" ' * SBUINT UTILFUNC SMExec_UnloadSMPModule(PVOID pmodinfo) REGISTER simpolbin$ + "SMEXEC32.DLL",\ "SMExec_UnloadSMPModule","JJ" ' * SBUINT UTILFUNC ' * SMExec_RunSMPFunction(PVOID pmodinfo, ' * PSBUBYTE pfuncname, ' * SBRAMSIZE funcnamecharcount, ' * PSBUBYTE pparams, ' * SBRAMSIZE paramcharcount, ' * PSBUBYTE poutput, ' * SBRAMSIZE maxoutputcharcount, ' * PSBRAMSIZE poutputcharcount, ' * SBRAMSIZE bytesperchar ) REGISTER simpolbin$ + "SMEXEC32.DLL",\ "SMExec_RunSMPFunction","JJCJCJFJMJ"
The program now declares a few variables that are used with the calls to the SMEXEC functions. It then clears the screen, prints the start time (this for doing time tests of calls to SIMPOL functions), and assigns the SIMPOL program file and function names to their respective variables.
DIM smp$,func$,params$,h&%(10),res$,pos%% CLS ? "Start time: " + TIME$ ( NOW ,"hh:mm:ss.sss") + " - "; ? DATE$ ( TODAY ,"dd mmm yyyy") ? smp$ = simpollib$ + "JPEGLIB.SML" func$ = "smexec_getjpegsize"
Now we load the compiled SIMPOL program/library (*.smp
or
*.sml
). The parameters to the function call are the program/library name
(smp$
), the length of the program/library name parameter
(LEN (smp$)
), the number of bytes per character (always
1
in SBL but it could be 2
if called by a program that
is operating internally in Unicode), and a pointer or reference to a long integer
where the handle can be stored if the function is successful (h&%(1)
).
In our example, if the call to load the library is successful, a handle to the library is returned
in the variable h&%(1)
. It is necessary to use an array here since
the function needs to assign the handle and this is the only reasonable way to do that in SBL.
Array variables are passed by reference rather than by value. It is also possible to load any
number of SIMPOL programs/libraries at the same time and to make calls to them as needed, freeing
them at the end. They do not need to be loaded and then unloaded right away.
h&% = CALL ("SMExec_LoadSMPModule",smp$, LEN (smp$),1,h&%(1))
At this point we can reset the directory. Prior to loading the program the current directory needs to be that of where the SIMPOL components are located. After the program is loaded the directory can be changed. This is a temporary restriction and it will be removed in later versions.
DIRECTORY cd$
Now we test the return value from loading the program and if it is not equal to zero then something has gone wrong. If an error occurs here, the likelyhood is that the file was either not found or that a required component could not be loaded. The return values will be SIMPOL error values.
IF (h&% <> 0) THEN ? "Error " + STR$ (h&%,".") + " loading '" + smp$ + "'" ELSE
Only one parameter can be passed to the SIMPOL function and it is always a string, but it is easily possible to place multiple parameters inside the string and to separate them with TAB characters. The TAB-separated entries will be assigned in order to each argument (which must be of type string) within the function paramter list. The maximum size of the parameter that can be passed when calling from SBL is the size of a Superbase string, which is 4000 characters. When calling from another language like Visual Basic, the limitation would be the maximum size of a VB string. In the case of this example the parameter being passed is the name of a JPEG file from which the size of the image is to be read.
params$ = "SBLOGO.JPG"
The maximum size of the return value, which is always a string, is 4000 characters. Here the variable is being presized to accomodate the maximum amount being returned.
res$ = SPACE$ (4000)
Now we call the function in the SIMPOL program/library. The return value of the call indicates
whether SIMPOL was able to call the function and if the function had any errors. It is not the
return value of the SIMPOL function. That is returned in the res$
parameter.
The h&%
variable receives the return value and the parameters to the
function call are the handle to the program/library that we received when it was loaded
(h&%(1)
), the name of the function we are calling in the loaded
SIMPOL program/library (func$
), the length of the function name that we
are passing in the previous parameter (LEN (func$)
), the string parameter
that we are passing to the function we are calling (params$
), the length
of the value passed as the parameter (LEN (params$)
), a variable to hold
the return value of our function call (res$
), the size of the return
buffer (4000
), a pointer or reference to a long integer variable that
will receive the number of characters written to the return buffer
(h&%(2)
), and the number of bytes per character that should be used
in the return buffer (in the case of SBL this is always 1
).
h&% = CALL ("SMExec_RunSMPFunction",h&%(1),func$,\ LEN (func$),params$, LEN (params$),\ res$,4000,h&%(2),1) IF (h&% <> 0) THEN ? "Error " + STR$ (h&%,"9999"); ? " executing '" + func$ + "'" ELSE
Assuming that the function call succeeded, we can retrieve from the return value in
res$
the actual string that may have been returned. For safety's sake we are
using the
function to retrieve the number of
characters that we were told was returned in the result. Then we output the results of the
function call, once as we received it to show what the return value actually was, and once after
having interpreted it. Normally a programmer would decide their own best return format and simply
interpret the results as needed.
LEFT$()
res$ = LEFT$ (res$,h&%(2)) ? "Size of the returned result string: "; ? STR$ (h&%(2),".") ? "Result: '" + res$ + "'" pos%% = INSTR (res$," ") IF pos%% THEN ? "The size of the JPEG image called: " + params$; ? " is " + FN numeric( LEFT$ (res$,pos%% - 1)); ? "x" + FN numeric( MID$ (res$,pos%% + 1)) END IF END IF
Now we need to unload the SIMPOL program since we are finished using it. Forgetting to unload the
program could result in memory and resources not being freed. Superbase does not free resources on
behalf of external programs when it closes. It is the responsibility of the programmer that uses
external calls to do that. We pass in the handle to the program (h&%(1)
)
in the unload call. If this handle is incorrect or does not exist, then a GPF
can occur, so it is important to maintain these handle values carefully.
h&% = CALL ("SMExec_UnloadSMPModule",h&%(1)) END IF
Finally, we output the finishing time for comparison purposes and clear the registered API calls from memory.
? :? "End time: " + TIME$ ( NOW ,"hh:mm:ss.sss") + " - "; ? DATE$ ( TODAY ,"dd mmm yyyy") REGISTER CLEAR END SUB
The preceding program should provide a useful template for building calls to SIMPOL functionality
into a program based on SBL. The basic approach should also be clear to anyone working with
similar languages such as Visual Basic, although the REGISTER
command would
need to be replaced with the Declare
syntax.