SIMPOL Documentation

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]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!

Example 15.1. SBL program calling SIMPOL function

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 LEFT$() 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.


       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.