2017-01-12 513 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); 
    } 
}