I have a rather simplistic plugin system that loads and re-loads dlls at runtime. There is no static linking invovled whatsoever. The .exe consists only of the code necessary to load the dlls. It currently does this under Windows by using the LoadLibrary/GetProcAddress/FreeLibrary functions. (In VisualStudio) no additional library directories, project references or libs are set.
What I AM doing however is including some header files from the dll projects that I am later loading dynamically (in case that matters). Now I am aware that I probably should only use the interface functions that get queried by GetProcAddress to make calls to the dll but this is the first time I am doing it so I'm just figuring stuff out.
Now let's say I get the function pointer for a CreateWindow function like so:
void LoadPlugin(PlatformPlugin* plugin)
{
plugin->Handle = LoadLibrary(plugin->PLUGIN_FILE_TEMP);
plugin->CreateWindow = (PLUGIN_PLATFORM_CreateWindow)GetProcAddress(plugin->Handle, "CreateWindow");
}
The CreateWindow function returns a pointer to a Window object that contains mostly virtual functions and gets inherited by a platform specific Window class like WindowsWindow:
class Window
{
public:
virtual unsigned int GetWidth() const = 0;
virtual unsigned int GetHeight() const = 0;
};
class WindowsWindow : public Window
{
public:
virtual unsigned int GetWidth() const override;
virtual unsigned int GetHeight() const override;
};
Of course there existst a .cpp file with the definitions as well. Here comes the 'issue': When calling CreateWindow I get the valid pointer and when calling GetWidth on it I get a correct result as well. But as you may have noticed I never exported any of the Window classes or functions. As of my understanding I should not be able to call GetWidth but I am. Dependencywalker confirms that and shows me only CreateWindow as an exported function. How am I able to call it anyways? The included header files only gives the compiler the information necessary to call the function in theory but since the header does not actually include the function implementation how can the linker know the address of my function?
The addresses of virtual functions are stored in an object's vtable. You therefore don't need exported symbols to call these functions, just a valid object (or the address of one) and knowledge of the vtable's layout (which is provided by your header files).
The compiler can then index into the vtable to find the address to be called and call it.