在Windows操作系统中使用动态链接库(DLL)的动态模块

3
我正在使用C编写一个应用程序,可以通过模块/共享对象/DLL在运行时进行扩展。这些模块可以使用现有程序的API,但也可以提供新功能供后续加载的模块使用,因此模块之间可能存在依赖关系。
我目前在Linux下的方法是让每个模块都定义一个depends()函数,该函数返回其所依赖的其他模块的名称列表。这样,我可以为每个模块单独编译和链接,在使用dlopen()和RTLD_LAZY加载模块时首先解决其依赖关系,然后使用RTLD_GLOBAL完全加载它。这很好地实现了我的要求。它还允许我用不同版本的模块替换一个模块,而无需重新编译所有依赖它的其他模块。
当我要将其移植到Windows时,实际问题就出现了。首先,我没有找到任何Link DLL的方式,而不必提供其所有依赖项的导出符号表。我有忽略的方法吗?
第二个问题,来自Windows API的LoadLibraryEx似乎无法执行任何延迟加载,因为它不是让我处理依赖项,而是在返回之前先加载所有引用的DLL。由于我希望在实际加载模块之前进行版本检查,这根本不是我想要的。有没有任何方法来规避这种行为?
第三个奇怪的事情是,我不能替换一个DLL而无需重新编译所有依赖它的其他模块。有时确实可以工作,但通常会发生各种问题或程序segfault。
在Windows上是否可能编写类似的模块化应用程序?非常感谢您的任何建议或不同的方法!
更新:只是为了说明我的模块如何在Linux上使用彼此的函数(我也希望在Windows上拥有):每个模块只返回它想从中调用函数的另一个模块的名称,并包含其头文件,然后在代码中直接调用使用的函数,没有任何封装。这可以工作,因为Linux对于共享对象不需要在链接时解析所有符号。
1个回答

4
你可以手动导出所有函数(使用 __declspec(dllexport)),并使用 GetProcAddress 加载它们。在这种情况下,你需要知道每个函数的签名,并且仅限于 C 函数,但这将会起作用。如果你编译了两个模块,你的 C 函数也可以返回 C++ 类,稍后会详细介绍。使用 GetProcAddressLoadLibrary 使模块完全独立。基本上,你需要手动进行链接,但我理解,在 Linux 上也是这么做的,对吧? LoadLibary 仅加载库所依赖的库,因此请确保它们不相互依赖。要么它们确实是独立的,要么它们不是。如果正确地实现,更改一个库不会强制重新编译另一个库(因为你不会将它们链接在一起)。
一个好的想法是使用类似 COM 的东西,使每个库都返回一个接口,而不是单独的函数。这样,你可以简单地加载整个 DLL,并轻松地将它们链接在一起(传递 DLL -> 传递对象)。查询 XPCOM 和 COM,实际上很容易做到。

1
好的,你关于 GetProcAddress 的建议已经有所帮助了。我刚刚尝试了一下,通过这种方式调用 DLL 中的函数可以在没有链接的情况下工作。但这意味着我不能像在 Linux 中那样让一个模块包含它依赖的模块的头文件,对吧?在调用父模块中的任何内容之前,我必须先使用 GetProcAddress 收集所有需要的函数指针。感谢你提供 COM 的提示,我会去看看的。 - smf68
1
是的,但正如我所说,只需将一个模块的所有函数分组到一个类中,然后调用该类即可。这样更容易,并且还可以减少导出的符号数量。这不算真正的COM,因为涉及到继承,它只是基于一个类似的想法。您的客户端使用接口,不知道其背后的实际实现。 - Anteru
жңүи¶Јзҡ„иҜқйўҳгҖӮжүҖд»ҘжІЎжңүеҠһжі•еңЁдёҚдәҶи§ЈWinAPIе’Ң/жҲ–COMзҡ„жғ…еҶөдёӢе®ҢжҲҗжЁЎеқ—зҡ„ејҖеҸ‘еҗ—пјҹ :-( - Gregor
1
当然可以,你可以做一些像COM一样的事情,而不实际使用COM。就像我说的,在最简单的情况下,你有类似这样的东西: struct Interface { your methods here ... };,并且你的模块包括extern "C" Interface* GetInterface(); -- 这样你只获取GetInterface的地址,并在该模块指针上工作。 - Anteru
啊好的,我理解错了,这个应该是指“接口”。 - Gregor
由于似乎没有其他方法可以做到这一点,我将其标记为已回答。 - smf68

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接