Jump to content
Nytro

A Crash Course In Exporting From A DLL

Recommended Posts

Posted

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.

HowTo1.gif

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.

HowTo2.gif

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.

HowTo3.gif

Figure 3 ExportDemoDLL1 MyFunctions.h

Notice that the DLL exports two functions using the "__declspec(dllexport)" 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:

HowTo7.gif

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.

***Vezi sursa***

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.

HowTo4.gif

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:

HowTo20.gif

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.

HowTo5.gif

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.

HowTo6.gif

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.

HowTo8.gif

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.

HowTo9.gif

igure 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.

HowTo10.gif

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.

HowTo21.gif

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.

HowTo11.gif

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().

HowTo12.gif

Figure 14 New MyFunctions.h With Function Sub()

Then, I modified the DEF file as follows:

HowTo13.gif

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.

HowTo22.gif

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.

.................................................................................

Sursa (articolul complet): How To Define And Use DLL Export Ordinals

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...