1

我有一个 C/C++ 程序,为 Windows 编写,并用 Visual Studio 编译。它是一个命令行工具,这意味着它可以合理地在 Cygwin Bash 下运行,可能在 MinTTY 中,并且它的一些用户现在正在这样做。

我想改变我的程序,让它与 Cygwin 一起玩得更好。

我的程序目前使用Windows 控制台 API来输出漂亮的彩色文本并管理光标,并且通常是“交互式的”(或 1980 年代“交互式”定义的“交互式”),至少当它的输出不是管道传输到文件。

为了让我的程序更好地与 Cygwin 配合使用,我可以让它发出 ANSI\x1B[...转义码而不是调用控制台 API,但真正的问题是知道何时这样做


我认为有四种情况:

  1. 作为交互式 Windows 控制台程序调用。-->(使用控制台 API。

  2. 作为普通的 Windows 控制台程序调用,但用户在某处通过管道传输其输出。-->(根本不发出任何样式或交互式调用。

  3. 由 Bash 或在 MinTTY 下作为交互式程序调用。-->(使用 ANSI 转义码。

  4. 由 Bash 调用,但用户将其输出传送到某处。-->(根本不发出任何样式或交互式调用。

我可以通过测试句柄是否GetConsoleMode()成功来区分案例1 ;stdout如果成功,我肯定有一个 Windows 控制台,当我打开代码以使用控制台 API 时就是这种情况。

我想以不同的方式对待案例 3 并使用 ANSI 代码,但不幸的是,案例 2、3 和 4 似乎几乎无法区分:

  • 对于所有三种情况,stdout句柄似乎只是一个不透明的对象。FILE_TYPE_PIPE
  • 我可以使用OSTYPE环境变量,至少猜测我在 Cygwin 下,它将案例 2 与案例 3 和 4 区分开来。
  • 如果我可以链接到cygwin1.dll,我可能会以某种方式使用它isatty(),但我不能链接到它,因为我的许多用户没有(也不会)安装 Cygwin。(而且我根本不相信这会奏效,即使它是可能的。)
  • MSVC 的本地人_isatty()认为 Cygwin Bash 是一个管道,而不是一个交互式外壳,因为它只是在底层使用GetFileType()

简而言之,我看不出将案例 3 与案例 4 分开的明显方法。

现在,我将案例 3 与案例 2 和案例 4 完全相同,Cygwin 用户(包括我自己)得到了糟糕的玻璃电传打字机交互,而不是友好的交互式全屏显示,我真的很想解决这个问题。


那么有什么方法可以将交互式Cygwin 调用与其他调用区分开来,以便我的本机 Windows 程序在被 Cygwin Bash 或其他类似的 shell 调用时表现得很好?

重要提示:一个解决方案不在讨论范围内:该程序不会被编译为原生 Cygwin 程序。这是一个 Windows 程序,它是使用 Microsoft 工具链编译的,不会使用 GCC 编译。目标是更改/更新它以使其在 Cygwin 下表现得尽可能好——而不实际链接到任何 Cygwin DLL。我只能合理地为 Windows 分发一个可执行文件,而不是两个。)

4

1 回答 1

0

如果我可以链接到 cygwin1.dll,我可能会以某种方式使用它的 isatty(),但我不能链接到它,因为我的许多用户没有(也不会)安装 Cygwin。(而且我根本不相信这会奏效,即使它是可能的。)

好的,您不能静态链接到该 DLL,但这并不意味着您不能尝试动态加载它。

你可以试试LoadLibrary("cygwin1.dll")。如果失败,则说明它没有在 cygwin 下运行,您可以使用原生 Windows 函数来检测控制台类型。

另一方面,如果您可以打开该库(这意味着 Cygwin 在当前系统中可用),那么您可以执行动态调用isatty()并使用结果来查看输出是否被重定向。

编辑:仅仅加载cygwin DLL并调用函数并不容易,您需要先初始化lib,如this link所示

提取物:

static BOOL setup_root()
{
    HMODULE hCygwin = NULL;

    // Load the cygwin dll into our application.
    if(!load_cygwin_library(&hCygwin))
        return FALSE;

    // Init the cygwin environment. (Required)
    if(!init_cygwin_library(hCygwin))
        return FALSE;

两个有趣的初始化程序。如果init_cygwin_library未调用,则进一步调用可能不起作用:

static BOOL load_cygwin_library(HMODULE* hCygwin)
{
    if((*hCygwin = GetModuleHandleW(cyglibrary)) == NULL)
        if((*hCygwin = LoadLibraryW(cyglibrary)) == NULL)
            return FALSE;
    return TRUE;
}

static BOOL init_cygwin_library(HMODULE hCygwin)
{
    cygwin_dll_init_fn cygwin_dll_init = NULL;
    if((cygwin_dll_init = (cygwin_dll_init_fn)GetProcAddress(hCygwin,"cygwin_dll_init")) == NULL) {
        FreeLibrary(hCygwin);
        return FALSE;
    }
    cygwin_dll_init();
    return TRUE;
}
于 2017-07-20T20:15:47.610 回答