Pierre Pagnoux
Pierre Pagnoux

Reputation: 11

What C++ compiler/linker does when using runtime DLL loading?

I would like to understand the DLL mechanism and what the compiler does when I loads the DLL at run-time (i.e. I will not use the generated .lib).

Consider the following C++ code:

DLL interface header file

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

class MYDLL_API Base
{
public:
  Base();

  virtual ~Base();

  virtual int get_number() const;
  virtual const char* what() const = 0;

private:
  int i_;
};

class MYDLL_API Child : public Base
{
public:
  Child();

  virtual ~Child();

  virtual int get_number() const override;
  virtual const char* what() const override;
private:
  int j_;
};

extern "C" {
  MYDLL_API Base* __cdecl initializeObject();
}

DLL implementation source file

#include "MyDLL.hh"

Base::Base()
  : i_(42)
{}

Base::~Base()
{}

int Base::get_number() const
{
  return i_;
}

Child::Child()
  : Base()
  , j_(24)
{}

Child::~Child()
{}

int Child::get_number() const
{
  return j_;
}

const char* Child::what() const
{
  return "Hello!";
}

Base* initializeObject()
{
  return new Child();
}

The goal of this DLL is to have a common interface defined by the Base class, but it allows specifics implementations compiled in different DLLs that are loaded at runtime (here the Child class is exposed for the purpose of the example).

At this stage, if I naively include the DLL's header:

#include "MyDLL.hh"

int main()
{
  Base* b = new Child();

  std::cout << b->get_number() << std::endl;
  std::cout << b->what() << std::endl;

  delete b;

  getchar();
  return 0;
}

The linker complains LNK2019 and LNK2001 errors: it can not resolves symbols. So, it behaves as expected (I did not use the .lib).

Consider now, the following code that I use to load the DLL at runtime:

#include "MyDLL.hh"

typedef Base* (*initFuncType)();

int main()
{
  HINSTANCE handle = LoadLibrary(L"MyDLL.dll");
  initFuncType init = nullptr;
  init = (initFuncType)(GetProcAddress(handle, "initializeObject"));
  if (init)
  {
    Base* b = init(); //< Use init() !

    std::cout << b->get_number() << std::endl;
    std::cout << b->what() << std::endl;

    delete b;
  }
  getchar();
  FreeLibrary(handle);
  return 0;
}

This time it works, the linkage is done.

The other issue I do not understand well is when I remove virtual and override of get_number():

int get_number() const;

I have a LNK2019 error because of the unresolved Base::get_number(void) const symbol in the _main function. I understand that the virtual keyword will resolve the member function dynamically (at run-time). In our case, the DLL is not loaded yet, the get_number symbol is not available.

Thanks for your reading! I hope I will read interesting answers! :)

Upvotes: 1

Views: 656

Answers (1)

MichaelCMS
MichaelCMS

Reputation: 4763

There are two ways to link dll files.

The 2nd way (the way it works) is the C Binding approach, where you query the dll for a specific function name and it returns a functor to you.

Using the 2nd way you won't be able to extend the base classes, since they are not defined (you don't have any code to be copy pasted so to speak at linkage time).

In order to have a dll who's classes can be extended, you will need to use dynamic binding. You need to compile your .dll and also provide a Symbols Library (or an export library). You have this option in VS studio in project properties.

The mechanism is as following :

  • Compile Dll project -> output : myLib.dll , myLib.lib
  • Use exported symbols from myLib.lib inside your main project (main project takes myLib.lib as dependency)
  • at runtime,due to the binding, your program will know it requires myLib.dll to work so it will load it (if found, else you get runtime error)

Another advantage of using Export Library is that you can export C++ functions (which are mangled on export).

It's very hard to have C Binding on mangled functions.

C Binding on the otherhand, compared to the dynamic binding, won't make your program scream if myLib.dll isn't found , you will just get a null pointer to function.

Upvotes: 0

Related Questions