A Stronger Defense Through Better Software
A Service Provided By
George Chastain, Huntsville, Alabama

Résumé
Home What's New? About Security Contact Submit Content Disclaimers

Search This Site


powered by FreeFind

Targeted Web Search

Departments
Development

Test, QA & Safety
The Biz

Information Security

Joint Resources

General
CDR
Systems
Weapons 101
DoD Links

Media
Office Resources
Defense News
Defense Magazines

Tech & World News

Reader's Corner

Books

 ©2006 G. Chastain

A Crash Course In Exporting From A DLL

With A Detailed Look At The DEF File

Using Visual C++ Professional 6.0

By: George Chastain

Date: 7/21/2000

 

When creating a new DLL, an Import Library (with a ".LIB" extension) is created. 

This Import Library has to remain consistent with the DLL used to create the library -- at least for the 
code utilized by the client. Occasionally, you may find yourself delivering multiple products to multiple 
customers and some of these products may share a particular DLL. But what happens if a customer 
obtains a new version of one of those products but doesn't obtain new versions of the other products 
that use that particular DLL? It is possible that the new version of the DLL delivered with the new 
product could break the other, older product that the customer already has. This can happen if 
information contained within the new version of the DLL becomes inconsistent with the information 
recorded for the DLL by the Import Library used to link the other older products.

Before describing how to help alleviate some the possibility of this occurring, we will take a brief look 
at what an Import Library contains.

The Import Library

The Import Library does not contain any code. It may be thought of as a "road map" to the functions, 
classes and other declarations provided by the DLL. When linking a client of a DLL, the Linker needs 
to record the information contained in this "road map" in the client that will use the DLL. That
information will allow the client to locate the things provided, or exported, by the DLL. See Figure 1.

Figure 1 DLL And Import Library

When a client of a DLL references something exported by the DLL, the client utilizes the information 
obtained by linking with the Import Library to find the item being referenced in the DLL. There is no 
actual code or resources in the Import Library. All of that is contained in the DLL. The Import Library 
just tells the client of the DLL where to find the things that the client needs from the DLL.

Figure 2 Client Links To Import Library

But just how does a client find the stuff provided by the DLL?

Names And Numbers

When the Linker links a DLL, it assigns to the exported functions, classes or data, unique names and 
identifying numbers to all the exported items. For C++ functions and classes, the unique names are 
called Decorated Names. And the unique numbers assigned to the exported items are called Ordinals. 
Earlier, I mentioned a potential problem in which the Import Library used to link a client can become 
"inconsistent" with a newer version of the DLL associated with that Import Library. To explain what 
happens, we will take a look at two examples.

I will not go into detail on how to create a DLL using Visual C++. If you do not know how to do this 
you should review the subject in the MSDN library or any book on Visual C++ programming. The first 
example we will look at exports in the conventional manner that most developers are familiar with. I 
have created a DLL project called ExportDemoDLL1. In that project I created a header file called 
MyFunctions.h
. The contents are shown in Figure 3.

Figure 3 ExportDemoDLL1 MyFunctions.h

Notice that the DLL exports two functions using the "__declspec(dllex

A Crash Course In Exporting From A DLL

With A Detailed Look At The DEF File

Using Visual C++ Professional 6.0

By: George Chastain

Date: 7/21/2000

 

When creating a new DLL, an Import Library (with a ".LIB" extension) is created. 

This Import Library has to remain consistent with the DLL used to create the library -- at least for the 
code utilized by the client. Occasionally, you may find yourself delivering multiple products to multiple 
customers and some of these products may share a particular DLL. But what happens if a customer 
obtains a new version of one of those products but doesn't obtain new versions of the other products 
that use that particular DLL? It is possible that the new version of the DLL delivered with the new 
product could break the other, older product that the customer already has. This can happen if 
information contained within the new version of the DLL becomes inconsistent with the information 
recorded for the DLL by the Import Library used to link the other older products.

Before describing how to help alle

port)" directive and a class using 
the AFX_EXT_CLASS macro. Currently, the AFX_EXT_CLASS macro is simply defined to be 
AFX_CLASS_EXPORT by the Microsoft header file AFXV_DLL.h if a DLL is being built. The 
AFX_CLASS_EXPORT, currently, is itself defined to be __declspec(dllexport). If an executable is 
being built, Microsoft defines the AFX_EXT_CLASS macro as AFX_CLASS_IMPORT which, in 
turn is declared as "__declspec(dllimport)". You may occasionally see classes written by developers 
that make use of the AFX_CLASS_EXPORT or __declspec directly. However, you are encouraged 
to use the proper macro AFX_EXT_CLASS when creating a class to export in case Microsoft changes 
the way in which class exports must be made in a future version of Visual Studio/Visual C++.

You will also notice the use of a preprocessor directive "_EXPORTING". This, together with the use 
of AFX_EXT_CLASS, makes it easy for you to create a single header file for use by the DLL project 
to export functions and data and for use by the client project to import the functions and data. This helps 
eliminate the need to maintain two separate header files. When you build your DLL, specify the 
preprocessor directive /D "_EXPORTING" in the list of compiler options. Do not do this when 
building the client and you will be able to use the same header file.

When the ExportDemoDLL1.dll is built, the exports are translated into Decorated Names and Ordinal 
Numbers as shown highlighted in the "Export Function List View" windowpane of Dependency 
Walker
:

Figure 4 Export Function List View of Dependency Walker

The "Export Function List View" windowpane has two columns of interest to us. The first is labeled 
"Ordinal". This is the unique Ordinal Number assigned to the exported function. The other column of 
interest is labeled "Function" and it shows the unique Decorated Name given to the exported function 
within the DLL. The client makes use of this information when locating the functions. The functions 
are defined as shown in Table 1.

 

Table 1 ExportDemoDLL1 Exported Functions

EXPORTED FUNCTION

ORDINAL

DECORATED NAME

Prod

1

?Prod@@YAJJJ@Z

Sum

2

?Sum@@YAJJJ@Z

CMyClass::CMyClass()

3

??0CMyClass@@QAE@XZ

CMyClass::~CMyClass()

4

??1CMyClass@@QAE@XZ

CMyClass::operator=

5

??4CMyClass@@QAEAAV0@ABV0@@Z

CMyClass::SAbout()

6

?SAbout@CMyClass@@QAEXXZ

CMyClass::SHowdy()

7

?SHowdy@CMyClass@@QAEXXZ

Later, I will explain how to obtain the Decorated Names for items you wish to export and how to 
convert Decorated Names to Undecorated Names.

You will notice in Figure 3 above that the class method CMyCLass::SAbout() is implemented within 
the class declaration. That is, the body is defined in the class declaration instead of within the CPP file 
for the class. When you fully define a class method within the class declaration, it is normally treated as 
an inline function. However, when you export an inline function with __declspec(dllexport), the inline 
function is always instantiated and exported, whether or not any module in the client program references 
the function. The function is presumed to be imported by another program. When you export an entire 
class using the AFX_EXT_CLASS macro as illustrated above you are, in effect, exporting the inline 
function SAbout(). This is why you see the method listed as an exported function in the "Export 
Function List View" of Dependency Walker in Figure 4 above.

Now, assume that we have built a client application that makes use of this ExportDemoDLL1 DLL. 
Then we decide later to add a new function to the DLL and export it. We will try this and add a 
function called Sub() as shown in the new version of the header file in Figure 5.

Figure 5 New Version of MyFunctions.h

We now build the new version of the DLL and take a look at the exports in Dependency Walker:

Figure 6 New Version of ExportDemoDLL in Dependency Walker

Look at what happened to the exported function Sum(). Its Ordinal Number is now 8 where it was 7 
in the previous version of ExportDemo1.DLL. If we do not re-link the client application with the Import 
Library created when building the new version of the ExportDemoDLL1.DLL the application will be 
looking for the function Sum() in the wrong location in the DLL!! The results are unpredictable and 
typically catastrophic. Further, you usually will have no clue as to why the executable crashed! You 
could spend a lot of time trying to debug this one.

Now that we understand the problem, what can we do about it? The answer is to explicitly define 
Ordinals for the exported functions so that the exported functions will always receive the same ordinals 
on every release of the DLL.

Defining Ordinals -- The DEF Way

For the next part of the discussion, we will assume that a new DLL project has been created. I will refer 
to it as ExportDemoDLL2. When you use the Visual C++ AppWizard to create a new DLL project, the 
wizard creates a file called the Module Definition File. The file has a ".DEF" extension and contains 
information similar to that shown in the example in Figure 7.

Figure 7 Default DEF File

We will be making some additions to the contents of this file. But before we do we need to remove the 
export directives and macros from the header file MyFunctions.h that was created for our first example, 
ExportDemoDLL1. The header file for our new example DLL is shown in Figure 8. We haven’t added 
the function Sub() yet. We will do that shortly.

Figure 8 New MyFunctions.h For ExportDemoDLL2

Now that we no longer state that we want the entire class CMyClass exported in the header (because 
we removed the AFX_EXT_CLASS macro) you must add export directives to the DEF file. The 
required entries for the DEF file for ExportDemoDLL2 are shown in Figure 9. Again, we will add our 
function Sub() shortly.

Figure 9 New DEF File

I have explicitly assigned ordinals to the exported functions. They are shown after the decorated names 
following the "@" sign. Text appearing to the right of a semi-colon is treated as a comment. The line 
containing the keyword LIBRARY specifies the internal name of the DLL. The line containing the 
keyword DESCRIPTION defines a string to be written into an .rdata section of the DLL. This 
description is different from the text inserted in the library by the Linker’s /COMMENT option.

Now we are ready to build the new version of our DLL. But what happens now?! We get an unresolved 
external symbol error from the Linker! See Figure 10.

Figure 10 Unresolved External Symbol

I mentioned above that because we were exporting an entire class with AFX_EXT_CLASS, the inline 
methods are always expanded and exported just like any other class method whose implementation is
provided in a CPP file. But now we have removed the AFX_EXT_CLASS macro from the class 
declaration. The inline method CMyClass::SAbout() will now remain treated as an inline function. You 
cannot export an inline function because there is nothing to export.

 

There are two solutions to this situation.

Option 1

We may remove the definition for CMyClass::SAbout() from the header file and place it in the CPP file 
MyFunctions.cpp. The new header file will then appear as shown in Figure 11.

Figure 11 MyFunctions.h Without Method Definition

When we do this we can successfully build the DLL and examine it in Dependency Walker. You will 
notice that the method SAbout() is listed as an exported function in the "Export Function List View" 
windowpane just as it was before.


Figure 12 ExampleDemoDLL2 With Non-Inlined Method Exported

Option 2

Or, we may simply remove the export line for the member function SAbout from the DEF file. In this 
case MyFunctions.h will remain as shown in Figure 8. The DEF file would then appear as shown in 
Figure 13. And since the header file MyFunctions.h will be included in source code that references the 
DLL, the method CMyClass::SAbout() will continue to be treated as an inline function. But there is a 
"gotcha" with this implementation so I recommend that you use the first implementation. I will explain 
why later when I discuss the pitfalls of using DEF Files.

Figure 13 DEF File Without Inlined Class Method

But for now, notice that in the "Ordinal" column of the "Export Function List View" windowpane the 
exported functions are assigned the ordinals I defined in the DEF file. Ordinal numbers may be any 
number between 1 and 65,535 inclusive. Ordinal numbers 4 and 5 are skipped in the DEF file for the 
example ExportDemoDLL2 so Dependency Walker displays them with no export entry. As a matter 
of good practice, you should number your exports sequentially.

Now, let us see what happens when we add the function Sub() to this new DLL project like we did in 
the example ExportDemoDLL1. I modified the header file to appear as shown in Figure 14. Note that I
 am going with the first option in the implementation of the method CMyClass::SAbout().

Figure 14 New MyFunctions.h With Function Sub()

Then, I modified the DEF file as follows:

Figure 15 New DEF File With Export Entry For Function Sub()

After building the ExportDemoDLL2 again, we can re-examine the DLL using Dependency Walker.

Figure 16 ExportDemoDLL2 In Dependency Walker With Function Sub()

We now see the export for the function Sub() at the top of the "Export Function List View" windowpane 
of Dependency Walker with the assigned ordinal of 1. Notice that the ordinals for the other exported 
functions are unchanged from those shown in Figure 12 above.

If I were to now place this new DLL (with the added function Sub()) from the example project 
ExportDemoDLL2 with an application linked with the Import Library produced by the version of 
ExportDemoDLL2 created before function Sub() was added, the executable would still run successfully. 
The newly added function Sub() would simply be ignored by the application.

Exporting Global Variables

Exporting global variables is just as easy as exporting functions. They will also be listed in the "Export 
Function List View" windowpane of Dependency Walker along with the functions and class methods 
that are exported. The only thing to remember is not to define the global in a header file that is included 
in both the DLL and your client or you will get an error from the Linker that the symbol is multiply |
defined.

As with the functions, you can export a global variable in two ways. The first method of exporting a 
global is to add a line to the header file as shown in MyFunctions.h in Figure 17. Then, in a CPP file of 
your DLL project, define the global variable as you would any global variable.

Figure 17 DLL Global Variable Export/Import

When you use this header file to build the DLL and the client, it will make the variable 
"DLLGlobalVariable" global to both the DLL and the client. Or if you wish to use a DEF file, you can
 add an entry similar to the one shown in bold in Figure 18.

Figure 18 DEF File With Exported Global

You will still need to add an import statement in your client as shown in Figure 19.

Figure 19 Import Statement When Accessing DLL Globals via DEF File

You would not add anything to MyFunctions.h and you would define the global in a CPP file of your 
DLL as described above.

Mixing "dllexport" with DEF Files

It is perfectly acceptable to use both DEF files and __declspec(dllexport) to define exports in a DLL 
project. Just make sure that you do not attempt to export the same item both ways or you will see a 
warning from the Linker informing you that the export was specified multiple times and that it is using 
the first specification.

 

Nonames And Numbers

If you use a DEF file, you have the ability to perform some space optimizations on your DLLs. The DEF 
File allows you to specify the option "NONAME" as shown in Figure 20.

Figure 20 DEF File WIth NONAME Option

After building the DLL with this modification to the DEF file, we take another look at the DLL in 
Dependency Walker:

Figure 21 Exports With NONAME In Dependency Walker

Notice that the Decorated Names are no longer listed in the "Export Function List View" windowpane 
of Dependency Walker. This can be useful if you have a large DLL that exports many items. By not 
recording the long Decorated Names, you can save a lot of space. However, if you have any code that 
utilizes explicit loading of the DLL, and thus calls GetProcAddress() with the export function name, that 
will no longer work. As a rule, you should not have to take advantage of this feature unless you 
absolutely must. Please refer to the MSDN documentation for details on the use of GetProcAddress().

 

Export C++ Functions For Use By C Executables

If you are writing C++ functions to be exported and used by C executables, you will need to export the 
functions in the DLL with the extern "C" modifier. I have added a function Div() to the header file 
MyFunctions.h that I am exporting with the extern "C" modifier as shown in Figure 22.

Figure 22 Export C++ Function For Use By C Executable

Again, since I defined the DECLSPEC preprocessor directive, I can use the same header file to export 
the function from the DLL and to import it into the client.

To do the same thing using a DEF file, you would add an export as shown in Figure 23.

Figure 23 C Export in DEF File

Notice in Figure 23 that we did not specify a Decorated Name for the function Div(). Decorated Names 
are only used for C++ linkage specification. Since we are exporting with the extern "C" linkage 
specification, we do not enter a Decorated Name in the DEF file export entry. You would then add an 
import directive to your client as shown in Figure 24.

Figure 24 Client Import For Exported C Function

 

Exporting C Function For Use By C Or C++ Executables

This is a simple matter. All you have to do is define your imports in a header file as shown in the following example 
taken directly from the MSDN library:

Figure 25 Importing C Functions In C Or C++ Executables

 

Pitfalls of Using DEF Files

I stated earlier that there are some issues that you must consider before implementing your DLLs using a DEF 
file. I will outline these issues.

  • The Decorated Names are version specific for the Visual C++ compiler. If you install a new version 
    of Visual C++ or a Service Pack that delivers a new version of the Linker, the Decorated Names may 
    change. Microsoft is free to change the decoration anytime they desire.
  • Development must never change the ordinals once they are assigned to a particular export. If you do, 
    clients built with previous versions of the Import Library will likely crash. The results will be unpredictable.
  • The Decorated Names are constructed using the exported symbol name and, in the case of functions, 
    the parameter list and return type. Development must not change any of these for exported items previously 
    delivered to a customer. If you do, the Decorated Names will change and create an inconsistency with previous 
    versions of the Import Library.
  • Avoid using inline functions including class methods that are fully defined in the class declaration. An inline 
    function gets expanded into the application code when the application is built; therefore, if you later rewrite 
    the function, it does not get updated unless the application itself is recompiled. So if you change the 
    implementation of that function in a later version of the DLL, the application that was linked with the original 
    version will still use the original implementation.

Obtaining And Converting Decorated Names

There are several ways in which you can obtain the decorated names for the items you wish to export. One is to use 
the dumpbin.exe utility.

  1. Open an MS-DOS window
  2. cd to the folder containing the object code for the DLL that you will export.
  3. Enter the following on the DOS command line:

    dumpbin –symbols *.obj > bindump.txt

    This will dump tons of information about the object files in the current folder into an ASCII text file.
  4. Search through the text file to locate the functions that you wish to export. You will find the Decorated 
    Names for these along with the Undecorated Names scattered throughout the ASCII dump text.

Another method for obtaining the Decorated Names is if you currently have a DLL that exports items without the 
use of the DEF file and you want to convert the DLL to use a DEF file. This makes it easier. Build the DLL as it 
currently is. Then follow the sameprocedure as outlined above but enter the following at the DOS prompt instead 
of the command shown for Step 3 above:

dumpbin –exports *.DLL > exports.txt

You can then easily cut-and-paste the exports into the DEF file for your DLL project. Then remove all the existing 
export directives and macros from the code as describe earlier in this discussion.

If you ever need to convert a Decorated Name to its Undecorated equivalent, Microsoft provides a utility to help 
here as well. Visual Studio delivers a command-line application named undname.exe. This application is provided 
in the Common\Tools folder of your "Microsoft Visual Studio" installation folder. To convert a Decorated Name to 
an undecorated name follow these steps:

  1. Open an MS-DOS window.
  2. If you do not have the Common\Tools folder under "Microsoft Visual Studio" in your system path, cd to 
    that folder.
  3. Enter the Decorated Name you wish to convert at the DOS prompt as shown in the following example:

    undname –f ?SHowdy@CMyClass@@QAEXXZ

The output of this example is shown in Figure 26. You can easily see that the Decorated Name "?SHowdy@CMyClass@@QAEXXZ " is for the class method "void CMyClass::SHowdy(void)."

Figure 26 Output of undname.exe

I hope that this discussion of exporting from DLLs has been informative. This should get you started in creating 
your own DLLs.

Constructive comments?  I would like to hear them.