113

I'd like a simple example of exporting a function from a C++ Windows DLL.

I'd like to see the header, the .cpp file, and the .def file (if absolutely required).

I'd like the exported name to be undecorated. I'd like to use the most standard calling convention (__stdcall?). I'd like the use __declspec(dllexport) and not have to use a .def file.

For example:

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

I'm trying to avoid the linker added underscores and/or numbers (byte counts?) to the name.

I'm OK with not supporting dllimport and dllexport using the same header. I don't want any information about exporting C++ class methods, just c-style global functions.

UPDATE

Not including the calling convention (and using extern "C") gives me the export names as I like, but what does that mean? Is whatever default calling convention I'm getting what pinvoke (.NET), declare (vb6), and GetProcAddress would expect? (I guess for GetProcAddress it would depend on the function pointer the caller created).

I want this DLL to be used without a header file, so I don't really need the a lot of the fancy #defines to make the header usable by a caller.

I'm OK with an answer being that I have to use a *.def file.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Aardvark
  • 8,474
  • 7
  • 46
  • 64
  • I may be mis-remembering but I think that: a) `extern C` will remove the decoration which describes the function's parameter types, but not the decoration which describes the function's calling convention; b) to remove **all** decoration you need to specify the (undecorated) name in a DEF file. – ChrisW Feb 11 '09 at 18:34
  • This is what I was seeing as well. Maybe you should add this as a full fledged answer? – Aardvark Feb 11 '09 at 19:15

4 Answers4

155

If you want plain C exports, use a C project not C++. C++ DLLs rely on name-mangling for all the C++isms (namespaces etc...). You can compile your code as C by going into your project settings under C/C++->Advanced, there is an option "Compile As" which corresponds to the compiler switches /TP and /TC.

If you still want to use C++ to write the internals of your lib but export some functions unmangled for use outside C++, see the second section below.

Exporting/Importing DLL Libs in VC++

What you really want to do is define a conditional macro in a header that will be included in all of the source files in your DLL project:

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

Then on a function that you want to be exported you use LIBRARY_API:

LIBRARY_API int GetCoolInteger();

In your library build project create a define LIBRARY_EXPORTS this will cause your functions to be exported for your DLL build.

Since LIBRARY_EXPORTS will not be defined in a project consuming the DLL, when that project includes the header file of your library all of the functions will be imported instead.

If your library is to be cross-platform you can define LIBRARY_API as nothing when not on Windows:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

When using dllexport/dllimport you do not need to use DEF files, if you use DEF files you do not need to use dllexport/dllimport. The two methods accomplish the same task different ways, I believe that dllexport/dllimport is the recommended method out of the two.

Exporting unmangled functions from a C++ DLL for LoadLibrary/PInvoke

If you need this to use LoadLibrary and GetProcAddress, or maybe importing from another language (i.e PInvoke from .NET, or FFI in Python/R etc) you can use extern "C" inline with your dllexport to tell the C++ compiler not to mangle the names. And since we are using GetProcAddress instead of dllimport we don't need to do the ifdef dance from above, just a simple dllexport:

The Code:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

And here's what the exports look like with Dumpbin /exports:

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

So this code works fine:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);
joshperry
  • 41,167
  • 16
  • 88
  • 103
  • 1
    extern "C" seemed to remove the c++ style name mangling. The whole import vs. export thing (which i tried to suggest not including in the question) isn't really what I'm asking about (but its good info). I figured it would cloud the problem. – Aardvark Feb 11 '09 at 19:29
  • The only reason I can think that you'd need that is for LoadLibrary and GetProcAddress... This is already taken care of, I'll expound in my answer body... – joshperry Feb 11 '09 at 19:39
  • Is EXTERN_DLL_EXPORT == extern "C" __declspec(dllexport) ? Is that in the SDK? – Aardvark Feb 11 '09 at 20:04
  • lol, umm I guess I don't even know my own code... #define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport) – joshperry Feb 11 '09 at 20:06
  • I'll give this a try... thanks for all the effort you've put into this answer. – Aardvark Feb 11 '09 at 21:18
  • 3
    Don't forget to add the module definition file into the project's linker settings - just "adding an existing item to the project" is not enough! – Jimmy Feb 10 '11 at 15:43
  • 1
    I used this to compile a DLL with VS and then call it from R using .C. Great! – Juancentro Feb 22 '15 at 19:17
  • I wonder why the `__declspec` idiom is preferred to the DEF files, especially if they can be generated. For a large project including many DLLs, you either have to copy that macro prologue into every header, or use the build system to manage a macro for each library (each macro set to import except the one currently being built). The DEF file seems a bit simpler, write during compilation, consume during compilation of a dependent. Maybe it's annoying to have to ship the DEF file? – sg_man Sep 06 '19 at 15:22
  • @sg_man This is a good question, I found a couple decent ones on SO on the topic; but with a small spattering of answers. I think the best argument for the macros is cross platform dev. A common idiom I've seen in many C/C++ projects is to have a public and private set of includes. The public set is used by internal and external consuming units, and are part of the public interface exported by libraries (e.g. .dll, .a, .lib). The build automation for the library project is then specially configured to set the macro flags. While consumers need not do anything but `#include` and link properly. – joshperry Sep 07 '19 at 19:16
  • If I build DLL from `C` code, do I still need to use `dllexport`? – Royi May 07 '20 at 15:11
  • I have a question. If we can have forward declarations in .hpp file and definitions in .cpp file & we can then compile cpp file to a dll and use the library by including hpp file and linking against dll, THEN WHY DO WE NEED DLLEXPORT (__declspec things) ? – 0xB00B Jul 07 '21 at 16:07
54

For C++ :

I just faced the same issue and I think it is worth mentioning a problem comes up when one use both __stdcall (or WINAPI) and extern "C":

As you know extern "C" removes the decoration so that instead of :

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

you obtain a symbol name undecorated:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

However the _stdcall ( = macro WINAPI, that changes the calling convention) also decorates names so that if we use both we obtain :

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

and the benefit of extern "C" is lost because the symbol is decorated (with _ @bytes)

Note that this only occurs for x86 architecture because the __stdcall convention is ignored on x64 (msdn : on x64 architectures, by convention, arguments are passed in registers when possible, and subsequent arguments are passed on the stack.).

This is particularly tricky if you are targeting both x86 and x64 platforms.


Two solutions

  1. Use a definition file. But this forces you to maintain the state of the def file.

  2. the simplest way : define the macro (see msdn) :

#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)

and then include the following pragma in the function body:

#pragma EXPORT

Full Example :

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

This will export the function undecorated for both x86 and x64 targets while preserving the __stdcall convention for x86. The __declspec(dllexport) is not required in this case.

Malick
  • 6,252
  • 2
  • 46
  • 59
  • 5
    Thank you for this important hint. I wondered already why my 64Bit DLL is different ffrom the 32 bit one. I find your answer much more usefull than the one accepted as answer. – Elmue Apr 04 '17 at 19:25
  • 1
    I really like this approach. My only recommendation would be to rename the macro to EXPORT_FUNCTION because the `__FUNCTION__` macro only works in functions. – Luis Jul 13 '17 at 16:49
  • Buy I am ok to use extern "C"{ __declspec(dllexport) void test(); } at VS2019 – vrqq Aug 23 '21 at 17:51
3

I had exactly the same problem, my solution was to use module definition file (.def) instead of __declspec(dllexport) to define exports(http://msdn.microsoft.com/en-us/library/d91k01sh.aspx). I have no idea why this works, but it does

Rytis I
  • 1,105
  • 8
  • 19
  • Note to anyone else running into this: using a `.def` module exports file *does* work, but at the expense of being able to supply `extern` definitions in the header file for e.g. global data—in which case, you have to supply the `extern` definition manually in *internal* uses of that data. (Yes, there are times when you need that.) It's better both generally and especially for cross-platform code simply to use `__declspec()` with a macro so that you can hand the data around normally. – Chris Krycho Jan 28 '15 at 13:06
  • 2
    The reason is probably because if you are using `__stdcall`, then `__declspec(dllexport)` will **not** remove the decorations. Adding the function to a `.def` will however. – Björn Lindqvist Sep 02 '15 at 11:49
  • 1
    @BjörnLindqvist +1, note that it is only the case for x86. See my answer. – Malick Jan 28 '17 at 13:41
-1

I think _naked might get what you want, but it also prevents the compiler from generating the stack management code for the function. extern "C" causes C style name decoration. Remove that and that should get rid of your _'s. The linker doesn't add the underscores, the compiler does. stdcall causes the argument stack size to be appended.

For more, see: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

The bigger question is why do you want to do that? What's wrong with the mangled names?

Rob K
  • 8,757
  • 2
  • 32
  • 36
  • The mangled names are ugly when called use LoadLibrary/GetProcAddress or other methods that do not rely on having a c/c++ header. – Aardvark Feb 11 '09 at 19:17
  • 4
    This would be unhelpful - you only want to remove the compiler generated stack management code in very specialized circumstances. (Just using __cdecl would be a less harmful way to lose the decorations - by default __declspec(dllexport) does not seem to include the usual _ prefix with __cdecl methods.) – Ian Griffiths Jul 04 '12 at 22:39
  • I wasn't really saying that it would be helpful, hence my caveats about the other effects and questioning why he even wanted to do it. – Rob K Dec 27 '13 at 05:07