1

I tried to load C++ dll dynamically, first I loaded the dll using "LoadLibrary" function and it is getting its handle correctly. After that I tried to get the function pointer of DLL file function using "GetProcAddress", it is returning NULL. Please find my DLL code and testing application code and let me know where is going wrong in the code.

dummy2.h

namespace newer
{
  class dllclass
  {
    public:
        static __declspec(dllexport) int run(int a,int b);
  };
}

dummy2.cpp

#include <iostream>
using namespace std;

#include "dummy2.h"

namespace newer
{
  int dllclass::run(int a,int b)
  {
    return a+b;
  }
}

dummy1.cpp

#include "stdafx.h" 
#include <windows.h>

#include <iostream>
using namespace std;
typedef int (*Addition)(int,int);

int _tmain(int argc, _TCHAR* argv[])
{
  Addition add;
  HINSTANCE hDLL;
  hDLL = LoadLibrary(TEXT("Dummy2.dll"));

  add = (Addition)GetProcAddress(hDLL, "run");  

  getchar();
  return 0;
}

Please refer above code and guide me.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
sivanesan1
  • 779
  • 4
  • 20
  • 44
  • 2
    You probably need `GetProcAddress(hDLL, "dllclass::run"); ` or `GetProcAddress(hDLL, "newer::dllclass::run");` ? – Paul R Jan 04 '17 at 10:06
  • 2
    GetProcAddress must know the **exact** name that is exported, including casing and any special characters, hyphens, question marks, @ symbols, etc. So "Run" is not the same as "run" which is not the same as "run@4". You need to load the DLL in something like Dependency Walker or other tool to see what the *exact* name is, and that is the name you must use in GetProcAddress. – PaulMcKenzie Jan 04 '17 at 10:22
  • 1
    It is actually something more like "__imp_?run@dllclass@newer@@SAHHH@Z" – EmDroid Jan 04 '17 at 10:23
  • 3
    From a linkage point of view, there is no function called "run". How do you expect to tell the difference between "run"s in different namespaces or as members of different classes? – molbdnilo Jan 04 '17 at 10:29

2 Answers2

7

It's because the name is mangled (i.e. the name of the function is not "run" but something different).

Your code will work with (for MSVC 2013 where I tested):

add = (Addition)GetProcAddress(hDLL, "?run@dllclass@newer@@SAHHH@Z");
cout << add(1, 2) << endl;

In general, if you want to load a class via plugin, your best shot is to use virtual interface. An example:

//dummy2.h
namespace newer
{
  class dllclass_interface
  {
    public:
        virtual int run(int a,int b) = 0;
 };

}

extern "C" __declspec(dllexport) newer::dllclass_interface* getDllClass();

//dummy2.cpp
#include <iostream>
using namespace std;

#include "dummy2.h"

namespace newer
{
  class dllclass: public dllclass_interface
  {
    public:
        virtual int run(int a,int b);
 };

  int dllclass::run(int a,int b)
  {
    return a+b;
  }
}

extern "C" newer::dllclass_interface* getDllClass()
{
    static newer::dllclass instance;
    return &instance;
}

typedef newer::dllclass_interface* (*GetClassFunc)();

GetClassFunc getClassFunc = (GetClassFunc)GetProcAddress(hDLL, "getDllClass");

newer::dllclass_interface* dllClass = getClassFunc();
cout << dllClass->run(a, b) << endl;
EmDroid
  • 5,918
  • 18
  • 18
  • For me also working fine but what is this "?run@dllclass@newer@@SAHHH@Z" – sivanesan1 Jan 04 '17 at 12:05
  • This is the actual name of the `never::dllclass::run()` function as exported from the DLL. Consider that you would have another class `dllclass2` which would also provide `run()` in the same DLL, or another `run()` override inside the same class - how would the exported name distinguish between them? Therefore the name mangling (decoration) is in place. You can see that the name contains also the namespace and the class name, and the other characters are there according to the parameters to distinguish between different overloads. – EmDroid Jan 04 '17 at 13:02
  • And note that the name will look completely different if exported by a different compiler (GCC, Borland etc.). Therefore the virtual interface works better (notice that there is then only one `extern "C"` exported accessor for the class intance and the name is not mangled - with `extern "C"` it is the same simple for all compilers, so you can use the DLL even between different compilers). – EmDroid Jan 04 '17 at 13:05
  • Got it buddy , Great ! – sivanesan1 Jan 04 '17 at 15:56
  • *so you can use the DLL even between different compilers* - simple functions will work between different compilers that way, but the virtual interface won't. You can't pass a non-POD class object across compiler boundaries, because there's no standard for, e.g., how the vtables are laid out. – Harry Johnston Jan 04 '17 at 23:36
  • @HarryJohnston In general that's true (it is not defined in the standard), however on most platforms the virtual table layout is pretty consistent. For example on Windows, the uniform virtual table layout is prerequisite for COM to work. So indeed compiler can implement virtual tables in a different way (or even use different means than virtual tables), but then it couldn't use COM. Of course if you'd like to be completely safe, you'd need to implement your own virtual table mechanism (via function pointers). – EmDroid Jan 05 '17 at 09:06
  • @axalis: I'm no expert, but I'm pretty sure COM doesn't work that way. The C++ wrapper classes are local to your code, the calls are converted to plain C function pointer calls before crossing any module boundaries. Otherwise, cross-language COM calls wouldn't work. And even different versions of Visual C++ aren't binary compatible according to the answers to [this question](http://stackoverflow.com/q/1600399/886887). – Harry Johnston Jan 05 '17 at 20:28
  • Well, it's not from my own head, see e.g. [here](http://eli.thegreenplace.net/2011/09/16/exporting-c-classes-from-a-dll/) and [here](https://chadaustin.me/cppinterface.html) (that one mentions the COM). Indeed there are limitations, like: all pure virtual methods, non-virtual destructor, no multiple/virtual inheritance, no overloaded methods, no C++ exceptions passing the DLL boundary and few others. However I've used this successfully across MSVC (various version incl. 6.0)/GCC/Watcom/Borland/DMC compilers where I've tested that (even mixture of dbg/rls builds between different compilers). – EmDroid Jan 05 '17 at 21:21
  • Another limitation is indeed no use of STL (or any compiler-specific concrete classes) in public interface, no concrete structs (all must be pure virtual interfaces), only using fixed size integer types (especially no "true" enums, no bools) etc. – EmDroid Jan 05 '17 at 21:26
1

In fact, DLLs were introduced back in the times of C. Since then C++ introduced function names overloading (depending on the types of parameters) and something called "mangled names" to allow linking function call with the proper name. C++ standard does not specify how this name should look like. Different compilers implemented embedding types of parameters into the names differently.

C++ understands this problem and sometimes it is necessary to have predictable name. There is special construct in C++ for that:

extern "C"
{
     int run(int, int);
}

When you specify the name of the function in GetProcAddress it should be exactly as it was exported from DLL. You can view these names using special utilities like DependencyWalker.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • The name of the function may still be decorated, but not mangled. The way to get a clean name is to both unmangle the name, and to specify the actual name in a module definition file (DEF) file. – PaulMcKenzie Jan 04 '17 at 10:21
  • 1
    Note that DependencyWalker is getting outdated and throws a lot of warnings and errors on things compiled with newer versions of VS. – rubenvb Jan 04 '17 at 10:23
  • If you use Dependency Walker to solely get the exported function names, it works correctly. Other usage may be "outdated", but to get a list of the exported names, not so much. – PaulMcKenzie Jan 04 '17 at 10:25
  • 1
    Or you can view the function names in the generated *.lib file. (or `objdump -t xxx.lib`) – EmDroid Jan 04 '17 at 10:37