A DLL is a Dynamic Link Library. This acronym means that
the library is loaded when the program starts by the loader, and not linked
statically with the program code like with a normal library.
To
build such a library:
1) Define your project as a DLL project. If you are using lcclnk directly in the command line, you should add the –DLL option to the linker.
This
is done in the project definition panel by checking the corresponding radio
button.
2) In your code, mark the functions of the DLL you want to export as such.
An
“exported” function is a function that is visible outside
the DLL, and can be called from other places in the program. There are two ways
of doing this.
The
first is by declaring it explicitly in the program text:
int
__declspec(dllexport) MyFunction(int f) { … }
This
instructs the compiler to add a special instruction for the linker to add this
name (in this case MyFunction ) to
the export table of the DLL.
The
second method is to build a definition file (.def) where you write the list of
exported functions from the DLL. This is just an ASCII text file whose extension
must be .def, with the list of the exported functions of the
DLL. An example of such a file is:
EXPORTS
MyFunction
The
first line must be the keyword EXPORTS, followed by the names of the exported
functions, one in each line.
The formal syntax is:
IDENTIFIER
[DATA]
IDENTIFIER
is a placeholder for the name of the exported symbol. It can be followed by the
keyword DATA, which means that the exported symbol is not a function, but a data
item. This is important to specify, since the entries generated in the import
library are not the same.
3) You should build an import library.
This
is done automatically by lcclnk. If you want to do it manually, you should type
the following command:
buildlib
mydll.exp mydll.lib
The
‘buildlib’ utility takes an ASCII description of the DLL exported entries in
a similar format as the .def described above, and will output a library that can
be used with the linker to build the finished product.[1]
4)
You
should declare the imported symbols in the executable that uses the DLL.
This means, add an “extern” declaration in your code or header
file.
5)
You should link your executable with the import library.
You
are then finished.
To
debug your DLL with Wedit’s debugger, change the name of the executable to
start in the project configuration tab ‘Debugger’ and write the name of the
executable that uses your DLL. Then, follow the executable until a DLL function
call appears. Next, press F8 to enter the function in the DLL. Remember that
both the executable and the DLL should have been compiled with debug information
turned on!
DLLs
should have a DllMain or LibMain entry point function,
that is called up by the system when the DLL is loaded or unloaded, and allows
you to initialize/cleanup elements. This function MUST be declared as follows:
#include <windows.h>
/*----------------------------------------------------------------------
Procedure: LibMain
Purpose: DLL entry point called up when a DLL is loaded or
unloaded
by a process, and when new threads are
created or destroyed.
Input:
hDllInst: Instance handle of
the DLL
fdwReason: event:
attach/detach
lpvReserved: not
used
Output:
The
return value is used only when the fdwReason is
DLL_PROCESS_ATTACH. True
means that the DLL has
successfully
loaded. False means that the DLL is unable
to
initialize and should be unloaded immediately.
Errors:
----------------------------------------------------------------------*/
BOOL WINAPI LibMain(HINSTANCE hDLL,DWORD Reason,LPVOID Reserved)
{
switch (Reason) // Code indicating the reason for this function being called up.
{
case DLL_PROCESS_ATTACH:
//
The DLL is being loaded for the first time by a given process.
//
Perform per-process initialization here. If
the initialization
// is successful, return TRUE; if unsuccessful, return FALSE.
break;
case DLL_PROCESS_DETACH:
//
The DLL is being unloaded by a given process.
Do any
//
pre-process clean up here, such as undoing what was done in
// DLL_PROCESS_ATTACH. The return value is ignored.
break;
case DLL_THREAD_ATTACH:
//
A thread is being created in a process that has already loaded
//
this DLL.
Perform any per-thread initialization here. The
// return value is ignored.
break;
case DLL_THREAD_DETACH:
//
A thread is exiting cleanly in a process that has already
//
loaded this DLL. Perform any
per-thread clean up here. The
//
return value is ignored.
break;
}
return TRUE;
}
This file is needed to define the exports of a DLL or mark the data sections of an executable as shared, i.e., usable by other programs.
The different directives in this file are:
· EXPORTS. This is followed by the exports list. The exports list should have an entrgy in each line, for instance: MyFunction or _MyFunction=Function, to change the name of the exported symbol.
· SECTION. This should be followed by one single line containing DATA SHARED, if you want to make the data sections (.data AND .bss) shared.
· STACKSIZE. This is ignored and is here for compatibility with older versions of the .def files.
· DESCRIPTION. This is ignored and is here for compatibility with older versions of the .def files.
An example of this is the following file:
Library Mydll
EXPORTS
Myfunction
_MyOtherFunction=Other
SECTION
DATA SHARED
The first line must contain the name of the DLL or file that you are building, but this is not required. The Exports section contains two exports: A function called “MyFunction”, and a function called “Other”, which is associated to the “MyOtherFunction” procedure, and which will not be visible with that name, since its exported name will be “Other”.
The SECTION part defines the data sections (.data and .bss sections) as shared, i.e., accessible by other programs.
[1]
The format of the .exp file is as follows :
The first line
should contain the name of the DLL you are using, i.e., Mydll.dll or
whatever. Since it is a file name, case is NOT significant. No path
information should be included. Just the name of the DLL.
The following
lines should contain three columns, separated by tabs or spaces.
The first one is
the name of the symbol as published by the compiler, i.e., if your symbol is
MyFunction, the column should contain _MyFunction. In the case of a _stdcall
function it would be _MyFunction@12 .
The second one
should contain the name of the symbol as exported from the DLL. Normally
this should be the same as the column 1, but sometimes you want to export
the functions without the leading underscore, for instance for using them
with another language processor like Visual Basic.
The third column
is optional. If present it should contain the column DATA, meaning that this
is an exported DATA symbol.
The .exp file is
generated automatically by lcclnk.