Nytro Posted December 9, 2010 Report Posted December 9, 2010 A Crash Course In Exporting From A DLLWith A Detailed Look At The DEF FileUsing Visual C++ Professional 6.0By: George ChastainDate: 7/21/2000When 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 LibraryThe 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. Thatinformation will allow the client to locate the things provided, or exported, by the DLL. See Figure 1.Figure 1 DLL And Import LibraryWhen 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 LibraryBut just how does a client find the stuff provided by the DLL?Names And NumbersWhen 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.hNotice 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:Figure 4 Export Function List View of Dependency WalkerThe "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.Figure 5 New Version of MyFunctions.hWe 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 WalkerLook 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 WayFor 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 FileWe 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 ExportDemoDLL2Now 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 FileI 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.igure 10 Unresolved External SymbolI 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 isprovided 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 1We 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 DefinitionWhen 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 ExportedOption 2Or, 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 MethodBut 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 VariablesExporting 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 Quote