0

I'm writing this Editor.exe program that loads a game.dll, gets the address of a function inside the dll, and pass a pointer to a Core object.

gameInitFuncPtr init = 
    (gameInitFuncPtr) GetProcAddress(LoadLibraryA("game.dll"),"gameInit");
init(&core); // core is already instanced somewhere, maybe on the stack

The game.dll includes the core.h where the Core class is defined. The Core class is implemented and compiled into Editor.exe.

On the dll side, calling functions from the passed object pointer, results in an Unresolved external symbol

An example of a call the game.dll would do with the given object pointer would be:

void gameInit(ldk::Core* core)
{
    _core->renderer.drawText("initializing...");
}

How can I compile the dll so that it does not try to find, for example, the drawText() implementation within the dll module ?

1 - Please, note that this is NOT a question about how to declare pointers to member functions.

2 - I know it could easily be fixed if i passed a struct with only pointer to the methods, but I'm really curious about this.

3 - I'm using Microsoft's cl compiler 18.00, the one that ships with Visual studio 2013

marciovmf
  • 11
  • 5

3 Answers3

1

I had a similar problem as you with almost the same setting - Game as dll and Engine as exe. Here are some notes on how to tackle this problem.

Call only virtual methods. As you pointed out, if the method you call is not declared virtual, the linker tries to find an implementation for it and fails (if it's not in the header - a thing we try to avoid). The method does not need to be abstract, virtual is enough. Also, note that in your struct Renderer you can have methods that are not virtual, as long as you don't call them from the dll (if you do, the linker complains). It is probably not advisable to have such an interface, it would be much better to have some sort of API class which has only virtual public methods so users of this class cannot make a mistake.

All classes used from the dll need to be shared or header only. What I mean by this is, that as far as I know, there is no magic way, to have classes declared in header, implemented in cpp which is compiled to the exe and then use these classes from the dll. E.g., if you have a custom string class, it needs to be in a shared library. If it's just in the exe you will not be able to instantiate it in the dll (return it from functions etc.). A solution to this is to use header-only classes. E.g., your string may be implemented in a header in the Editor project and this header may be included by your Game project. This way you essentially compile the same code to both exe and dll.

To see a small working example see my repository with VS 2017 solution which demonstrates this exact problem and nothing else. repo link.

Much larger working example of this problem can be seen in idTech4 engine - DOOM 3 version here. It also uses a game as a dll and an engine as an exe. And also needs to exchange pointers to the engine's systems which are used from the game. The project is big, but if you take a look at project Game-d3xp class Game.h all the way down, they have the game's API with a single function GetGameAPI_t which expects to get gameImport_t struct with pointers to engine systems and returns gameExport_t with game informations. The loading then happens in the Common.cpp

As you can see they use shared library idLib in the respective project for things such as idString. All engine classes used from the dll are usually very small and implemented in headers only (they are mostly structs).

Note that id themselves are moving away from this architecture and even their latest version of DOOM 3 - DOOM 3 BFG edition compiles to a single exe and the modules are static libraries instead of dlls.

Aldarrion
  • 366
  • 3
  • 12
0

It is not clear where you initiallize _core. At first glance gameInit should do it.

Declare interface class Core, i.e. it should be abstract. Implement it in a successor class for example CoreImpl in exe. This will fix Unresolved external symbols.

273K
  • 29,503
  • 10
  • 41
  • 64
  • Core is instanced by the Editor.exe and passed to gameInit(Core* core) that is implemented on the game.dll. – marciovmf Mar 25 '18 at 04:30
0

Looks like I was overthinking it. Wen compiling the editor.exe the Core sould be declared just link any class.

struct Core
{
    struct Renderer
    {
        void drawText(const char* text);
    }
    ...
}

But, since the editor and the game.dll share the same Core.h, I used a macro to modify the declaration of Core.h member functions to be pure virtual, for example:

struct Core
{
    struct Renderer
    {
        virtual void drawText(const char* text) = 0;
    }
    ...
}

So the Unresolved external symbol linking error is gone

BUT: I does not work as expected in RUNTIME! :(

marciovmf
  • 11
  • 5