2

I'm trying to create a library Lib.dll to get dynamically called from a console application but cannot find the function funci() I want to call.

The Lib.dll is the outcome of a project (Console Application, but set to Configuration Type: .dll) created in Visual Studio 2019.

Lib.cpp is the only file in that project, and only contains the code:

__declspec(dllexport) int funci() 
{
    return 50;
}

I think I'm exporting the function correctly as I found the function by using DLL Export Viewer v1.66.

enter image description here

However, I struggle to locate the function through my Console Application (.exe):

#include <windows.h>
#include <iostream>

typedef int(__cdecl* o_funci)(void);

o_funci funci;

int main()
{
    HINSTANCE hGetProcIDDLL = LoadLibraryA("C:\\Lib.dll");

    if (!hGetProcIDDLL) {
        std::cout << "could not load the dynamic library" << std::endl;
        return EXIT_FAILURE;
    }

    // resolve function address here
    funci = (o_funci) GetProcAddress(hGetProcIDDLL, "funci");
    if (!funci) {
        std::cout << "could not locate the function" << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << "funci() returned " << funci() << std::endl;

    FreeLibrary(hGetProcIDDLL);
}

Something is going wrong at GetProcAddress but don't know why. Where did I go wrong?

Output:

enter image description here

I've been looking at this old post: Dynamically load a function from a DLL


EDIT: SOLVED THANKS TO tenfour

I used DependencyWalker.

Without extern "C" I could see the undecorated funci had the name ?funci@@YGHXZ,

enter image description here

So funci = (o_funci)GetProcAddress(hGetProcIDDLL, "?funci@@YGHXZ"); worked.

With extern "C" the undecorated funci had the name _funci@0 - a little cleaner.

enter image description here

Another note; using ordinal 0x0001 worked in both cases. Like this: funci = (o_funci)GetProcAddress(hGetProcIDDLL, (PCSTR)0x0001);

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
jubibanna
  • 1,095
  • 9
  • 21

1 Answers1

5

The tool you're using is showing you a pretty-fied version of the export name. Its real name will include name mangling, which is a convoluted attempt to embed call info into the export name.

You have a number of options to make this work with GetProcAddress:

  1. Use the REAL export name. Your tool probably has the option to see the un-prettyfied name (the mangled export name)
  2. Export the function using a module definition file (*.def), where you can even specify the name it will be exported as
  3. Import by ordinal instead of name
  4. Wrap the function in extern "C" { ... } which will use C-style naming, which avoids name mangling.

The most common solution is probably #4, with #2 as a close second.

tenfour
  • 36,141
  • 15
  • 83
  • 142
  • WTF? I missed the `extern "C"` but looked at the snapshot of OP and thought: Hmm. `__cdecl` might fix it but I don't have experience with it. I never had guessed that the DLL export viewer shows _a pretty-fied version of the export name_ (I've some experience with DependencyWalker but I cannot remember how it is done there.) How underhanded... – Scheff's Cat Apr 23 '20 at 08:55
  • 1
    @Scheff "_I've some experience with DependencyWalker but I cannot remember how it is done there._" Dependecy Walker has a toggle named "Undecorate C++ functions". – Algirdas Preidžius Apr 23 '20 at 08:59
  • @AlgirdasPreidžius Correct, confirmed. ;-) I'm not sure whether this can be configured (or is remembered somehow) but in my case the "true" (decorated) names are shown by default. That's what I would consider as "less underhanded". (Dep.Walker still rocks.) :-) – Scheff's Cat Apr 23 '20 at 09:03
  • I used DependencyWalker. Without `extern "C"` I could see the undecorated `funci` had the name `?funci@@YGHXZ`, so `funci = (o_funci)GetProcAddress(hGetProcIDDLL, "?funci@@YGHXZ");` worked. With `extern "C"` the undecorated `funci` had the name `_funci@0` - a little cleaner. Using ordinal `0x0001` worked in both cases: `funci = (o_funci)GetProcAddress(hGetProcIDDLL, (PCSTR)0x0001);` Is it a better idea to use ordinal in general? Thank you @tenfour – jubibanna Apr 23 '20 at 09:20
  • 1
    `extern "C"` doesn't avoid name mangling. It merely chooses a different set of name decorations, that are easier to read and type. There's also a 5th option: Compile a 64-bit binary (with `extern "C"`). The name decoration in this case will disappear. – IInspectable Apr 23 '20 at 10:01
  • 2
    @jub: Importing by ordinal requires that you *never* change the order of exports. A pretty tough requirement to satisfy, and hardly justifiable if all you need is more convenient important names. – IInspectable Apr 23 '20 at 10:06
  • [Use a `.def` file](https://learn.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-def-files?view=vs-2019) to control the exact formatting of the exported name. Then you can remove all of the decoration and just export `funci` and have it map internally to `?funci@@YGHXZ` or `_funci@0` as needed – Remy Lebeau Apr 23 '20 at 17:06