1

I built a DLL project in Visual Studio 2010 (by following this post). It contains only one function:

extern "C" __declspec(dllexport) void APIENTRY hello()
{
    std::cout << "Hello DLL.\n" << std::endl;
}

Then I created a Qt console application to use that DLL. Its main.cpp contains this:

typedef bool (*f_void)(void);

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QLibrary lib("TestDll");
    f_void hello = (f_void) lib.resolve(QString("hello").toLatin1());
    hello();

    return a.exec();
}

When I use APIENTRY in the DLL, the program crashes when calling hello(). It works fine if I remove APIENTRY from hello() declaration though. Why does this happen?

Community
  • 1
  • 1
liewl
  • 4,021
  • 13
  • 46
  • 65
  • BTW, don't do character-set conversion on names of exported functions. The export table uses 8-bit characters. There are no Unicode versions of `GetProcAddress`. – Ben Voigt Apr 10 '14 at 18:22

1 Answers1

4

In addition to the mechanics of making a function call requiring matching calling conventions, which is fixed by giving the function pointer the correct type as described below, the calling convention affects name mangling.

extern "C" prevents the C++ modus operandi of including the type into the name, so that overloads of a function get unique names and can be differentiated during symbolic lookup. But it doesn't prevent mangling entirely. For example, the function in the question, void __stdcall hello(void) will be exported by __declspec(dllexport) as _hello@0, where the trailing number is the number of bytes in the argument list. This helps avoid the situation that the caller and callee disagree on the size of the arguments, which is particularly problematic in __stdcall where the callee cleans up the stack.

Nevertheless, one can disable name mangling (and the Win32 DLLs such as gdi32.dll and shell32.dll have done so). For that you need a linker definitions file:

EXPORTS
  hello
  ; or hello = _hello@0

The linker knows about the mangling rules and will find the mangled name in the object file even if you don't provide it explicitly.

Additionally, when the exports are listed in the definition file, you no longer need __declspec(dllexport) in the code.

More information is available on MSDN.


If you call a function through a function pointer of the wrong type, you get undefined behavior. The calling convention is part of the type. Try:

typedef bool (APIENTRY *f_void)(void);  // or __stdcall instead of APIENTRY

I would guess that one of your header files contains #define APIENTRY __stdcall

It's always a good idea to explicitly set the calling convention of exported functions and function pointers. If you don't, you get the currently effective default calling convention, which is a project-specific compiler option.


Pedantically, whether a function and function pointer is marked extern "C" or not is also part of the type. But on Windows, where DLLs are found, extern "C" and extern "C++" have identical effect on calling convention.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • If I declare the type with APIENTRY, does the way I use it change? Just adding APIENTRY to typedef didn't work. – liewl Apr 10 '14 at 18:13
  • @DavidMcDavidson: Define "didn't work". Still crashes, or compile error? I might have put it in the wrong place... – Ben Voigt Apr 10 '14 at 18:21
  • Sorry, it crashed when calling `hello()`. No problems during compilation. Seems like QLibrary can't resolve `hello`, it returns a null pointer. – liewl Apr 10 '14 at 18:44
  • @DavidMcDavidson: Ahh! A null pointer from `GetProcAddress` will certainly cause a crash. You may know that `extern "C"` changes C++ name mangling, but doesn't eliminate it entirely. You can use `dumpbin` to view the export table and the actual name, I expect it to be `hello@0` (the trailing number is the size, in bytes, of the arguments). And you can use a linker definition file to prevent mangling entirely. – Ben Voigt Apr 10 '14 at 18:48
  • I've inspected the DLL, and indeed, the exported name is `_hello@0`. If I ask QLibrary to resolve `_hello@0` instead of `hello`, it works just fine. The linker definition file you mentioned, could you elaborate on how to do that? – liewl Apr 10 '14 at 18:54
  • Also, `DEF_FILE` can be used to point qmake to a `.def` file. – user362515 Jan 07 '16 at 21:22