I do this in my C# window programs (WInforms or WPF) using a Win32 console window.   I have a small class that wraps some basic Win32 APIs, thin I create a console when the program begins.  This is just an example: in 'real life' you'd use a setting or some other thing to only enable the console when you needed it.
using System;
using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using MWin32Api;
namespace WFConsole
{
    static class Program
    {
        static private SafeFileHandle ConsoleHandle;
    /// <summary>
    /// Initialize the Win32 console for this process.
    /// </summary>
    static private void InitWin32Console()
    {
        if ( !K32.AllocConsole() ) {
            MessageBox.Show( "Cannot allocate console",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        IntPtr handle = K32.CreateFile(
                             "CONOUT$",                                    // name
                             K32.GENERIC_WRITE | K32.GENERIC_READ,         // desired access
                             K32.FILE_SHARE_WRITE | K32.FILE_SHARE_READ,   // share access
                             null,                                         // no security attributes
                             K32.OPEN_EXISTING,                            // device already exists
                             0,                                            // no flags or attributes
                             IntPtr.Zero );                                // no template file.
        ConsoleHandle = new SafeFileHandle( handle, true );
        if ( ConsoleHandle.IsInvalid ) {
            MessageBox.Show( "Cannot create diagnostic console",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        //
        // Set the console screen buffer and window to a reasonable size
        //  1) set the screen buffer sizse
        //  2) Get the maximum window size (in terms of characters) 
        //  3) set the window to be this size
        //
        const UInt16 conWidth     = 256;
        const UInt16 conHeight    = 5000;
        K32.Coord dwSize = new K32.Coord( conWidth, conHeight );
        if ( !K32.SetConsoleScreenBufferSize( ConsoleHandle.DangerousGetHandle(), dwSize ) ) {
            MessageBox.Show( "Can't get console screen buffer information.",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        K32.Console_Screen_Buffer_Info SBInfo = new K32.Console_Screen_Buffer_Info();
        if ( !K32.GetConsoleScreenBufferInfo( ConsoleHandle.DangerousGetHandle(), out SBInfo ) ) {
            MessageBox.Show( "Can't get console screen buffer information.",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Exclamation);
            return;
        }
        K32.Small_Rect sr; ;
        sr.Left = 0;
        sr.Top = 0;
        sr.Right = 132 - 1;
        sr.Bottom = 51 - 1;
        if ( !K32.SetConsoleWindowInfo( ConsoleHandle.DangerousGetHandle(), true, ref sr ) ) {
            MessageBox.Show( "Can't set console screen buffer information.",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        IntPtr conHWND = K32.GetConsoleWindow();
        if ( conHWND == IntPtr.Zero ) {
            MessageBox.Show( "Can't get console window handle.",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        if ( !U32.SetForegroundWindow( conHWND ) ) {
            MessageBox.Show( "Can't set console window as foreground.",
                             "Error",
                             MessageBoxButtons.OK,
                             MessageBoxIcon.Error );
            return;
        }
        K32.SetConsoleTitle( "Test - Console" );
        Trace.Listeners.Add( new ConsoleTraceListener() );
    }
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        InitWin32Console();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault( false );
        Application.Run( new Main() );
    }
}
}
using System;
using System.Runtime.InteropServices;
namespace MWin32Api
{
    #region Kernel32 Functions
//--------------------------------------------------------------------------
/// <summary>
/// Functions in Kernel32.dll
/// </summary>
public sealed class K32
{
    #region Data Structures, Types and Constants
    //----------------------------------------------------------------------
    // Data Structures, Types and Constants
    // 
    [StructLayout( LayoutKind.Sequential )]
    public class SecurityAttributes
    {
        public UInt32  nLength;
        public UIntPtr lpSecurityDescriptor;
        public bool    bInheritHandle;
    }
    [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 4 )]
    public struct Coord
    {
        public Coord( UInt16 tx, UInt16 ty )
        {
            x = tx;
            y = ty;
        }
        public UInt16 x;
        public UInt16 y;
    }
    [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 8 )]
    public struct Small_Rect
    {
        public Int16 Left;
        public Int16 Top;
        public Int16 Right;
        public Int16 Bottom;
        public Small_Rect( short tLeft, short tTop, short tRight, short tBottom )
        {
            Left = tLeft;
            Top = tTop;
            Right = tRight;
            Bottom = tBottom;
        }
    }
    [StructLayout( LayoutKind.Sequential, Pack = 1, Size = 24 )]
    public struct Console_Screen_Buffer_Info
    {
        public Coord      dwSize;
        public Coord      dwCursorPosition;
        public UInt32     wAttributes;
        public Small_Rect srWindow;
        public Coord      dwMaximumWindowSize;
    }
    public const int ZERO_HANDLE_VALUE = 0;
    public const int INVALID_HANDLE_VALUE = -1;
    #endregion
    #region Console Functions
    //----------------------------------------------------------------------
    // Console Functions
    // 
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern bool AllocConsole();
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern bool SetConsoleScreenBufferSize(
        IntPtr hConsoleOutput,
        Coord dwSize );
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern bool GetConsoleScreenBufferInfo(
        IntPtr hConsoleOutput,
        out Console_Screen_Buffer_Info lpConsoleScreenBufferInfo );
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern bool SetConsoleWindowInfo(
        IntPtr hConsoleOutput,
        bool bAbsolute,
        ref Small_Rect lpConsoleWindow );
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern IntPtr GetConsoleWindow();
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern bool SetConsoleTitle(
        string Filename );
    #endregion
    #region Create File
    //----------------------------------------------------------------------
    // Create File
    // 
    public const UInt32 CREATE_NEW          = 1;
    public const UInt32 CREATE_ALWAYS       = 2;
    public const UInt32 OPEN_EXISTING       = 3;
    public const UInt32 OPEN_ALWAYS         = 4;
    public const UInt32 TRUNCATE_EXISTING   = 5;
    public const UInt32 FILE_SHARE_READ     = 1;
    public const UInt32 FILE_SHARE_WRITE    = 2;
    public const UInt32 GENERIC_WRITE       = 0x40000000;
    public const UInt32 GENERIC_READ        = 0x80000000;
    [DllImport( "kernel32.dll", SetLastError = true )]
    public static extern IntPtr CreateFile(
        string Filename,
        UInt32 DesiredAccess,
        UInt32 ShareMode,
        SecurityAttributes SecAttr,
        UInt32 CreationDisposition,
        UInt32 FlagsAndAttributes,
        IntPtr TemplateFile );
    #endregion
    #region Win32 Miscelaneous
    //----------------------------------------------------------------------
    // Miscelaneous
    // 
    [DllImport( "kernel32.dll" )]
    public static extern bool CloseHandle( UIntPtr handle );
    #endregion
    //----------------------------------------------------------------------
    private K32()
    {
    }
}
#endregion
//--------------------------------------------------------------------------
/// <summary>
/// Functions in User32.dll
/// </summary>
#region User32 Functions
public sealed class U32
{
    [StructLayout( LayoutKind.Sequential )]
    public struct Rect
    {
        public Int32 Left;
        public Int32 Top;
        public Int32 Right;
        public Int32 Bottom;
        public Rect( short tLeft, short tTop, short tRight, short tBottom )
        {
            Left = tLeft;
            Top = tTop;
            Right = tRight;
            Bottom = tBottom;
        }
    }
    [DllImport( "user32.dll" )]
    public static extern bool GetWindowRect(
        IntPtr hWnd,
        [In][MarshalAs( UnmanagedType.LPStruct )]Rect lpRect );
    [DllImport( "user32.dll", SetLastError = true )]
    public static extern bool SetForegroundWindow(
        IntPtr hWnd );
    //----------------------------------------------------------------------
    private U32()
    {
    }
} // U32 class
#endregion
} // MWin32Api namespace