The traditional way of linking programs, when all object modules are collected into a single executable, is called static linking, because all references between modules are resolved statically, at link time. This approach has certain disadvantages:
Dynamic linking allows several applications to use a single copy of an executable module (dynamic link library, or DLL). The external references to procedures and variables built into a DLL are resolved at load time or even at run time instead of link time. Applications may use these procedures and variables as though they were part of the applications’ executable code.
The major advantages of dynamic linking are:
Some applications, such as Netscape browsers, may be enhanced with third-party plug-ins implemented as DLLs with a known interface.
The Windows operating system makes heavy use of DLLs. For instance, system DLLs provide interfaces to the kernel and devices.
A dynamic link library has a table which contains names of entities (procedures and variables) which are exported — made available to other executable components, along with the information about their location within the DLL. Table entries also have numbers associated with them — so called ordinal numbers.
In turn, an executable file (EXE or DLL) referencing the DLL contains, for each reference, a record with the DLL name and a name or an ordinal number of an exported entity. When the operating system loads the executable, it also loads all the DLLs it references, and tunes up the executable’s code so that it will call DLL procedures and access DLL variables. This is called load-time dynamic linking.
Alternatively, an application may explicitly load a DLL at run-time using an API call. Another call retrieves an address of a procedure or variable exported from the loaded DLL, given its name or ordinal number. This is called run-time dynamic linking.
See Using your DLL for more information on these two types of dynamic linking.
If you want to separate one or more sets of modules from your existing application into DLLs, perform the following steps:
-GENDLL+ -USEDLL+ -DLLEXPORT+ -IMPLIB- -LOOKUP=*.mod|*.def|*.ob2=<dll source files directory> !module module1 !module module2 . . .
where module1, module2, etc. are the modules which has to be visible outside the DLL.
You may link the XDS run-time library into your application either statically or dynamically. The USEDLL option controls which version of the run-time library is to be used. By default, this option is set OFF, causing the run-time library to be statically linked. With this setting, your application is linked and executed exactly as it was done with Native XDS-x86 v2.2x.
If your application suite contains more than one EXE file, you may wish to use the dynamically linked version of the run-time library, in order to save disk space and memory.
Note: If your application itself is dynamically linked, it is essential to use the dynamically linked run-time library to ensure that all executable modules use the same unique instance of the run-time library data during execution.
See Chapter Redistributable components for the run-time library redistribution rights.
For your dynamically linked application to function properly, it is essential to correctly set up the related compiler options, so please read this section carefully.
First of all, the USEDLL compiler option has to be set ON during compilation of each module, regardless of whether it will be linked into an EXE or a DLL, indicating that a dynamically-linked version of the run-time library is to be used.
The GENDLL compiler option must be set ON during compilation of modules which are to be built into a DLL, and OFF for modules which will constitute the EXE file.
The compiler stores the name of the DLL into which a module will be linked in that module’s symbol file. This information may be used then for linking without import libraries.
In the COMPILE and MAKE modes, specify the target DLL name in the DLLNAME equation. In the PROJECT mode, if DLLNAME is not set, the name of the project file is assumed.
Warning: Under Windows NT/95, the compiler uses information about DLL names when producing code for dynamically linked variables access. If your program crashes or incorrectly behaves trying to access such a variable, make sure that you comply with the above requirement. You may need to remove all symbol files or use the ALL submode.
Depending on whether you want to use import libraries, you may also wish to specify the IMPLIB compiler option. Use the DLLEXPORT option to control which objects are exported from the DLL (see Controlling export from a DLL).
To summarize, if you invoke the compiler in the COMPILE mode, you have to specify at least the following options on the command line:
When compiling a DLL module:
-USEDLL+ -GENDLL+ -DLLNAME=name of the DLL
When compiling an EXE module:
To allow the MAKE mode to be used, you have to orgranize your source files and redirections as if you were using the PROJECT mode (see below).
If you prefer to use the compiler in the PROJECT mode, create a project file for each executable component (EXE or DLL). You have to place the source files, including Modula-2 definition modules, belonging to different executable components into separate directories. Then, use LOOKUP directives in your project files to ensure that during processing of each project source files belonging to other projects are not visible to the compiler via redirections.
Warning: If you fail to comply with the above requirement, the make subsystem may include some object files into more than one executable module. Also, wrong code may be produced by the compiler for dynamically linked variables access in the Windows NT/95 environment.
In the PROJECT mode, if the DLLNAME equation was not set, the compiler substitutes the name of the project file (without path and extension, of course). So you do not have to care about this setting, as long as your DLLs are named after their project files.
Thus, project files for your DLLs have to contain at least the following lines:
-LOOKUP=*.def|*.mod|*.ob2=<this DLL sources directory>
-DLLNAME=<name of the DLL>
The last line is not required if the name of the DLL matches the project file name.
Projects for EXE files must include the following lines:
-LOOKUP=*.def|*.mod|*.ob2=<this EXE sources directory>
Any public identifier (procedure or variable) from the object files linked together to form the DLL may be made available (exported) to other executable modules /Under Windows NT/95, most compilers do not allow variables to be exported from DLLs. This is a nearly unique feature of XDS./ . There are three ways to control which identifiers are to be exported:
This option may be specified in a project file, in a module header, or even in-line, allowing you to precisely select objects to be exported from the DLL:
<* DLLEXPORT - *> DEFINITION MODULE M; PROCEDURE Internal; (* This procedure is only visible inside the DLL. *) <* DLLEXPORT+ *> PROCEDURE Exported; (* This procedure is available to other executables. *) <* DLLEXPORT - *> END M.
The compiler always produces export rtecords for automatically generated entities, such as module intialization routines (module_BEGIN) and global variables containing descriptors of Oberon-2 types and modules.
LIBRARY MyDLL DESCRIPTION 'My DLL' EXPORTS Exported=M_Exported @1 M_BEGIN @2
Use the /DLL linker option to pass the name of the EDF:
xlink . . . /dll=mydll.edf
You may switch to this method at any time. Once you have built a DLL without an EDF, you may use the XDS Library Manager to produce it (see Export info extraction). Next time you link the DLL, you may use this file, or a modified version of it.
xlink . . . /EXPORT=Exported.1=M_Exported,M_BEGIN.2
This method has the same capabilities as export definition files.
Note: If you supply an EDF file containing an EXPORT section to the XDS Linker, export definition records in object files will be ignored, so the DLLEXPORT option will have no effect. The export infomation supplied via the /EXPORT linker option is always used, but if it conflicts with .EDF file settings, a warning will be issued, and .EDF file settings will be used.
The advantages and disadvantages of the three methods are discussed below.
Using the DLLEXPORT compiler option, you do not have to introduce an extra entity (export definition files) or care about linker options; export information is specified right in the source text, so it never goes out of sync, and export is performed transparently. However, in this case export can only be performed by name; ordinal numbers may change from one link to another, so clients of the DLL should not rely on ordinal numbers. You may not use name mapping: internal and external names are always the same. Finally, if you link any third-party object files or libraries not containing export definiton records into your DLL, you will not be able to export any entities from these files outside the DLL.
EDF usage involves some extra work, but gives you full control on ordinal numbers assignment and mapping of internal names specified in object files to arbitrary external names.
The /EXPORT linker option is rarely used, as the same result may be achieved via EDF. You may, however, consider using it if your DLL exports just a few entities.
In general, we would recommend to use DLLEXPORT during initial development of the DLL and switch to EDF before shipping, so that you will be able to update just the DLL without risk of compatibility problems.
The operating system transfers control to the DLL entry point right after it is loaded and right before it is unloaded, in order to enable the DLL to carry out the required initialization or cleanup /Under Windows NT/95, this also happens when a thread is about to be started or terminated in the application which uses the DLL. DLLs built with the XDS startup code ignore these calls./ .
The default XDS DLL startup code performs all the actions required for the DLL to function properly in the XDS run-time environment. In addition, if there is a main module /A Modula-2 program module or an Oberon-2 module compiled with the MAIN option set ON./ linked into the DLL, its initialization and finalization bodies will be called after the DLL is loaded and before it is unloaded, respectively. This feature provides a convenient way to add your own DLL initialization and termination code and also allows run-time linked DLLs to be properly initialized (see Run-time dynamic linking).
Once you have built a DLL, you may reference the code and data contained in it from an executable module or another DLL.
There are two types of dynamic linking: load-time and run-time, briefly introduced in section How does the dynamic linking work.
Load-time dynamic linking is similar to static linking in that you may reference procedures and variables in your source code regardless of whether they will reside in the same component (EXE or DLL) or not. Then you link the component which uses DLLs with an import library, which contains no code or data but references to DLLs where the particular procedures or variables are located. The resulting executable module or DLL incorporates these references, which are resolved by the operating system when it loads your application. The referenced DLLs are loaded into memory and stay there until your application terminates. If the operating system fails to resolve any of the references, it stops loading the application and returns an error.
Run-time dynamic linking involves explicit loading of DLLs using an operating system API call. Addresses of the procedures and variables in the loaded DLL may only be retrieved by their names via API calls, with no type checking. However, linking at run-time has certain advantages:
In XDS, there are two ways to link with a DLL: one involves usage of import libraries, which may only be built on a separate step, while another is based on the XDS compiler ability to embed import definition sections in object files and thus is more convenient. However, in some cases, discussed below, import libraries have to be used. You may use both methods simultaneously.
The IMPLIB option controls whether the currently compiled project may be linked without import libraries:
In this case, import will be performed either by name or by ordinal (see How does the dynamic linking work), depending on how the import library was built.
As symbol files do not contain ordinal numbers, import can only be performed by name.
This method requires that during creation of a symbol file the compiler knew the name of the DLL into which the correspondent object file was is linked (see Setting up the environment). The compiler then uses that name in import definition records.
Importing by ordinal speeds up program load, but requires extra efforts to preserve ordinal numbers when the DLL is updated. Win32 API function ordinals differ even between ServicePaks.
You may directly use Win32 API functions to handle the DLLs your application needs to load at run-time. You may also consider usage of the XDS run-time library calls to ensure portability.
The library module dllRTS provides means for loading and unloading DLLs, and obtaining addresses of procedures and data contained in loaded DLLs. Some other useful functions are available.
If the DLL is linked at load-time, initialization and finalization of all modules exported from it and used by other load-time linked components is performed automatically as if it was linked statically. In case of run-time linking, the operating system only executes DLL startup code (see DLL initialization and termination). You have the following options:
VAR hmod: dllRTS.HMOD; init: PROC; . . . hmod := dllRTS.LoadModule("MyDLL"); init := dllRTS.GetProcAdr(hmod, "MyModule_BEGIN"); init; . . .
MODULE MyDLLInitTerm; IMPORT MyModule; . . . BEGIN (* DLL initialization *) . . . FINALLY (* DLL termination *) . . . END MyDLLInitTerm.
If you develop a product which is a single DLL or a set of DLLs expected to be used by third-party applications (e.g. a Netscape plug-in or a REXX external function package), you have to ensure that the Modula-2 and Oberon-2 modules which constitute your DLL(s) are initialized. See DLL initialization and termination and Run-time dynamic linking for more information.
You also have to specify a different calling and naming convention for the procedures exported from your DLL, which are to be called by a third-party application. See Direct language specification for more information on interfacing to foreign languages. In general, unless your DLL will be used with a particular application, avoid using the "C" convention, as it depends slightly on the CC equation setting, thus bounding your product to a particular C compiler. Instead, use "StdCall" on Windows.
If your product consists of exactly one DLL, you may wish to statically link it with the XDS run-time library, so that you don’t have to redistribute the dynamically linked version of the run-time library with your product. This is the only case when you may have the USEDLL option switched OFF while a DLL is built (GENDLL in ON).