如何从控制台应用程序(如Far)接收颜色输出?

时间:2016-03-07 14:15:36

标签: c# winapi c#-4.0 console

我有一些代码:

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的例子,因为我认为我的问题可以用它来解决。 如果有人能帮助我,我将不胜感激。

P.S。我很抱歉英语不好。

2 个答案:

答案 0 :(得分:3)

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

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

但是如果您调用的进程没有转储ANSI序列,(cmd不执行此操作),您将不会收到任何颜色信息。

答案 1 :(得分:-1)

使用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;
                    }
                }