Compiling a DLL

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;

}

The Definitions File (.def)

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.