我如何与Cygwin玩得很好?

时间:2017-07-20 19:40:50

标签: c windows bash cygwin mintty

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

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

我的程序目前使用Windows Console APIs输出漂亮的彩色文本并管理光标,并且通常是"交互式" (或"互动"对于20世纪80年代的定义"互动"),至少在其输出没有通过管道传输到文件时。

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

我认为有四种情况:

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

  2. 作为普通的Windows控制台程序调用,但用户正在将其输出汇总到某处。 - > (根本不发布任何样式或互动电话。

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

  4. 由Bash调用,但是用户正在将输出汇总到某处。 - > (根本不发布任何样式或互动电话。

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

    我希望以不同的方式处理案例3并使用ANSI代码,但遗憾的是,案例2,3和4似乎几乎无法区分:

    • 对于所有三种情况,stdout句柄似乎只是FILE_TYPE_PIPE的不透明对象。
    • 我可以使用OSTYPE环境变量,至少猜测我在Cygwin下面,这将案例2与案例3和4区分开来。
    • 如果我可以链接cygwin1.dll,我可能会以某种方式使用其isatty(),但我无法与之相关联,因为我的许多用户都没有(并赢得了&# #39; t)安装了Cygwin。 (即使有可能,我也会not at all convinced that'd work。)
    • MSVC的本地_isatty()认为Cygwin Bash是一个管道,而不是一个交互式shell,因为它只是在引擎盖下使用GetFileType()

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

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

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

    重要提示: 一个解决方案的解决方案:程序 不会 被编译为本机Cygwin程序。这是一个Windows程序,它是用Microsoft工具链编译的,它不会用GCC编译。目的是更改/更新它以表现得很好尽可能在Cygwin下 - 没有实际链接任何Cygwin DLL。我只能合理地为Windows分发一个可执行文件,而不是两个。)

1 个答案:

答案 0 :(得分:0)

  

如果我可以链接cygwin1.dll,我可能会以某种方式使用它的isatty(),但我无法链接,因为我的许多用户没有(也不会)安装Cygwin。 (而且即使有可能,我也完全不相信它会工作。)

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

您可以尝试LoadLibrary("cygwin1.dll")。如果失败,那么它不会在cygwin下运行,你可以使用本机Windows函数来检测控制台类型。

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

编辑:加载cygwin DLL并调用函数并不容易,你需要先初始化lib,如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例程。如果未调用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;
}