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

3

好的,当需要同时使用Winform并在控制台输出时,我一直使用AllocConsole()方法进行控制台输出,因为我在写入控制台时使用了各种颜色。

在使用VS 2015及以下版本时,在Debug模式下,AllocConsole总是能正常工作,Console.WriteLine也能正常将内容输出到控制台。但是现在使用VS 2017,调用AllocConsole后,控制台显示出来了,但是控制台输出的内容却被发送到了Visual Studio的输出窗口。

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


1
由于VS2017仍处于RC阶段,我建议直接向他们提问 - Scott Chamberlain
可能是重复的... 这是您可以在项目中设置的内容。请查看 Chaz 的答案 如何在表单中显示控制台输出/窗口 - JohnG
@JohnG 谢谢!这是一个更简单的方法,可以完全满足需求。 - apotter96
有相同的问题,有任何更新吗? - Ashkan Nourzadeh
@Ashkan,JohnQ提供的链接已经足够了。我把应用程序改成了控制台应用程序,然后在不需要它时隐藏了控制台。非常好用。 - apotter96
2个回答

6

AllocConsole() 不能单独使用,因为VS 2017会进行一些“debug stdout redirect magic”的操作。要解决这个问题,需要使用 AllocConsole() 创建控制台并修复stdout句柄。

以下是我找到的代码片段:

[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 方法,当我尝试使用仅具有 GENERIC_WRITE 的 CreateFile 返回的句柄来调用该方法时,会出现 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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接