Lua在加载dll扩展时崩溃了。

3

我正在尝试在Windows上创建一个Lua dll扩展。我使用的是Lua 5.3版本,并且我的编译器来自于MinGW,版本是gcc 4.9.3。

我的dll扩展的C代码类似于以下内容:

#include <stdio.h>
#include <lua.h>

static int dub(lua_State *L) {
    const double a = lua_tonumber(L, 1);
    lua_pushnumber(L, a*2);
    return 1;
}

__declspec(dllexport) int __cdecl luaopen_mylib(lua_State *L){
    printf("One\n");
    lua_pushcfunction(L, dub);
    printf("Two\n");
    lua_setglobal(L, "dub");
    printf("Three\n");
    return 1;
}

我这样编译我的动态链接库:

gcc mylib.c -shared -o mylib.dll -llua

这个想法是我可以从Lua中加载它并像这样使用它:

require "mylib"
print (dub(5)) --should print 10

然而,当我尝试运行Lua代码时,它在require "mylib"行崩溃了。该DLL能够打印出"One"和"Two",但它在打印出"Three"之前就崩溃了。这告诉我问题可能出在'lua_setglobal'调用上。
出了什么问题?如何进一步调试或修复它?
额外的问题:luaopen_mylib的返回值应该是什么?
谢谢!

是什么类型的崩溃?它是否会从Lua生成堆栈跟踪?还是C段错误? - Oka
@Oka 我收到了一个Windows弹出消息,上面写着“lua.exe已停止工作”。还有一些细节,但对我来说毫无意义。 - luaboy
1
我不是Windows开发者,但这些细节对于那些是的人可能有意义。在寻求帮助时,包括错误消息/报告通常是一个好主意。 - Oka
你为什么尝试使用cdecl?你尝试过stdcall吗? - moteus
@moteus 我从这里得到了 __cdecl:http://lua-users.org/wiki/LoadLibrary 将其更改为 __stdcall 会使 'require()' 调用显示 "找不到指定的程序。" - luaboy
1个回答

2
我已经编译了你的代码,它能正常运行。因此,你的问题不是来自于你的代码。我猜测你可能正在使用不同版本的Lua,或者链接出现了某些问题。
有三个地方可能会发生版本不匹配的情况:
1. 在你的#include <lua.h>这一行中。 2. 在你的构建行中-llua. 3. 运行lua可执行文件时。
这三者必须都是相同的Lua版本。如果其中一个不匹配,可能会导致你遇到的问题。我猜测你的Lua可执行文件版本与其他文件不匹配。
还有一件事需要尝试的是,你的DLL应该连接到Lua动态链接库。在你的系统上应该有一个名为lua53.dll的文件。将它复制到你的构建目录下。当你使用gcc编译时,请尝试以下做法:
gcc mylib.c -shared -o mylib.dll lua53.dll

这意味着您的dll扩展调用的是与lua可执行文件完全相同的lua代码。现在的情况是,使用-llua行,似乎您正在静态链接lua。对于dll库来说,这几乎永远不是您想要的,因为lua可执行文件将调用lua53.dll中的代码,而您的dll将调用静态库中的单独代码。我认为这并不是导致崩溃的唯一原因,但这不是好的实践方法。如果lua使用全局状态(它没有),这种链接问题肯定会导致崩溃。此外,如果您编译针对lua53.dll,您会发现mylib.dll要小得多。
总之,我认为您在某个地方有一个导致崩溃的lua版本不匹配。出于良好的形式,您还应该链接到lua dll而不是静态库。
作为额外的问题:luaopen_mylib的返回值应该是什么?
在您的代码中,它实际上应该是0而不是1。返回值是您希望require()调用返回的堆栈上剩余的东西的数量。您的库只是将自己塞入全局状态并没有返回任何lua值。另一种做事情的替代方法是返回一个包含库函数的表。这样您就不会污染全局状态。由于您的库只有一个函数,因此可以直接返回它,如下所示:
__declspec(dllexport) int __cdecl luaopen_mylib(lua_State *L){
    lua_pushcfunction(L, dub);
    return 1;
}

然后你可以在lua中这样使用它:
local dub = require("mylib")
print (dub(5)) --should print 10

我更喜欢这种方式,因为调用者可以决定如何命名导入,并且不会污染全局空间。


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