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

Pass pointer to object to dll

本文关键字:dll 指针 对象      更新时间:2023-10-16

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

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

游戏.dll包括定义核心类的 core.h。 核心类被实现并编译到编辑器.exe中。

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

调用游戏.dll 使用给定对象指针的示例是:

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

如何编译 dll 以便它不会尝试在 dll 模块中找到例如 drawText() 实现?

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

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

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

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

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

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

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

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

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

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

不清楚你在哪里初始化_core.乍一看,gameInit应该这样做。

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

看来我想多了。 温编译编辑器.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;
}
...
}

所以未解析的外部符号链接错误消失了

但是:我在运行时没有按预期工作! :(