Console.Out 输出显示在输出窗口中,需要在 AllocConsole() 中

问题描述 投票:0回答:2

好吧,当需要 Winform 时,我一直使用

AllocConsole()
方法进行控制台输出,因为在写入控制台时我使用了多种颜色。

使用VS 2015及以下版本,调试模式下的AllocConsole始终正常工作,

Console.WriteLine
可以正确写入。现在使用 VS 2017,当 AllocConsole 被调用时,控制台会显示,但是,console.WriteLine 输出不是转到该控制台,而是转到 Visual Studio 的输出窗口。

我更喜欢使用 AllocConsole 而不是输出窗口,因为我严重依赖颜色。我已经做了很多关于如何解决这个问题的搜索,但我似乎找不到答案。

c# console output visual-studio-2017
2个回答
6
投票

AllocConsole()
本身不起作用,因为 VS 2017 做了一些“调试标准输出重定向魔法”。要解决此问题,您需要使用
AllocConsole()
创建一个控制台并修复标准输出句柄。

这是我找到的截图:

[DllImport("kernel32.dll",
    EntryPoint = "AllocConsole",
    SetLastError = true,
    CharSet = CharSet.Auto,
    CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
    string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    uint lpSecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    uint hTemplateFile);

private const int MY_CODE_PAGE = 437;
private const uint GENERIC_WRITE = 0x40000000;
private const uint FILE_SHARE_WRITE = 0x2;
private const uint OPEN_EXISTING = 0x3;

public static void CreateConsole()
{
    AllocConsole();

    IntPtr stdHandle = CreateFile(
        "CONOUT$",
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        0, OPEN_EXISTING, 0, 0
    );

    SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
    FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
    Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
    StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
    standardOutput.AutoFlush = true;
    Console.SetOut(standardOutput);

    Console.Write("This will show up in the Console window.");
}

特别感谢 Ramkumar Ramesh 的解决方法: VS2017 中控制台输出消失了


3
投票

基于 wischi 的答案,如果您希望静态 Console 类的属性正常工作,例如 Console.ForegroundColor,则将desiredAccess 设置为 GENERIC_READ | 非常重要。 GENERIC_WRITE。我认为原因是 Console 在内部使用 GetConsoleScreenBufferInfo,当我尝试在 CreateFile 返回的句柄上使用该方法(仅使用 GENERIC_WRITE)时,这给了我一个 ACCESS_DENIED 错误。

    [DllImport("kernel32.dll")]
private static extern bool AllocConsole();

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(string lpFileName
    , [MarshalAs(UnmanagedType.U4)] DesiredAccess dwDesiredAccess
    , [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode
    , uint lpSecurityAttributes
    , [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition
    , [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes
    , uint hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetStdHandle(StdHandle nStdHandle, IntPtr hHandle);

private enum StdHandle : int
{
    Input = -10,
    Output = -11,
    Error = -12
}

[Flags]
enum DesiredAccess : uint
{
    GenericRead = 0x80000000,
    GenericWrite = 0x40000000,
    GenericExecute = 0x20000000,
    GenericAll = 0x10000000
}

public static void CreateConsole()
{
    if (AllocConsole())
    {
        //https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html
        // Console.OpenStandardOutput eventually calls into GetStdHandle. As per MSDN documentation of GetStdHandle: http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx will return the redirected handle and not the allocated console:
        // "The standard handles of a process may be redirected by a call to  SetStdHandle, in which case  GetStdHandle returns the redirected handle. If the standard handles have been redirected, you can specify the CONIN$ value in a call to the CreateFile function to get a handle to a console's input buffer. Similarly, you can specify the CONOUT$ value to get a handle to a console's active screen buffer."
        // Get the handle to CONOUT$.    
        var stdOutHandle = CreateFile("CONOUT$", DesiredAccess.GenericRead | DesiredAccess.GenericWrite, FileShare.ReadWrite, 0, FileMode.Open, FileAttributes.Normal, 0);

        if (stdOutHandle == new IntPtr(-1))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        if (!SetStdHandle(StdHandle.Output, stdOutHandle))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        var standardOutput = new StreamWriter(Console.OpenStandardOutput());
        standardOutput.AutoFlush = true;
        Console.SetOut(standardOutput);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.