【问题标题】:How can I recieve color output from console application (like Far)?如何从控制台应用程序(如 Far)接收颜色输出?
【发布时间】:2016-03-07 14:15:36
【问题描述】:

我有一些代码:

cmdProcess = new Process();
var procStartInfo = new ProcessStartInfo( "cmd", "/k "C:\\Program Files (x86)\\Far Manager\\Far.exe"" );

procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardInput = true;
procStartInfo.UseShellExecute = false;

procStartInfo.CreateNoWindow = true;
cmdProcess.OutputDataReceived += ( s, e ) => {
       callbackFn(e.Data + "\n");
};
cmdProcess.StartInfo = procStartInfo;

cmdProcess.Start();
cmdProcess.BeginOutputReadLine();

但是使用此代码,我只能启动该过程并获得一些东西,但不能完全和没有颜色。我还尝试了ReceiveConsoleOutput 函数,但我只收到空白缓冲区。 使用 WinAPI,我只能启动控制台,而不能启动其他任何东西 - 我不太了解。但我并不反对 WinAPI 示例,因为我认为我的问题可以用它来解决。 如果有人可以帮助我,我将不胜感激。

附:我很抱歉英语不好。

【问题讨论】:

  • 我怀疑你能读懂输出的颜色
  • 在这些项目中,有某种可能:linklink
  • 我没有看到任何证据表明这些项目创建了子流程,然后捕获输出的每个字符的颜色。无论如何,如果我错了,那么我想你可以从那些项目中读取代码。
  • @DavidHeffernan 我还看到Python example 可以做到这一点,但是这个代码不太容易理解。在那个 Python 示例中也使用了 WinAPI。
  • @DavidHeffernan ConEmu 和控制台项目有很多关于 C++ 的代码,我无法理解。如果我能理解,我不会问。

标签: c# winapi c#-4.0 console


【解决方案1】:

您谈论的是两件不同的事情。 ConEmu 和原始控制台支持颜色,但这是通过console buffer API 实现的(here 是一个完整的 C# 库)。控制台不仅支持着色,还支持光标和鼠标;但是,它们都与标准输出无关。

但是如果你想在标准输出中接收颜色信息,你可以使用ANSI escape sequences,它是终端通信中的标准(ANSI graphics art也是这样),支持着色和光标定位很好,可以编码为字符流。

但如果您调用的进程不转储 ANSI 序列,(cmd 不会这样做)您将不会收到任何颜色信息。

【讨论】:

  • 那么,你能解释一下如何在 C# 中使用控制台缓冲区 API 吗?或者请给我一些带有工作教程/示例的链接?
  • 我已经编辑了答案,添加了指向 C# 库的链接。
  • 这个库用于创建比 C# 中的标准更底层但不是数据的控制台应用程序已经创建了一个控制台进程,但仍然感谢您的回答!
【解决方案2】:

ReadConsoleOutput 代码无法正常工作,因为它不是 FreeConsole 函数被调用 - 以免除其自己的控制台应用程序

Proof

        public static IEnumerable<string> ReadFromBuffer(IntPtr hOutput, short x, short y, short width, short height)
        {
        IntPtr buffer = Marshal.AllocHGlobal(width * height * Marshal.SizeOf(typeof(CHAR_INFO)));
        if (buffer == null)
            throw new OutOfMemoryException();

        try
        {
            COORD coord = new COORD();
            SMALL_RECT rc = new SMALL_RECT();
            rc.Left = x;
            rc.Top = y;
            rc.Right = (short)(x + width - 1);
            rc.Bottom = (short)(y + height - 1);

            COORD size = new COORD();
            size.X = width;
            size.Y = height;

            if (!ReadConsoleOutput(hOutput, buffer, size, coord, ref rc))
            {
                // 'Not enough storage is available to process this command' may be raised for buffer size > 64K (see ReadConsoleOutput doc.)
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }

            IntPtr ptr = buffer;
            for (int h = 0; h < height; h++)
            {
                StringBuilder sb = new StringBuilder();
                for (int w = 0; w < width; w++)
                {
                    CHAR_INFO ci = (CHAR_INFO)Marshal.PtrToStructure(ptr, typeof(CHAR_INFO));
                    char[] chars = Console.OutputEncoding.GetChars(ci.charData);
                    sb.Append(chars[0]);
                    ptr += Marshal.SizeOf(typeof(CHAR_INFO));
                }
                yield return sb.ToString();
            }
        }
        finally
        {
            Marshal.FreeHGlobal(buffer);
        }
    }

...

        Process proc = new Process();
        proc.StartInfo.FileName = "cmd.exe";
        proc.StartInfo.Arguments = command; 
        //proc.StartInfo.UseShellExecute = false;
        proc.Start();

        Thread.Sleep(1000);

        bool resultFree = ConsoleApi.FreeConsole();

        if (resultFree)
        {
            Debug.WriteLine("FreeConsole: {0}", true);
        }
        else
        {
            Debug.WriteLine("FreeConsole: {0}", false);
        }

        Debug.WriteLine("Process ID: {0}", Convert.ToUInt32(proc.Id));

        bool result = ConsoleApi.AttachConsole( Convert.ToUInt32(proc.Id) );

        Debug.WriteLine("AttachConsole: {0}", result);

        IntPtr _consoleH = ConsoleApi.GetStdHandle(ConsoleApi.STD_OUTPUT_HANDLE);

        ConsoleApi.CONSOLE_SCREEN_BUFFER_INFO _bufferInfo;

        bool getInfo = ConsoleApi.GetConsoleScreenBufferInfo(_consoleH, out _bufferInfo);

        if (getInfo)
        {
            Debug.WriteLine("GetConsoleScreenBufferInfo: {0}x{1}", _bufferInfo.dwSize.X, _bufferInfo.dwSize.Y);
        }
        else
        {
            Debug.WriteLine("GetConsoleScreenBufferInfo: {0}", false);
        }

        short _widthConsole = _bufferInfo.dwSize.X;
        short _heightConsole = _bufferInfo.dwSize.Y;

        IEnumerable<string> rows = ConsoleApi.ReadFromBuffer(_consoleH, 0, 0, _widthConsole,_heightConsole);

        foreach (string row in rows)
        {
            Debug.WriteLine(row);
        }

读取颜色:

                    [DllImport("kernel32.dll")]
                    public static extern bool ReadConsoleOutputAttribute(IntPtr hConsoleOutput,
       [Out] ushort[] lpAttribute, uint nLength, COORD dwReadCoord,
       out uint lpNumberOfAttrsRead);

...

                    string[] colors = new string[]{
                        "black",
                        "darkblue",
                        "darkgreen",
                        "darkcyan",
                        "darkred", 
                        "darkmagenta",
                        "brown",
                        "white",
                        "lightgrey",
                        "blue",
                        "green",
                        "cyan",
                        "red",
                        "magenta",
                        "yellow",
                        "white"
                    };

                for (int i = 0; i < _rowsList.Length; i++)
                {

                    ushort[] lpAttr = new ushort[_widthConsole];

                    ConsoleApi.COORD _coordReadAttr = new ConsoleApi.COORD(0,(short)i);

                    uint lpReadALast;

                    bool readAttr = ConsoleApi.ReadConsoleOutputAttribute(_consoleH, lpAttr, Convert.ToUInt32(_widthConsole), _coordReadAttr, out lpReadALast);

                    string[] attrText = new string[_widthConsole];

                    for (int _attr = 0; _attr < lpAttr.Length; _attr++)
                    {
                        string _text = colors[lpAttr[_attr] & 0x0F];
                        string _background = colors[((lpAttr[_attr] & 0xF0) >> 4) & 0x0F];

                        attrText[_attr] = _text + "|" + _background;
                    }
                }

【讨论】:

  • 背景颜色和文字可以使用这个方法获取:ReadConsoleOutputAttribute
  • 这是否回答了您提出的问题?
  • @David Heffernan 是的
  • 真的。你在哪里读取外部进程输出的文本颜色?
  • 你能把整个代码贴在 Github gist 上吗?将不胜感激。
猜你喜欢
  • 1970-01-01
  • 2013-03-03
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-10
  • 1970-01-01
相关资源
最近更新 更多