Modular Programming

Modular programming can be used to break up a large program into manageable units, or to create code that can be easily re-used.  A modular program consists of a main module and one or more auxiliary modules.  Each module originates from a separate source code file.  The main module is compiled as an EXE, and calls functions in the auxiliary modules.  The auxiliary modules can be dynamically linked, meaning they exist as separate executable files (DLLs) which are loaded when the main EXE is run; or they can be statically linked, meaning they are compiled as object files or static libraries (LIBs) that are combined with the main module into a single executable file.

Once you have learned how to create an executable file from an XBasic source program, it is easy to extend the procedure to make dynamically linked modules and (a little less easily) statically linked modules.  There are, however, a few rules that must be followed when writing the source code for the auxiliary modules.

Dynamic Linking

Create a DLL exactly the same way you create an EXE, just begin the process by selecting 'library' from the PDE's 'run' menu, instead of 'assembly' (or use the -lib option if compiling from the command line).  The .mak created by the PDE will have the appropriate commands to make a DLL.  The files should be distributed as follows:

  .dll to the XBasic\bin folder, \Windows, or any folder named in the PATH environment variable in your autoexec.bat.  It can also be placed in the same folder as the main module that calls it.
  .lib to the XBasic\lib folder.  This file will be needed when you compile the main module to create an EXE; it is not needed to run the main module in the PDE.
  .dec to the XBasic\include folder.  This file is needed to run the main module in the PDE, but not to run it as a standalone EXE.
  .mak is the file that nmake.exe uses to create all of the other files.  It is not needed once the DLL has been created.
  .s, the assembly language source code, is a translation of your XBasic source to assembly language.  spasm.exe uses this to create the object (.o) file; no longer needed.
  .o is an object file, containing the binary code for the module, whose contents have been added to the DLL; no longer needed.
  .def, the module definition file, is a text file that lists functions exported by the DLL.  It is used to create the LIB, and is no longer needed.
  .exp is a type of object file listing exported functions; not needed.

To access functions in a DLL named auxiliary.dll from the main module, just include the statement IMPORT "auxiliary" in the main module's PROLOG.  The functions in auxiliary.dll can then be accessed as ordinary function calls.

Static Linking

To prepare an auxiliary module for static linking, follow the procedure just outlined for creating a DLL.  The DLL itself won't be needed, however, nor will the DEC or LIB files - it is the object (.o) file that contains the code that will be statically linked into the final EXE. 

Ordinarily, the auxiliary modules require no special programming considerations when they are to be statically linked, but the main module may need to be written somewhat differently.  Suppose for the purpose of illustration that there is a main module with source code in main.x and two auxiliary modules aux1.x and aux2.x.  There are three possible approaches to static linking, each having its advantages and disadvantages:

  1. The main module IMPORTs the auxiliary modules, and the main.s and main.mak files are created by selecting 'assembly' from the 'run' menu.  This is exactly the same as preparing for dynamic linking.  Before running nmake.exe, however, the main.mak file is edited in a text editor, removing references to the auxiliary .lib files, and adding references to the .o files.  main.mak will contain the lines:

    LIBS = xb.lib aux1.lib aux2.lib


    $(LD) $(LDFLAGS) -out:$(APP).exe xstart.o $(APP).o $(RESOURCES) $(LIBS) $(STDLIBS)

    which are changed to:

    LIBS = xb.lib


    $(LD) $(LDFLAGS) -out:$(APP).exe xstart.o $(APP).o aux1.o aux2.o $(RESOURCES) $(LIBS) $(STDLIBS)

    When the .mak file is processed by nmake.exe, an executable file named main.exe is created, which contains the code from the main module and both auxiliary modules, and does not require aux1.dll or aux2.dll to run.

  2. Same as method 1, except the auxiliary object files are converted to LIB files.  In a DOS window, type

    lib aux1.o
    lib aux2.o

    This will create aux1.lib and aux2.lib, which can be moved to the XBasic\lib folder if desired.  Of course, when the auxiliary modules were compiled in the first place, two files named aux1.lib and aux2.lib were already created.  These files, however, contained only the minimal code necessary to allow main.exe to access aux1.dll and aux2.dll.  The new LIBs contain all of the executable code in the auxiliary modules, and this will be linked into main.exe so that the DLLs are not needed.

    Once the new LIBs are created, main.exe is created as in the previous method.  However, main.mak does not need to be edited, which is the main advantage of this approach.  This method is probably preferable if the auxiliary modules are complete, debugged, and not likely to be changed.

  3. The previous two methods contain a hidden flaw: because main.x IMPORTs the two auxiliary modules, main.exe will actually include code that loads and initializes aux1.dll and aux2.dll, even though the functions in these DLLs are never called (main.exe contains the statically linked code for both auxiliary modules, and uses that code instead of the DLL code).  If the DLLs are not found, the program proceeds normally, so main.exe is still independent of the DLLs; but of course a certain amount of time is required for the operating system to search for and load the unneeded DLLs.

    To avoid this situation requires that the main module be written somewhat differently.  The source code (main.x) should not IMPORT the auxiliary modules; instead, the TYPEs, EXTERNAL FUNCTIONs, and global constants that are normally would be in the DEC files for each auxiliary module must be included in the main.x PROLOG.  Select 'run', 'assembly' to create main.mak, then edit it as described in the first method.  There will be no aux1.lib and aux2.lib in the MAK file, so all that is necessary is to add the references to aux1.o and aux2.o.

    The drawbacks to this method are that main.x cannot be run in the PDE, since it does not IMPORT the auxiliary modules, and that main.mak must be edited every time the main module is re-compiled.

The Entry Function

The entry function is an important and often neglected element in an auxiliary module.  When a DLL is loaded by the main module, whether running in the PDE or as a standalone, the DLL's entry function - the first declared function in the PROLOG, whatever its name - is called automatically.  This causes not only that function's code to be executed, but also some initialization code that prepares the DLL for use.  It is always necessary for an auxiliary module to have an entry function, even if the function has no code.

(Prior to XBasic V6.2.1, the initialization of a DLL was carried out only when the main module (running in the PDE) was recompiled.  If a program that called functions in a DLL was run a second time in the PDE, without recompiling, errors could occur.  If you have this problem, the best solution is to upgrade to the latest XBasic version; otherwise, just select 'recompile' from the 'run' menu before running the program each time.)

The entry function in an auxiliary module is not called automatically when the module is statically linked.  If the entry function is used to initialize the module, it is necessary for the main module to explicitly call it.  This is particularly important for a modular GUI Designer program.  It will not run properly if statically linked, unless the entry function in each auxiliary module is called by the main module before the main module calls its own CreateWindows() function.

The entry function usually has no arguments, but this is not a requirement.  There may be cases in which the entry/initialization function in a DLL may need to be called from a user program with arguments.  This is perfectly legal, but there is one important point: when the DLL is loaded, the entry function will be automatically called with all arguments set to 0.  The function needs to written to handle this situation without error.