3

Ok, I've always used the AllocConsole() method for console output when it comes to needing a Winform as well, because I use a wide variety of color when it comes to writing to console.

Using VS 2015 and below, AllocConsole in Debug mode always worked properly, Console.WriteLine wrote to it properly. Now using VS 2017, The Console Shows when AllocConsole is called, however, instead of console.WriteLine outputs going to that console, it is going to the Output window of Visual Studio.

I prefer to use the AllocConsole rather than the Output window because I rely heavily on color. I've done plenty of searching on how to fix this, but I can't seem to find an answer.

apotter96
  • 156
  • 12
  • 1
    Being that VS2017 is still in the RC phase, [I would ask them directly](https://learn.microsoft.com/en-us/visualstudio/ide/how-to-report-a-problem-with-visual-studio-2017) – Scott Chamberlain Jan 12 '17 at 22:30
  • Possible duplicate… This is something you can set in the project. Check out Chaz’s answer [How do I show a console output/window in a forms](http://stackoverflow.com/questions/4362111/how-do-i-show-a-console-output-window-in-a-forms-application) – JohnG Jan 12 '17 at 22:41
  • @JohnG Thank you! A much more simple way to do exactly what was needed. – apotter96 Jan 12 '17 at 22:46
  • having the same problem, any updates? – Ashkan Nourzadeh Mar 22 '17 at 13:54
  • @Ashkan the link provided by JohnQ was sufficient. I changed the app to a console app, and then hid the console when I didn't need it. Works like a charm. – apotter96 Mar 27 '17 at 03:55

2 Answers2

6

AllocConsole() does not work on it's own, because VS 2017 does some "debug stdout redirect magic". To fix this you need to create a console with AllocConsole() and fix the stdout handle.

Here is the snipped I found:

[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.");
}

Special thanks to Ramkumar Ramesh for the work-around: Console Output is gone in VS2017

wischi
  • 666
  • 10
  • 34
3

Building on the answer from wischi, if you want properties for the static Console class to work correctly, for instance Console.ForegroundColor it is important to set the desiredAccess to GENERIC_READ | GENERIC_WRITE. I think the reason for this is that Console is using GetConsoleScreenBufferInfo internally and when I tried to use that method on the handle returned by CreateFile with only GENERIC_WRITE that gave me an ACCESS_DENIED error.

    [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);
    }
}