将指向对象的指针传递给 dll

Pass pointer to object to dll

提问人:marciovmf 提问时间:3/25/2018 最后编辑:marciovmf 更新时间:7/20/2018 访问量:1388

问:

我正在编写这个编辑器.exe程序,它加载游戏.dll,获取dll中函数的地址,并将指针传递给Core对象。

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

game.dll 包括定义 Core 类的 core.h。 Core 类已实现并编译为 Editor.exe。

在 dll 端,从传递的对象指针调用函数会导致 Unresolved 外部符号

game.dll 将对给定对象指针执行的调用示例如下:

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

我怎样才能编译dll,使其不会尝试在dll模块中查找drawText()实现?

1 - 请注意,这不是关于如何声明指向成员函数的指针的问题。

2 - 我知道如果我传递一个只有指向方法指针的结构,它可以很容易地修复,但我对此真的很好奇。

3 - 我正在使用 Microsoft 的 cl 编译器 18.00,这是 Visual Studio 2013 附带的

C++ C DLL visual-studio-2013 未解析的外部

评论


答:

0赞 273K 3/25/2018 #1

目前尚不清楚您在哪里初始化。乍一看应该这样做。_coregameInit

声明接口类,即它应该是抽象的。在后继类中实现它,例如 exe 中的 CoreImpl。这将修复未解析的外部符号。Core

评论

0赞 marciovmf 3/25/2018
Core 由 Editor.exe 实例化,并传递给在 game.dll 上实现的 gameInit(Core* core)。
0赞 marciovmf 3/26/2018 #2

看来我想多了。 温编译编辑器.exe,核心灵魂被声明,只需链接任何类。

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

但是,由于编辑器和游戏.dll共享相同的Core.h,我使用宏将Core.h成员函数的声明修改为纯虚拟,例如:

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

因此,未解决的外部符号链接错误消失了

但是:我在 RUNTIME 中没有按预期工作!:(

1赞 Aldarrion 7/20/2018 #3

我遇到了与您类似的问题,设置几乎相同 - 游戏作为 dll,引擎作为 exe。以下是有关如何解决此问题的一些说明。

仅调用虚拟方法。正如你所指出的,如果你调用的方法没有被声明为虚拟方法,链接器会尝试为它找到一个实现,但会失败(如果它不在标头中 - 我们试图避免这种情况)。方法不需要抽象,虚拟就足够了。另外,请注意,在您的结构中,您可以拥有非虚拟的方法,只要您不从 dll 调用它们(如果这样做,链接器会抱怨)。拥有这样的接口可能并不可取,最好有某种只有虚拟公共方法的 API 类,这样这个类的用户就不会犯错误。Renderer

dll 中使用的所有类只需要共享或仅标头。我的意思是,据我所知,没有神奇的方法可以在header中声明类,在cpp中实现,cpp被编译到exe,然后从dll中使用这些类。例如,如果您有一个自定义字符串类,它需要位于共享库中。如果它只是在 exe 中,您将无法在 dll 中实例化它(从函数等返回它)。解决此问题的方法是使用仅标头类。例如,您的字符串可能在 Editor 项目的标头中实现,并且此标头可能包含在您的游戏项目中。这样,您基本上可以将相同的代码编译为 exe 和 dll。

要查看一个小的工作示例,请参阅我的 VS 2017 解决方案存储库,该解决方案演示了这个确切的问题,仅此而已。回购链接

这个问题的更大工作示例可以在idTech4引擎中看到 - DOOM 3版本在这里。它还使用游戏作为 dll,使用引擎作为 exe。并且还需要交换指向游戏中使用的引擎系统的指针。这个项目很大,但如果你一直看一下项目类 Game.h,他们就会发现游戏的 API 只有一个函数,它期望通过指向引擎系统的指针获取结构,并返回游戏信息。然后加载发生在 Common.cppGame-d3xpGetGameAPI_tgameImport_tgameExport_t

如您所见,他们在各自的项目中使用共享库来执行诸如 .dll 中使用的所有引擎类通常都非常小,并且仅在标头中实现(它们大多是结构)。idLibidString

请注意,id 本身正在远离这种架构,甚至他们最新版本的《DOOM 3 - DOOM 3 BFG edition》也编译为单个 exe,并且模块是静态库而不是 dll。

评论

0赞 Aldarrion 8/16/2018
@downvoter如果你能告诉我你不喜欢什么,我很乐意改进这个问题。