创建一个能够被Lua加载的C++ DLL

9
我有一个使用Lua链接为dll(非静态)的小应用程序。我想使用package.loadlib (libname, funcname)通过Lua加载我自己编写的c++ dll。为此,我需要导出一个遵循Lua的lua_CFunction协议的函数。显然,为此我必须将lua.h合并到我的项目中,并使用Lua的函数传递参数和结果。所以我的问题是:
  1. 我的DLL会使用已经加载到小应用程序进程中的Lua dll吗?
  2. package.loadlib会立即加载和卸载我的DLL,还是一旦加载,直到lua脚本执行结束或应用程序终止,我的DLL就一直存在?
2个回答

7

从您具体的问题开始:

  1. 是的,但仅当您的DLL与之隐式链接时。请注意这一点,因为如果您意外地将两个Lua VM的副本链接到应用程序中,这可能会导致极大的混乱。同样的问题也适用于C运行时库。我建议使用Dependency Walker加载整个应用程序,以验证它只引用了一个Lua DLL实例和一个单独的C运行时。

  2. 我的理解是,package.loadlib()仅负责加载和链接到命名库中的命名函数。只要返回的函数对象(表示您命名的lua_CFunction)存在,那么DLL肯定已加载。如果您失去对该函数的最后一个引用,则该库可能会被垃圾回收,并且如果被回收,它将被卸载。在Lua-L邮件列表上已经讨论了如何确保特定DLL被卸载的问题。否则,如果您只是假设只要您可以访问其中存储的函数,DLL就已加载,那么您就没问题。

让我补充一下,建立在此之上的模块系统是用C或C++代码扩展Lua的更好方法。 《Lua编程》第26章 更详细地描述了这一点,并且链接指向第一版在线副本中的该章节(描述了Lua 5.0)。请注意,模块系统在Lua 5.1中有所改变,然后又在Lua 5.2中改变了一些。获取PiL的第二版或第三版副本(通过许多书商提供纸质和电子书形式)可能会有所帮助。
这里是执行摘要:在C语言中创建名为foo的模块,需要创建一个foo.dll文件,其中至少导出一个函数,其原型为int luaopen_foo(lua_State *L)。该函数应加载您的模块(通常使用Lua 5.1中的luaL_register(),或使用Lua 5.2中的luaL_newlib()luaL_setfuncs()注册一个包含C函数的表),并返回该表。在Lua方面,将DLL放置在package.cpath中描述的路径上,然后可以通过local foo = require "foo"之类的代码加载它。各种Lua 5.x版本之间存在其他细微差别,但相对而言,创建可编译为任何版本的C代码相对简单。
这样做的好处是,该模块可以从路径加载,可以用C或Lua编写,也可以混合使用两者,并且与其他模块兼容。您还可以通过一次require调用加载所需数量的C函数。

非常感谢您的出色回答。它对我帮助很大!除了Lua功能外,我希望我的DLL可以钩取应用程序的WndProc并添加自定义工具栏,因此我不希望我的DLL在我不知道的情况下被卸载。 - Aoi Karasu
1
你可以通过持有自己的额外引用来获得对其生命周期的一些明确控制,只有在准备好时才会释放。您可以使用luaL_ref()LUA_REGISTRYINDEX中的表格一起获取对任何可以在C端保持(以整数形式)的对象的引用,并使用luaL_unref()进行释放。 - RBerteig
对于刚发现这个问题的任何人,从Lua 5.2开始,luaL_register已被弃用;应该使用luaL_newlib,因为动态加载的模块不应该在全局环境中注册任何内容。 - bcrist
@bcrist 我已经更新了答案,包括一个链接到5.2参考手册,作为提示,最佳实践随着时间的推移而改变。 - RBerteig

-1

您可以动态链接到与应用程序相同的Lua dll。

至于package.loadlib,我不知道它是如何工作的。阅读源代码?


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