【问题标题】:Embedded Lua does not print to allocated console嵌入式 Lua 不打印到分配的控制台
【发布时间】:2023-03-23 00:28:01
【问题描述】:

我的代码是这样的(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));
}

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

【问题讨论】:

  • 那些错误是什么?
  • @hjpotter92 例如,如果我写的是 foo 而不是 print

标签: windows console lua stdout


【解决方案1】:

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 上尽可能简单地编译和链接,这还不错,因为我安装了一个用于 Windows 的 Lua 副本,恰好留下环境变量 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 确实被正确重定向了。

我在调用freopen() 之前添加了FILE *old=stdout;,之后添加了printf("%p %p %p", fp, stdout, oldstdout);。所有三个指针完全相等,表明freopen() 没有做任何不寻常的事情。

查看 Lua 5.1 实现 print() 函数的源代码,我们发现它只是调用了 fputs(s,stdout)

那么从main() 调用printf() 怎么可能有效,但使用stdout 的类似调用会失败?

解决方案

如果main() 中的stdoutluaB_print() 中的stdout 不同,这是可能的。

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

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

Dependency Walker 的快速检查显示我的测试可执行文件链接到 MSVCRT.DLL(MinGW 首选的 C 运行时),但来自 Lua for Windows 的 lua5.1.dll 链接到 MSVCR80.DLL(来自Visual Studio 2005)。

这个问题很容易解决。我将测试用例更改为链接到与 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 .

【讨论】:

  • 惊人的调查!我还能说什么...我知道不能混合运行时,我认为我从 lua 网站获得了正确的 dll。无论如何,我永远无法想象不同的运行时会引入这样的结果,并且一直认为它会引入更糟糕的程序崩溃错误。
  • @user206334,它可以产生更严重的错误。你得到了“幸运”,只是得到了一个谜。正是因为您的代码表面上是正确的,所以我决定写一个更长的答案来说明哪里出了问题。盯着正确的代码寻找问题往往会导致一种狭隘的眼光。似乎展示如何从不同的角度看待它可能会对您和其他人有所帮助。
【解决方案2】:

另一个可能的原因是luaxx.dll发布了,而测试项目是调试版。所以,luaxx.dll使用的是msvcrxx.dll,而测试项目使用的是msvcrxxd.dll——微软c运行库的调试版。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-07
    • 2015-05-04
    • 2016-11-19
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多