2017-01-12 525 views
2

好吧,我一直使用AllocConsole()控制台输出的方法,当涉及到需要Winform时,因为我使用各种各样的颜色时它涉及到写入控制台。Console.Out输出显示在输出窗口,需要AllocConsole()

使用VS 2015及以下版本,调试模式下的AllocConsole始终正常工作,Console.WriteLine正确写入。现在使用VS 2017,控制台显示何时调用AllocConsole,但是,而不是console.WriteLine输出到该控制台,它将进入Visual Studio的输出窗口。

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

+1

由于VS2017仍处于RC阶段,[我会直接询问](https://docs.microsoft.com/en-us/visualstudio/ide/how-to-report-a-问题与视觉工作室2017) –

+0

可能重复...这是你可以在项目中设置的东西。查看Chaz的答案[如何在窗体中显示控制台输出/窗口](http://stackoverflow.com/questions/4362111/how-do-i-show-a-console-output-window-in-a -forms-application) – JohnG

+0

@JohnG谢谢!一种更简单的方法来完成所需的工作。 – apotter96

回答

2

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拉梅什的变通办法: Console Output is gone in VS2017

1

大厦从wischi答案,如果你想要的属性静态控制台类才能正常工作,例如Console.ForegroundColor,将所需的访问权限设置为GENERIC_READ | GENERIC_WRITE。我认为这是因为控制台在内部使用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); 
    } 
}