TL;DR:该程序使用了多个版本的 C 运行时库。不要那样做。它总是会导致其他正确代码的神秘症状。
背景
从表面上看,您提供的代码应该可以工作。而且,如果在这里小心建造和链接,我可以让它工作。作为参考,我在 Win7 Pro 上使用 MingW GCC 4.7.2 构建 32 位 Windows。但我相信任何针对 Windows 的编译器都可能出现潜在问题。
我将介绍查找此错误的过程,希望对了解我如何解决此问题有所帮助。但是,如果您不耐烦,请跳到最后,然后回到这里看看我是如何到达那里的。
可测试代码
首先,我将您的代码片段包装在足够的样板中以使其完全编译和运行:
#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>
#include <windows.h>
int main(int argc, char **argv) {
AllocConsole();
FILE *fp = freopen("CONOUT$", "w", stdout);
lua_State *L = luaL_newstate();
luaL_openlibs(L);
if(luaL_dostring(L,
"print 'print works!'\n"
"io.write 'io.write works'"
))
{
printf("%s\n", lua_tostring(L, -1));
}
Sleep(5000); // give me 5 seconds to read the console
}
使用 GCC 编译
我在 Windows 上尽可能简单地编译和链接,这还不错,因为我安装了 Lua for Windows 的副本,恰好使环境变量LUA_DEV
指向它的安装:
gcc -o q15787892 q15787892.c -mwindows -I"%LUA_DEV%\include" "%LUA_DEV%\lua5.1.dll"
该-mwindows
标志告诉 GCC(特别是链接器ld
)将可执行文件标记为完整的 Windows GUI 程序。如果没有该标志,您将获得一个已经分配了控制台的控制台程序,并且 AllocConsole() 将简单地为持有命令提示符的程序提供句柄。
结果
有趣的是,既没有调用print()
也没有io.write()
产生输出。我在 Lua 文本中引入了一个语法错误,并注意到它确实输出到了控制台,表明它stdout
确实被正确重定向了。
我FILE *old=stdout;
在调用之前freopen()
和printf("%p %p %p", fp, stdout, oldstdout);
之后添加了它。所有三个指针完全相等,表明freopen()
没有做任何不寻常的事情。
查看 Lua 5.1 函数实现的源代码,print()
我们发现它只是调用fputs(s,stdout)
.
printf()
那么,对from的调用怎么可能main()
有效,但类似的调用 usingstdout
失败?
解决方案
如果stdout
inmain()
与stdout
in不同,则有可能luaB_print()
。
但是两者都是全局变量,链接器应该使它们相同,对吗?
嗯,不一定。全局变量stdout
是 C 运行时库的一部分。如果 Lua 核心链接到与程序不同的 C 运行时 DLL,那么 Lua 核心和程序实际上可能引用不同的名为stdout
.
对Dependency Walker的快速检查显示我的测试可执行文件与 MSVCRT.DLL(MinGW 首选的 C 运行时)链接,但lua5.1.dll
来自 Lua for Windows 的链接与 MSVCR80.DLL(Visual Studio 2005 中的 C 运行时)链接。
这个问题很容易解决。我将测试用例更改为链接到与 MSVCRT.DLL 链接的 Lua 构建,现在原始代码按预期工作。这是新的构建步骤,现在可以在 BAT 文件中找到,并假设其中lua5_1-4_Win32_dll6_lib
包含正确构建的 Lua 核心:
setlocal
set LUADIR="lua5_1_4_Win32_dll6_lib"
gcc -o q15787892 q15787892.c -mwindows -I"%LUADIR%\include" "%LUADIR%\lua5.1.dll"
if not exist lua5.1.dll copy %LUADIR%\lua5.1.dll .