Technical Information Database TI2407C.txt Linking to Functions in Third-Party DLLs Category :General Platform :All Product :Borland C++ All Description: Linking to Functions in Third-Party Dynamic Link Libraries ======= == ========= == =========== ======= ==== ========= The use of dynamic link libraries, or DLLs, is fundamental to Windows programming. Windows itself is a set of DLLs; when you call a Windows API function in your program, Windows finds the address of the function at run-time. Dynamic linking is not limited to Windows API calls;any library used for static linking can be rebuilt as a DLL. One advantage of DLLs is that they share a common file format, which is not the case for static libraries. Frequently, third-party static libraries such as Microsoft's use file formats that cannot be read by TLINK. If the same library is available in DLL form, linking will usually be possible. This document discusses two methods for linking to functions in a DLL: 1) import libraries, and 2) module definition files. It also addresses "undefined symbol" messages when attempting to link DLLs and gives methods for resolving these errors. Import Libraries ------ --------- An import library contains linker symbols for the function names in a DLL and reference information which the linker uses to construct a table in your .EXE file. At run-time, Windows uses the table to resolve function calls. You already use an import library whenever you write a Windows program: the library IMPORT.LIB is linked into your application to resolve calls to the Windows API functions. (To see this in your project, set the "Show run-time nodes" option under Options | Environment | Project View). To build an import library for a DLL, use the IMPLIB utility: IMPLIB LibName DLLName where "LibName" is the name of the import library and "DLLName" is the name of the DLL you wish to link to. Then add the import library to your project to link to the DLL. Undefined Symbols --------- ------- Of course, just as with static linking, there is no guarantee that the linker symbols generated by your compiler in your .OBJ file will match the linker symbols in the import library. The most common cause of "undefined symbol" error messages during linking is a language mismatch -- your program is written in C++, and the DLL is written in C. Extern C ------ - For most compilers, linker symbols in C are generated by simply prepending an underbar to the function name. In C++, a more sophisticated scheme must be used to generate a unique symbol for each declaration of an overloaded function; this is called "name mangling." Unfortunately, name mangling is not standardized, but varies from one compiler vendor to another. DLL vendors, aware of this problem, generally try to write their products in C to facilitate linking. However, if you are writing a C++ program, the compiler will automatically generate name-mangled symbols for all function names unless you explicitly tell it otherwise. To turn off name mangling for a function name, declare the function as extern "C": extern "C" void YourFunctionName(int); If you are linking with multiple functions in a header file, you can wrap the include directive within an extern "C" declaration: extern "C" { #include "somedll.h" }; Calling Conventions ------- ----------- What if the extern "C" declaration still leaves you with an undefined symbol? The first thing to check is whether you are using the correct calling convention in your function declaration. If you think you are not using a calling convention, you're wrong -- the compiler is assuming a default calling convention. The calling convention tells the compiler how parameters are pushed on the stack, and how the stack is cleaned up upon returning from a function call. It also tells the compiler how to generate linker symbols. In Borland C++, the default calling convention is cdecl, where parameters are pushed on the stack from right to left and the caller cleans up the stack. If you're programming in 16 bits, however, you will often attempt to link with functions that use the same calling convention as the 16-bit Windows API -- pascal, in which parameters are pushed on the stack from left to right and the called function cleans up the stack. The cdecl convention produces symbols that have a leading underscore and are case sensitive. The pascal convention produces symbols that have no leading underscore and are not case sensitive -- i.e., all letters are uppercase. In the 32-bit world, a different kind of mismatch is common. The stdcall calling convention, used by the 32-bit Windows API, produces symbols that are case sensitive, but with no leading underscore. It is sometimes possible to force a symbol match by changing compiler and linker options, but you have to be careful: you need to match calling conventions as well as symbols. Suppose that while you're compiling in 16 bits, you turn off the compiler option to generate underbars (Options | Project | Compiler | Compiler Output) and you find that the undefined symbol messages for the functions in your DLL have disappeared. You're probably safe, because most likely the DLL functions use the cdecl calling convention, and the DLL compiler did not generate leading underbars. But if you're compiling in 32 bits, you've probably got a mismatch in calling conventions -- most likely, the DLL functions use the stdcall convention. Borland C++ provides keywords that enable you to inform the compiler of the appropriate calling convention for a function. For example, a declaration for an externally defined pascal function would look like this: void _far _pascal YourFunctionName(int); (The _far modifier tells the compiler that the function definition is in a far data segment, always the case with 16-bit DLLs.) For a stdcall function, the declaration would be void __stdcall YourFunctionName(int); For portability, you may use the WINAPI macro for both declarations: void WINAPI YourFunctionName(int); WINAPI is defined as _far _pascal in 16 bits, and __stdcall in 32 bits. Seeing What the Linker Sees ------ ---- --- ------ ---- If you have been unable to resolve an undefined symbol with the methods discussed so far, it's time to look and see what linker symbols are being generated by the compiler and compare them with those in the DLL. Borland C++ has two tools for this purpose: TDUMP and IMPDEF. Run TDUMP on your .OBJ file with the -m option to show C++ name mangling and the -oiextdef option to show externally defined symbols. You can also use -oipubdef to see what symbols are being made public by an .obj file. tdump -m -oiextdef yourobj.obj > yourobj.lst It should be an easy matter to identify the "undefined symbols" the linker is complaining about. With name mangling (-m option) turned on, TDUMP shows a symbol as the linker sees it, with additional characters appearing before and after the function name. For example, the function declaration extern void foo() becomes @foo$qv IMPDEF takes two parameters, the name of a module definition file for the DLL, and the name of the DLL. impdef somedll.def somedll.dll The output of IMPDEF is the module definition file somedll.def, which contains an "EXPORTS" section listing the linker symbol for each exported function, followed by an ordinal value of the function. You'll probably recognize the function you're trying to link to, and see why the linker is having trouble; the function name within the symbol is the same, but the characters in which it is embedded, if any, are different. Resolving Undefined Symbols with Module Definition Files --------- --------- ------- ---- ------ ---------- ----- Let's say the DLL's compiler uses a different name-mangling convention, and has written the symbol for foo() as &foo%xy. How do you tell the linker that @foo$qv is the same as &foo%xy ? It can easily be done by aliasing the symbol in your program's module definition file. Edit the file to create an "IMPORTS" section, and list each undefined symbol, followed by an equal sign, the name of the DLL's module definition file without the extension, and the symbol as it appears in that file. For example, if &foo%xy appears in the EXPORTS section of SOMEDLL.DEF, you would alias @foo$qv as follows: @foo$qv=SOMEDLL.&foo%xy References ---------- For further information, see TI 1029, Resolving Undefined Linker Symbol Messages. Reference: 7/2/98 10:40:50 AM
Article originally contributed by Borland Staff