VC++

From HerzbubeWiki
Jump to navigation Jump to search

This page is about Microsoft Visual C++, or VC++ (sometimes also abbreviated as MSVC).


Exporting/Importing symbols

The .lib file

When a library project is compiled, the symbols that are "exported" (i.e. visible to clients) are written to the so-called "import library file". This is a file that uses the extension .lib.

If the library project is compiled as a static library, the .lib file contains the actual code of the library in its compiled form. If the library project is compiled as a dynamic link library (DLL), the .lib file contains only symbol references - the code in its compiled form is placed in a separate .dll file.


Using the .lib file

The .lib file can be used by a client project to "import" the symbols that it wants to use. It is said to "link against" the library.

If the library project was compiled as a static library, the client project creates a duplicate of the compiled code in the .lib file and places that copy in its own binary.

If the library project was compiled as a DLL, the client project only places references to the symbols it uses in its own binary, including the name (not the path) of the DLL file that must provide the symbols at runtime.


Exporting/importing via __declspec

A function or class can be declared using the __declspec(dllexport) keyword to make its symbol visible to clients:

__declspec(dllexport) void foo();
class __declspec(dllexport) Bar
{
  [...]
};

A client uses the symbol by declaring the function prototype with __declspec(dllimport):

__declspec(dllimport) void foo();


Using a preprocessor macro for __declspec

Repeating function prototypes in a client is prone to error and inflexible in case the function prototype is changed in the library project. Repeating an entire class declaration is usually out of the question. A client will therefore often #include a header file provided by the library project which contains public function prototypes and/or class declarations.

There is an established idiom that uses a bit of preprocessor magic to allow the same header file to be used both when compiling the library project and the client project. First let's look at how the header file for a class looks like:

// The usual include guard
#ifndef MYCLASS_H
#define MYCLASS_H

#include "FooAPI.h"

class FOO_API MyClass
{
  [...]
};
#endif  // MYCLASS_H

Obviously the preprocessor macro FOO_API contains the magic, so let's look at how FooAPI.h defines the macro:

#ifndef FOOAPI_H
#define FOOAPI_H

#ifdef _WIN32
  #ifdef FOO_EXPORT
    #define FOO_API __declspec(dllexport)
  #else
    #define FOO_API __declspec(dllimport)
  #endif
#else
  #define FOO_API
#endif

#endif  // FOOAPI_H

Notes:

  • The #ifdef _WIN32 part is only necessary if the library is intended for cross-platform usage. It makes sure that if the library is not compiled on Windows the FOO_API macro will resolve to nothing, since other platforms do not need to explicitly specify the symbols that are visible to clients.
  • When the library project is compiled, it must define the FOO_EXPORT macro. This makes sure that functions and classes that use FOO_API will be exported
  • When the client project is compiled, it needs to do nothing. FOO_EXPORT is not defined and thus functions and classes that use FOO_API will be imported


Exporting/importing via .def file

As an alternative to using __declspec, a library project might also export functions using a so-called "module definition file". This is a file that uses the extension .def.

The .def file contains a list of all functions that the library project wants to export. As far as I know it is not possible to export classes in this way. The .def file then needs to be made known to the linker by adding to the project properties. In Visual Studio 2010 this is done under "Configuration Properties > Linker > Input > Module Definition File".

Refer to MSDN for details about using .def files. I do not recommend using them because they require to duplicate function names.


Note: If an undecorated function name is added to a .def file, the function declaration must use extern "C" to keep the compiler from decorating the function name. If extern "C" is omitted, then the .def file must contain the decorated name.


LoadLibrary() / GetProcAddress() vs. decoration

If a client binds to and executes an exported function at runtime using LoadLibrary() and GetProcAddress(), it is important that the function was exported undecorated. The ugly alternative, of course, is that the client uses the decorated function name for GetProcAddress().

Use extern "C" in a function declaration to keep the compiler from decorating the function names.