I am not understanding the point of using .def files with DLLs.
It seems that it replaces the need to use explicit exports within your DLL code (ie. explicit __decls
.DEF files were more common in 16-bit windows where they were usually the only way to specify which symbols should be exported.
In addition, they provided a means to specify exports by ordinal value (@1, @2 etc.) rather than by name. This method of looking up symbols was used when performance was really important, such as in video drivers.
For those interested still... to be able to link to the dll and def file you also require a lib file. In windows this can be made from the def using the 'LIB' tool. See below for an example of the command line way of doing this.
lib /machine:i386 /def:sqlite3.def
Hope this helps others.
I find the use of both __declspec(dllexport) and the .def file together to be useful in creating portable DLLs, i.e. DLLs that can be called from code compiled with a different compiler or with different compiler settings.
Just putting __declspec(dllexport) on your function declarations will cause those functions to be "exported" by your DLL (at least on Windows) so that they can be called from outside the DLL.
However, adding to the build a .def file that lists all of your exported functions lets you stop Microsoft compilers (for example) from adding a leading underscore and trailing parameter-width information to the exported function name (at least when combined with the __stdcall directive, also useful for portability). E.g. the function declaration
void foo(int i);
could end up being exported as "_foo@4" if you aren't careful about calling convention and .def file usage.
Keeping the exported function names in the symbol table free of such name-decoration comes in really handy when making GetProcAddress() calls as part of loading and hooking into a DLL explicitly at runtime. i.e. to get a pointer to the above function foo() (assuming it was exported at all) at runtime, you ideally just want to call:
HANDLE dllHandle = LoadLibrary("mydll.dll");
void* fooFcnPtr = GetProcAddress(dllHandle, "foo");
With some appropriate error case checking of course!
Use of a .def file plus __stdcall, __declspec(dllexport) and extern "C" on your function declarations when building your DLL will ensure that the above client-side code will work for a wide range of compilers and compiler settings.
I haven't worked with DLLs much, but my understanding is that for exported C++ functions, you shold use the "__declspec(dllexport)", and for exported C functions you should write the .def file. That's probably because C++ functions support overloading, but C functions do not.
My understanding is that .def files provide an alternative to the __declspec(dllexport) syntax, with the additional benefit of being able to explicitly specify the ordinals of the exported functions. This can be useful if you export some functions only by ordinal, which doesn't reveal as much information about the function itself (eg: many of the OS internal DLL's export functions only by ordinal).
See the reference page.
Note that the names in the .def file must match the names in the binary. So if you using C or C++ with 'extern "C" { ... }', the names will not be mangled; otherwise you must use the correct mangled names for the specific version of the compiler used to generate the DLL. The __declspec() function does this all automatically.
My understanding is that .def files do not actually specifies which all apis need to be exported. It just contains the apis exported and their ordinal numbers. If you want to actually export a particular api, you need to specify __declspec(dllexport) in the definition of the api and __declspec(dllimport) at the declaration.
The advantage of def file is that, it helps you to maintain the backword compatibility with the already realsed dlls. i.e it maintains the ordinal numbers for apis. Suppose you add a new api in the dll, then the linker looks at your .def file genearate the ordinal number for the ne wapi such that the ordinal numbers for the old apis are intact.
So, if the client code uses the latest dll, it does not break the existing apis.