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.

4
  • 2
    You probably need GetProcAddress(hDLL, "dllclass::run"); or GetProcAddress(hDLL, "newer::dllclass::run"); ? Commented Jan 4, 2017 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. Commented Jan 4, 2017 at 10:22
  • 1
    It is actually something more like "_imp?run@dllclass@newer@@SAHHH@Z" Commented Jan 4, 2017 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? Commented Jan 4, 2017 at 10:29

2 Answers 2

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;
Sign up to request clarification or add additional context in comments.

9 Comments

For me also working fine but what is this "?run@dllclass@newer@@SAHHH@Z"
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.
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).
Got it buddy , Great !
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.
|
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.

4 Comments

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.
Note that DependencyWalker is getting outdated and throws a lot of warnings and errors on things compiled with newer versions of VS.
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.
Or you can view the function names in the generated *.lib file. (or objdump -t xxx.lib)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.