如何在运行lua程序时替换.dll

时间:2012-08-09 09:33:51

标签: dll lua

Lua可以使用requirepackage.loadlib动态加载.dll。但是没有卸载模块的功能。

我希望能够在运行程序中更新模块。程序本身将被通知有关.dll的新版本。然后它将加载新的,更改模块表并卸载前一个。

我设法通过使用带有unrequire函数的一些奇怪的代码片段,并强制对代码进行垃圾回收来实现。我希望能够以更文明的方式做到这一点。我知道在Windows和Linux上有函数dlcloseFreeLibrary。但是,我无法调用它们,因为我无法访问库的实际句柄。

缺乏卸载功能的动机是,lua解释器无法真正知道它是否将不再使用模块中的C函数。在我的情况下情况并非如此,因为我只会更新库,而不是完全删除它。

1 个答案:

答案 0 :(得分:2)

有一种方法可以做到这一点,但要使它发挥作用将会有局限性。

基本思想是在Lua函数和脚本之间创建一个间接层require并使用该模块,以及您尝试在幕后更改的实际C函数。最好在您的模块本身中完成。也就是说,让Lua代码替换函数等,让C函数自行替换。

这样,你就掌控了。

这个想法很简单。您有两个模块:一个是Lua注册函数的存根模块,以及带有函数实现的实际模块。只有后一个模块会改变。

您的存根模块是一个Lua模块,它有两个与Lua一起注册的函数。第一个是存根函数,它可以多次注册。它被注册为具有单个upvalue的闭包

该upvalue是一个C函数。这个存根函数唯一能做的就是将一个up值从Lua状态中拉出来并用它给出的相同参数调用它,然后返回被调用函数返回的值。它需要一些堆栈finessing正确传递,但我会假设你知道如何处理它。此外,此函数应检查upvalue的值;如果它是nil,那么什么也不做,什么都不返回(这可以防止错误)。

第二个功能是我们稍后会想到的。

您的存根模块初始化例程将加载您的实际模块,这实际上只是一个DLL。这个DLL需要有一个函数,它将提供实际模块导出的函数列表。因此,它可以查询要转发的各种Lua函数。

对于实际模块中的每个导出函数,它将寄存器函数注册到Lua,并为其命名。存根的upvalue设置为导出的函数,加载时没有upvalues。

显然,需要保留DLL的句柄,可能是注册表项中的用户数据或其他内容。

存根模块导出的第二个功能是将重新加载实际模块的功能。为此,您加载新的DLL(不首先卸载旧的DLL)。为了使这个过程健壮(尽管你保证函数列表是相同的。我不喜欢这样的事情),你需要迭代你的模块表。

对于不在新DLL中的每个注册函数,将其值设置为nil(并将其从模块表中删除)。对于新DLL中的每个注册函数,将upvalue更改为新函数。如果新DLL具有未注册的函数,现在通过使用新的upvalue注册stub函数将其粘贴到表中。

完成所有这些操作后,您可以卸载旧的DLL。

有;完成。

注意:为了使其正常工作,您的模块功能必须良好。他们不能做像注册其他C函数等的事情。他们不能使用Lua 5.2的机制来跨C边界生成和恢复协同程序。等等。否则,您将面临调用已卸载DLL的风险。