[<<Previous Entry] [^^Up^^] [Next Entry>>] [Menu] [About The Guide]
Coding for DLLs
------------------------------------------------------------------------------
Each function which other modules will use must be declared as FAR and must
be listed in the EXPORTS section of the library's module definition file,
described in the section 'Module definition files' later in this chapter.
For DLL functions written in lower level languages such as Assembler, C, C++
and PASCAL, extra compiler switches are usually required to generate the
appropriate entry and exit code for exported functions if they have not been
explicitly declared as exported in the source code (usually using the
__export keyword or an equivalent). When writing DLL code in these languages
please refer to the appropriate section of your compiler manual for these
other special requirements.
For DLL functions written in CA-Clipper there are no special requirements
for code which is to be placed in a DLL, since Blinker handles the entry and
exit to exported functions transparently.
As discussed previously, functions in a DLL can be exported in one of two
ways, by name or by ordinal, controlled by their definitions in the EXPORTS
section of the module definition file. One or both methods can be used for
each function, and any combination of methods can be used in the same DLL.
Exporting by name
When exporting a function by name, the function would typically be given the
name by which it is known internally to the DLL, but it can also be given a
different name by specifying it in the EXPORTS definition. When using C++ or
other languages which use name mangling or decoration, the full mangled or
decorated name must be specified.
For example:
EXPORTS myfunction
will export the function 'myfunction' using its own name, while
EXPORTS newname=myfunction
will use the name 'newname' to export the function 'myfunction'.
The exported name is stored in a table in the header of the DLL exporting
the function, along with the address of the function within the DLL. The DLL
name and the exported name are also stored in the header of each module
which makes a reference to that function. When the referencing module is
loaded, the operating system looks for the requested function name in the
header of the requested DLL and extracts the address of the function from
the table.
At the expense of a slight overhead at load time while looking up all the
names, this method gives the maximum versatility for the DLL, since
functions can be rewritten or moved around inside the DLL without modifying
the calling module in any way.
Exporting by ordinal
When exporting a function by ordinal, the function would be assigned a
unique ordinal number within the DLL, which would be listed in the EXPORTS
definition. The keyword NONAME can be used to ensure that the function name
itself is not available for import.
For example:
EXPORTS myfunction @ 1 NONAME
will export the function 'myfunction' only as ordinal number 1, while:
EXPORTS newname=myfunction @1
will use both the name `newname' and ordinal number 1 to export the function
`myfunction'.
When exporting by ordinal the address of the function within the DLL is
stored in a table in ordinal order, allowing fast lookup at run time. The
DLL name and the ordinal number are stored in the header of each module
which makes a reference to that function. When the referencing module is
loaded, the operating system uses the ordinal number to look up the function
address for the requested function in the header of the requested DLL.
This method has minimal overhead at run time, but is a little less
versatile, as functions must retain the same ordinal number within the DLL
if the calling module is not to be modified. A benefit of exporting by
ordinal is the improved security, since the function names are not stored in
the DLL, so are not available to help hackers gain unintended access to the
functions within the DLL.
DLLs and the stack
Unlike task modules, DLLs do not have their own stack. Instead, they use the
stack segment of the task which called them. This can create problems when a
module calls a function which assumes that the data segment register (DS)
and the stack segment register (SS) hold the same address. This problem is
most likely to occur in small and medium model DLLs, since pointers in these
models are, by default, near pointers. For example, many C run time library
functions treat DS and SS as equal, so are not suitable for use in a DLL.
Care should therefore be taken in placing existing compiled code into a DLL,
such as compiler libraries and add on libraries for which the source is not
available.
This is another reason that lower level language code must be compiled with
the correct compiler options, as mentioned in the previous section.
CA-Clipper is the notable exception to this rule, since its code is actually
compiled to pseudo code which makes no assumptions about segments registers
at all.
If a problem arises with a function in a DLL, always try the same code in
the main .EXE to ensure that the problem is not this assumption about DS
being equal to SS.
Version control
The major advantage to DLLs is that they can be transparently replaced
without modifying the rest of the program, but this can lead to problems of
its own if a DLL is replaced by a newer or older version which appears
compatible in terms of function names or ordinal numbers, but is in fact
incompatible.
For this reason it is strongly recommended that the programmer implement
some form of version control or feature checking in any DLLs which may
change significantly over time.
In the simplest case this could be a single function which returns a version
number which can be checked for at least a certain version. A more reliable
method would be to return a set of flags indicating whether certain features
or functions are, or are not, available.
Online resources provided by: http://www.ClipX.Net --- NG 2 HTML conversion by Dave Pearson