INFO: Using _declspec(dllimport) & _declspec(dllexport) in Code

This article was previously published under Q132044
This article has been archived. It is offered "as is" and will no longer be updated.
This article supplements the information covered in the following articlein the Microsoft Knowledge Base:
107501 INFO: __export Replaced By __declspec in Visual C++ 32-bit
This article discusses the advantages and mechanics of using_declspec(dllimport) and _declspec(dllexport) in your application.
The 32-bit edition of Visual C++ uses _declspec(dllimport) and_declspec(dllexport) to replace the __export keyword previously used in16-bit versions of Visual C++.

You do not need to use _declspec(dllimport) for your code to compilecorrectly, but doing so allows the compiler to generate better code. Thecompiler is able to generate better code because it knows for sure whethera function exists in a DLL or not, so the compiler can produce codes thatskip a level of indirection that would normally be present in a functioncall that crossed a DLL boundary.

With the proper .DEF file EXPORTS section, _declspec(dllexport) is notrequired. _declspec(dllexport) was added to provide an easy way to exportfunctions from an .EXE or .DLL without using a .DEF file.

The remainder of this article provides a fairly low-level, thoroughdiscussion of these issues.

The Win32 Portable Executable format is designed to minimize the number ofpages that must be touched to fix imports. To do this, it places all theimport addresses for any program in one place called the Import AddressTable. This allows the loader to modify only one or two pages whenaccessing these imports.

Using _declspec(dllimport) for Function Calls

In the following code example, assume func1 is a function that resides in aDLL separate from the .EXE file that contains the main() function.

Without _declspec(dllimport), given this code:
void main(void) {    func1();}				
the compiler generates code that looks like this:
call func1				
and the linker translates the call into something like this:
call 0x4000000         ; The address of 'func1'.				
If 'func1' exists in another DLL, the linker can't resolve this directlybecause it has no way of knowing what the address of 'func1' is. In 16-bitenvironments, the linker adds this code address to a list in the .EXEthat the loader would patch at run-time with the correct address. In 32-bitenvironments, the linker generates a thunk for which it does know theaddress. The thunk looks like this:
   0x40000000:    jmp DWORD PTR __imp_func1				
Here __imp_func1 is the address for func1's slot in the import addresstable of the .EXE file. All the addresses are thus known to the linker. Theloader only has to update the .EXE file's import address table at load timefor everything to work correctly.

Therefore, using _declspec(dllimport) is better because it is better if thelinker does not generate a thunk if it does not have to. Thunks make thecode larger (on RISC systems, it can be several instructions) and candegrade your cache performance. If you tell the compiler the function is ina DLL, it can generate an indirect call for you.

So now this code:
__declspec(dllimport) void func1(void);void main(void) {    func1();}				
generates this instruction:
call DWORD PTR __imp_func1				
There is no thunk and no jmp instruction, so the code is smaller andfaster.

On the other hand, for function calls inside a DLL, you don't want to haveto use an indirect call. You already know a function's address. Time andspace are required to load and store the address of the function before anindirect call, so a direct call is always faster and smaller. You only wantto use __declspec(dllimport) when calling DLL functions from the outsidethe DLL itself. Don't use __declspec(dllimport) on functions inside a DLLwhen building that DLL.

Using _declspec(dllexport)

Microsoft introduced __export in the 16-bit compiler version to allow thecompiler to generate the export names automatically and place them in a.LIB file. This .LIB file could then be used just like a static .LIB tolink with a DLL.

Microsoft added __declspec(dllexport) to continue this convenience. Itspurpose is to add the export directive to the object file so you don't needa .DEF file.

This convenience is most apparent when trying to export decorated C++function names. There is no standard specification for name decoration, sothe name of an exported function may change between compiler versions. Ifyou use _declspec(dllexport), recompiling the DLL and dependent .EXE filesis necessary only to account for any naming convention changes.

Many export directives such as ordinals, NONAME, or PRIVATE, can be madeonly in a .DEF file, and there is no way to specify these attributeswithout a .DEF file. However, using _declspec(dllexport) in addition tousing a .DEF file does not cause build errors.

As a reference, search through the Win32 WINBASE.H header file. It containsexamples of preferred __declspec(dllexport) and __declspec(dllimport)usage.

Using _declspec(dllexport) and _declspec(dllimport) on Data

In the case of data, using _declspec(dllimport) is a convenience item thatremoves a layer of indirection. When you import data from a DLL, you stillhave to go through the import address table. In the Win32 days before_declspec(dllimport), this meant you had to remember to do an extra levelof indirection when accessing data exported from the DLL:
// project.h#ifdef _DLL     // If accessing the data from inside the DLL   ULONG ulDataInDll;else            // If accessing the data from outside the DLL   ULONG *ulDataInDll;#endif				
You would then export the data in your .DEF file:
// project.defLIBRARY projectEXPORTS    ulDataInDll   CONSTANT				
and access it outside the DLL:
if (*ulDataInDll == 0L) {   // Do stuff here}				
When you mark the data as __declspec(dllimport), the compiler automaticallygenerates the indirection code for you. You no longer have to worry aboutthe steps above. As stated previously, do not use _declspec(dllimport)declaration on the data when building the DLL. Functions within the DLLwill not use the Import Address Table to access the data object. Therefore,you will not have the extra level of indirection present.

To export the data automatically from the DLL, use this declaration:
__declspec(dllexport) ULONG ulDataInDLL;				

Using a .DEF File

If you choose to use __declspec(dllimport) along with a .DEF file, youshould change the .DEF file to use DATA in place of CONSTANT to reduce thelikelihood that incorrect coding will cause a problem:
// project.defLIBRARY projectEXPORTS    ulDataInDll   DATA				
The following chart shows why:
Keyword     Emits in the import lib     ExportsCONSTANT    __imp_ulDataInDll           ulDataInDll            __ulDataInDllDATA        __imp_ulDataInDll           ulDataInDll				
Using _declspec(dllimport)and CONSTANT lists both the __imp_ version andthe undecorated name in the .LIB DLL import library that is created toallow explicit linking. Using _declspec(dllimport) and DATA lists just the__imp_ version of the name.

If you use CONSTANT, either of the following code constructs could be usedto access the ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll; /*prototype*/    if (ulDataInDll == 0L)   /*sample code fragment*/ 				
ULONG *ulDataInDll;      /*prototype*/ if (*ulDataInDll == 0L)  /*sample code fragment*/ 				
However, if you use DATA in your .DEF file, only code compiled with thefollowing definition can access the variable ulDataInDll:
__declspec(dllimport) ULONG ulDataInDll;if (ulDataInDll == 0L)   /*sample code fragment*/ 				
Using CONSTANT is more risky because if you forget to use the extra levelof indirection, you could potentially access the Import Address Table'spointer to the variable -- not the variable itself. This type of problemcan often manifest as an Access Violation because the Import Address Tableis currently made read-only by the Microsoft compiler and linkers.

The Current Visual C++ linker issues a warning if it sees CONSTANT inthe .DEF file to account for this case. The only real reason to useCONSTANT is if you can't recompile some object file where the header filedidn't list dllimport on the prototype.
The Visual C++ Books Online provide a substantial amount of documentationon the dllexport and dllimport storage-class attributes. This includes"The dllexport and dllimport Attributes" and the "Using dllimport anddllexport in C++" topics in the "Microsoft-Specific Modifiers" chapter ofthe C++ Language Reference, and the "Exporting Symbols" topics in the"Creating DLLs for Win32" chapter of the Programming Techniques reference.For a thorough listing related topics, search the Books Online for"dllimport" or "dllexport".

For more information, please see the following articles in the MicrosoftKnowledge Base:
90530 How To Export Data from a DLL or an Application
107501 INFO: __export Replaced By __declspec in Visual C++ 32-bit

Article ID: 132044 - Last Review: 12/04/2015 11:35:01 - Revision: 2.0

Microsoft Visual C++ 1.0 Professional Edition, Microsoft Visual C++ 2.0 Professional Edition, Microsoft Visual C++ 2.1, Microsoft Visual C++ 4.0 Standard Edition, Microsoft Visual C++ 5.0 Enterprise Edition, Microsoft Visual C++ 5.0 Professional Edition

  • kbnosurvey kbarchive kbcode kbcompiler kbinfo KB132044