我正在编写这个加载game.dll的Editor.exe程序,获取dll中函数的地址,并将指针传递给Core对象。
gameInitFuncPtr init =
(gameInitFuncPtr) GetProcAddress(LoadLibraryA("game.dll"),"gameInit");
init(&core); // core is already instanced somewhere, maybe on the stack
game.dll包含core.h,其中定义了Core类。 Core类已实现并编译为Editor.exe。
在dll方面,从传递的对象指针调用函数会产生未解析的外部符号
game.dll对给定对象指针的调用示例如下:
void gameInit(ldk::Core* core)
{
_core->renderer.drawText("initializing...");
}
如何编译dll以便它不会尝试查找dll模块中的drawText()实现?
1 - 请注意,这不是关于如何声明指向成员函数的指针的问题。
2 - 我知道如果我传递的结构只有指向方法的指针,那么很容易修复它,但我对此非常好奇。
3 - 我使用的是Microsoft的cl编译器18.00,与 Visual Studio 2013一起提供的
答案 0 :(得分:0)
目前尚不清楚初始化internalFrame
的位置。乍一看_core
应该这样做。
声明接口类gameInit
,即它应该是抽象的。在后继类中实现它,例如在exe中的CoreImpl。这将修复未解析的外部符号。
答案 1 :(得分:0)
看起来我正在思考它。 编译editor.exe的文件可以声明Core只链接任何类。
struct Core
{
struct Renderer
{
void drawText(const char* text);
}
...
}
但是,由于编辑器和game.dll共享相同的Core.h,我使用宏来修改Core.h成员函数的声明为纯虚拟,例如:< / p>
struct Core
{
struct Renderer
{
virtual void drawText(const char* text) = 0;
}
...
}
所以未解析的外部符号链接错误消失了
但是:我在RUNTIME中没有按预期工作! :(
答案 2 :(得分:-1)
在设置上几乎和您一样,我也遇到了类似的问题-dll和Game分别为dll和Game。以下是有关如何解决此问题的一些说明。
仅调用虚拟方法。如您所指出,如果调用的方法未声明为虚拟方法,则链接器将尝试为其找到一个实现并失败(如果它不在标头中-我们尽量避免的事情)。该方法不需要是抽象的,虚拟的就足够了。另外,请注意,在结构Renderer
中,您可以具有非虚拟的方法,只要您不从dll调用它们(如果这样做,链接器就会抱怨)。最好不要有这样的接口,最好有某种API类,该类仅具有虚拟公共方法,这样此类的用户就不会犯错。
从dll使用的所有类都只需要共享或仅是头文件。我的意思是,据我所知,在头文件中声明类没有任何神奇的方法。 ,在cpp中实现,后者被编译为exe,然后从dll使用这些类。例如,如果您有一个自定义字符串类,则它必须位于共享库中。如果只是在exe中,您将无法在dll中实例化它(从函数等中返回它)。解决方案是使用仅标头的类。例如,您的字符串可以在Editor项目的标头中实现,并且此标头可能包含在Game项目中。这样,您基本上可以将相同的代码编译为exe和dll。
要查看一个小型工作示例,请参阅我的带有VS 2017解决方案的存储库,该存储库演示了此确切问题,仅此而已。 repo link。
在idTech4引擎-DOOM 3版本here中可以看到此问题的更大示例。它还将游戏用作dll,将引擎用作exe。并且还需要交换指向游戏中使用的引擎系统的指针。该项目很大,但是如果您从头到尾查看项目Game-d3xp
类Game.h
,他们的游戏API都带有一个函数GetGameAPI_t
,该函数希望获得{{1 }}具有指向引擎系统的指针的结构,并返回gameImport_t
和游戏信息。然后,加载会在Common.cpp
如您所见,他们在各自的项目中使用共享库gameExport_t
来处理诸如idLib
之类的事情。 dll中使用的所有引擎类通常都很小,仅在标头中实现(它们大多是结构)。
请注意,id本身正在脱离此体系结构,甚至其最新版本的DOOM 3-DOOM 3 BFG edition都可以编译为单个exe,并且模块是静态库而不是dll。