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.
- 1st question: What happened? What changed for the compiler and the linker? The use of the function pointer on initializeObject() solves the problem.
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.
2nd question: Does this means that methods must always be
virtual
using DLL run-time linking?3rd question: How can I have the C++ function exportation with the Windows API? So that I could remove the
extern "C" { ... }
stuff.
Thanks for your reading! I hope I will read interesting answers! :)