嵌入式 Lua 不打印到分配的控制台

use*_*334 1 windows console lua stdout

我的代码如下(Windows 平台):

AllocConsole();
FILE *fp = freopen("CONOUT$", "w", stdout); //couldn't find documentation what CONOUT$ actually is
lua_State *lua_state = luaL_newstate();
luaL_openlibs(lua_state);
if(luaL_dostring(lua_state, "print 'It works!'"))
{
    printf("%s\n", lua_tostring(lua_state, -1));
}
Run Code Online (Sandbox Code Playgroud)

我无法获得 Lua 输出,而正常的 printf 可以工作(Lua 错误也会被打印)

RBe*_*eig 5

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
}
Run Code Online (Sandbox Code Playgroud)

使用 GCC 编译

我在 Windows 上尽可能简单地编译和链接,这还不错,因为我安装了 Windows 版 Lua 的副本,它恰好让环境变量LUA_DEV指向其安装:

gcc -o q15787892 q15787892.c -mwindows -I"%LUA_DEV%\include" "%LUA_DEV%\lua5.1.dll"
Run Code Online (Sandbox Code Playgroud)

-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()frommain()有效,但类似的调用 using 却stdout失败呢?

解决方案

stdout如果in与inmain()不同,则有可能。stdoutluaB_print()

但两者都是全局变量,链接器应该使它们相同,对吧?

嗯,不一定。全局变量stdout是 C 运行时库的一部分。如果 Lua 核心链接到与程序不同的 C 运行时 DLL,则 Lua 核心和程序实际上可能引用名为 的不同变量stdout

使用Dependency Walker进行快速检查显示,我的测试可执行文件与 MSVCRT.DLL(MinGW 首选的 C 运行时)链接,但lua5.1.dll来自 Windows 的 Lua 与 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 .
Run Code Online (Sandbox Code Playgroud)