I have a c# .net 3.5 application that writes text to the console using a StreamWriter. Is there a way I can add text decorations like underline and strikethrough to the text that is printed to the console? Possibly using ANSI escape sequences?
TextWriter writer = new StreamWriter(Console.OpenStandardOutput());
writer.WriteLine("some underlined text");
Thanks,
PaulH
In Windows 10 build 16257 and later:
using System;
using System.Runtime.InteropServices;
class Program
{
const int STD_OUTPUT_HANDLE = -11;
const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
static void Main()
{
var handle = GetStdHandle(STD_OUTPUT_HANDLE);
uint mode;
GetConsoleMode(handle, out mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(handle, mode);
const string UNDERLINE = "\x1B[4m";
const string RESET = "\x1B[0m";
Console.WriteLine("Some " + UNDERLINE + "underlined" + RESET + " text");
}
}
The Windows console does not support ANSI escape sequences. To my knowledge, the only way to change the attributes of an output character is to call SetConsoleTextAttribute before writing the character. Or, in .NET, modify the Console.ForegroundColor or Console.BackgroundColor attributes.
It might be possible to set those properties to custom values (i.e. values not defined by ConsoleColor) with a type cast. But I don't know what good that would do you.
I don't know that I've ever seen strikethrough text on a Windows console, and it's been years since I saw underline. I suppose it's possible, but I don't know how it's done.
I use this code. It's a fixed version of Vladimir Reshetnikov's answer, using the correct escape code for the reset.
private static void WriteUnderline(string s)
{
var handle = GetStdHandle(STD_OUTPUT_HANDLE);
uint mode;
GetConsoleMode(handle, out mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(handle, mode);
Console.WriteLine($"\x1B[4m{s}\x1B[24m");
}
This will do underlined text, and has the benefit of not resetting any colors you have set.
Short answer, no; the console doesn't allow the use of underlined characters in output.
Longer answer: The screen buffer used by the console is little more than a byte array. Each cursor position is one byte or one character. To create an underline, you either need two characters overlapping (which isn't possible in the console), or you need access to a codepage that uses the upper 128 character values as underlined or strikethrough versions of the lower 128 (I don't know of one).
You can work around this if you are willing to go "double-spaced" for lines that have underlines. Character code 0x00AF (decimal 175) is a "text art" character representing a border across the top of the character space. If you use those in the line underneath your text, presto, underlines.
I found this question and thought I would add to the answers for pre-windows 10 terminal using kernel32 functions,
using System;
using System.Runtime.InteropServices;
namespace color_console
{
class Class1
{
static void Main(string[] args)
{
Class1 c = new Class1();
c.change();
}
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, CharacterAttributes wAttributes);
[DllImport("kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput,
ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
void change()
{
const int STD_OUTPUT_HANDLE = -11;
IntPtr hOut;
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(hOut, ref ConsoleInfo);
CharacterAttributes originalAttributes = (CharacterAttributes)ConsoleInfo.wAttributes;
//write some text
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE);
Console.WriteLine("Blue text");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_RED);
Console.WriteLine("Red background");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN);
Console.WriteLine("Green background");
SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_GREEN | CharacterAttributes.BACKGROUND_RED);
Console.WriteLine("Yellow background");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED | CharacterAttributes.COMMON_LVB_UNDERSCORE);
Console.WriteLine("Red underlined text");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
| CharacterAttributes.FOREGROUND_BLUE);
Console.WriteLine("Purple text");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_RED
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.FOREGROUND_INTENSITY);
Console.WriteLine("Purple text intense");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.COMMON_LVB_REVERSE_VIDEO);
Console.WriteLine("Aqua reversed text ");
SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_GREEN
| CharacterAttributes.FOREGROUND_BLUE
| CharacterAttributes.COMMON_LVB_REVERSE_VIDEO
| CharacterAttributes.FOREGROUND_INTENSITY);
Console.WriteLine("Aqua reversed intense text ");
SetConsoleTextAttribute(hOut, CharacterAttributes.COMMON_LVB_GRID_LVERTICAL
| CharacterAttributes.FOREGROUND_GREEN);
Console.WriteLine("What does this do");
SetConsoleTextAttribute(hOut, originalAttributes);
Console.WriteLine("Back to the shire");
}
public enum CharacterAttributes
{
FOREGROUND_BLUE = 0x0001,
FOREGROUND_GREEN = 0x0002,
FOREGROUND_RED = 0x0004,
FOREGROUND_INTENSITY = 0x0008,
BACKGROUND_BLUE = 0x0010,
BACKGROUND_GREEN = 0x0020,
BACKGROUND_RED = 0x0040,
BACKGROUND_INTENSITY = 0x0080,
COMMON_LVB_LEADING_BYTE = 0x0100,
COMMON_LVB_TRAILING_BYTE = 0x0200,
COMMON_LVB_GRID_HORIZONTAL = 0x0400,
COMMON_LVB_GRID_LVERTICAL = 0x0800,
COMMON_LVB_GRID_RVERTICAL = 0x1000,
COMMON_LVB_REVERSE_VIDEO = 0x4000,
COMMON_LVB_UNDERSCORE = 0x8000
}
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
public COORD dwSize;
public COORD dwCursorPosition;
public int wAttributes;
public SMALL_RECT srWindow;
public COORD dwMaximumWindowSize;
}
// Standard structures used for interop with kernel32
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
public short x;
public short y;
}
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
}
Output on my PC
It's pretty easy to change the foreground/background color of console : http://www.dotnetperls.com/console-color but AFAIK there is no way to put some bold text, for example. But I didn't really tried to achieve that so i'm not sure.
Related
Using c# .net 4.7.1, I'm trying to make a console app Blackjack game and I'm having trouble displaying the card suits to the console output with different versions of Windows. For Windows 7, this is the Main method that displays the suits correctly:
static void Main(string[] args)
{
string[] Suits = new string[] { "♠", "♣", "♥", "♦" };
Methods.Print(Suits[0] + " " + Suits[1] + " " + Suits[2] + " " + Suits[3]);
Console.ReadLine();
....
}
the suits display as I want them, like this:
But if I run my program with this Main method in it on my Windows 10 machine they display like this:
I've found that if I include this line in my Main method on my Windows 10 machine then the suits display as I want them to:
Console.OutputEncoding = System.Text.Encoding.UTF8;
But then that makes it so the suits don't display correctly on my Windows 7 machine. Can anyone help me out with how I can have these card suits display properly regardless of what Windows OS the program is run on? Thanks in advance.
If you want it to work reliably in the console then here is my solution:
static void Main(string[] args)
{
Console.WriteLine("D, C, H, S");
Console.ReadLine();
}
Here are 2 other options:
Check windows versions and test all scenarios using ♠, ♣, ♥, ♦ and Encoding.UTF8;
Gui application.
Windows 7
The problem with Windows7 console and
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("♠, ♣, ♥, ♦");
Console.ReadLine();
is most likely the font in console app.
From Console.OutputEncoding Property:
Note that successfully displaying Unicode characters to the console requires the following:
The console must use a TrueType font, such as Lucida Console or Consolas, to display characters.
You can change the font in the Console app's properties:
Thanks to #tymtam for the insight on the issue I'm having. I looked into the possibility of changing the console font as a solution. I found this article that shows how to programmatically change the console font to Lucida Console which is a true type font. Here's my formatted code from that link:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
namespace BlackJack
{
class BlackJack
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCurrentConsoleFontEx(IntPtr consoleOutput, bool maximumWindow, ref CONSOLE_FONT_INFO_EX consoleCurrentFontEx);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int dwType);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int SetConsoleFont(IntPtr hOut, uint dwFontNum);
private const int STD_OUTPUT_HANDLE = -11;
private const int TMPF_TRUETYPE = 4;
private const int LF_FACESIZE = 32;
private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal unsafe struct CONSOLE_FONT_INFO_EX
{
internal uint cbSize;
internal uint nFont;
internal COORD dwFontSize;
internal int FontFamily;
internal int FontWeight;
internal fixed char FaceName[LF_FACESIZE];
}
[StructLayout(LayoutKind.Sequential)]
internal struct COORD
{
internal short X;
internal short Y;
internal COORD(short x, short y)
{
X = x;
Y = y;
}
}
public static void SetConsoleFont(string fontName = "Lucida Console")
{
unsafe
{
IntPtr hnd = GetStdHandle(STD_OUTPUT_HANDLE);
if (hnd != INVALID_HANDLE_VALUE)
{
CONSOLE_FONT_INFO_EX info = new CONSOLE_FONT_INFO_EX();
info.cbSize = (uint)Marshal.SizeOf(info);
// Set console font to Lucida Console.
CONSOLE_FONT_INFO_EX newInfo = new CONSOLE_FONT_INFO_EX();
newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
newInfo.FontFamily = TMPF_TRUETYPE;
IntPtr ptr = new IntPtr(newInfo.FaceName);
Marshal.Copy(fontName.ToCharArray(), 0, ptr, fontName.Length);
// Get some settings from current font.
newInfo.dwFontSize = new COORD(info.dwFontSize.X, info.dwFontSize.Y);
newInfo.FontWeight = info.FontWeight;
SetCurrentConsoleFontEx(hnd, false, ref newInfo);
}
}
}
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
SetConsoleFont();
....
}
Two things to note
I had to add this using statement for it to work:
using System.Runtime.InteropServices;
I had to check the Allow unsafe code checkbox located in the Project>Properties>Build screen as shown below:
After making these changes the program runs on both Windows 7 and Windows 10 and it displays the card suits as I want them to. Like I said before, I don't have access to machines that have other versions of Windows on them, so I can only say that this runs on Windows 7 and Windows 10 for sure.
I can't get this to work for font names that are 16 characters or longer, but the console itself obviously doesn't have this limitation. Does anyone know a programmatic way to set the font that will work with the built-in "Lucida Sans Typewriter" or the open source "Fira Code Retina"?
This following code works:
I have copied PInvoke code from various places, particularly the PowerShell console host, and the Microsoft Docs
Note that the relevant docs for CONSOLE_FONT_INFOEX and SetCurrentConsoleFontEx do not talk about this, and the struct defines the font face as a WCHAR field of size 32...
Also note what's not a limitation, but is a restriction from the console dialog, is that the font has to have True Type outlines, and must be genuinely fixed width. Using this API you can chose variable width fonts like "Times New Roman" ...
However, in the API, it has to have less than 16 characters in the name -- which is a restriction the console itself doesn't have, and may be a bug in the API and not my code below 🤔
using System;
using System.Runtime.InteropServices;
public static class ConsoleHelper
{
private const int FixedWidthTrueType = 54;
private const int StandardOutputHandle = -11;
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr GetStdHandle(int nStdHandle);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool GetCurrentConsoleFontEx(IntPtr hConsoleOutput, bool MaximumWindow, ref FontInfo ConsoleCurrentFontEx);
private static readonly IntPtr ConsoleOutputHandle = GetStdHandle(StandardOutputHandle);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FontInfo
{
internal int cbSize;
internal int FontIndex;
internal short FontWidth;
public short FontSize;
public int FontFamily;
public int FontWeight;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
//[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.wc, SizeConst = 32)]
public string FontName;
}
public static FontInfo[] SetCurrentFont(string font, short fontSize = 0)
{
Console.WriteLine("Set Current Font: " + font);
FontInfo before = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>()
};
if (GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref before))
{
FontInfo set = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>(),
FontIndex = 0,
FontFamily = FixedWidthTrueType,
FontName = font,
FontWeight = 400,
FontSize = fontSize > 0 ? fontSize : before.FontSize
};
// Get some settings from current font.
if (!SetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref set))
{
var ex = Marshal.GetLastWin32Error();
Console.WriteLine("Set error " + ex);
throw new System.ComponentModel.Win32Exception(ex);
}
FontInfo after = new FontInfo
{
cbSize = Marshal.SizeOf<FontInfo>()
};
GetCurrentConsoleFontEx(ConsoleOutputHandle, false, ref after);
return new[] { before, set, after };
}
else
{
var er = Marshal.GetLastWin32Error();
Console.WriteLine("Get error " + er);
throw new System.ComponentModel.Win32Exception(er);
}
}
}
You can play with it in a PowerShell window by using Add-Type with that code, and then doing something like this:
[ConsoleHelper]::SetCurrentFont("Consolas", 16)
[ConsoleHelper]::SetCurrentFont("Lucida Console", 12)
Then, use your console "Properties" dialog and manually switch to Lucida Sans Typewriter ... and try just changing the font size, specifying the same font name:
[ConsoleHelper]::SetCurrentFont("Lucida Sans Typewriter", 12)
And you'll get output like this (showing the three settings: before, what we tried, and what we got):
Set Current Font: Lucida Sans Typewriter
FontSize FontFamily FontWeight FontName
-------- ---------- ---------- --------
14 54 400 Lucida Sans Typeʈ
12 54 400 Lucida Sans Typewriter
12 54 400 Courier New
You see that weird character on the end of the "before" value? That's happens whenever the font is longer than 16 characters (I'm getting garbage data because of a problem in the API or the marshalling).
The actual console font name is obviously not length limited, but perhaps it's not possible to use a font with a name that's 16 characters or longer?
For what it's worth, I discovered this problem with Fira Code Retina, a font that has exactly 16 characters in the name -- and I have a little bit more code than what's above in a gist here if you care to experiment...
I had found a bug in the Console API. It's fixed as of Windows 10 (insider) build 18267.
Prior to that release, there wasn't a way around it -- except to use fonts with shorter names, or use the actual window properties panel to set it.
The original post code works now ...
This is the signature of my function in DLL:
int __stdcall myFun( void * const context, const char * const pszFileName, const unsigned int buffSize, void * const pWaveFormatex );
All parameters are [in]. The user should pass a pointer to a WAVEFORMATEX struct through the last parameter. Upon return, it will be filled. All that works very well in C++.
Now, I'm trying for days to use the same DLL from C# and it simply doesn't work. The problem is in the last parameter. Since I do not know C# at all, I would like to ask somebody if this is doable at all. If it is, I would appreciate an example.
One of my last attempts was this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
Note: I also built my DLL written in C++ with the Struct Member Alignment = 1. Maybe I'm stupid, but I thought that Pack = 1 above is related with that in C++, but I have no idea if it is...
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int myFun( IntPtr context,
[MarshalAs( UnmanagedType.LPStr )]
string pszFileName,
int bufferSize,
ref IntPtr pWfx );
IntPtr unmanaged_pWfx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WAVEFORMATEX)));
result = DLLWrapper.myFun(context,
"C:\\video.wmv",
176400,
ref unmanaged_pWfx );
WAVEFORMATEX wfxFormat = (WAVEFORMATEX)Marshal.PtrToStructure( unmanaged_pWfx, typeof(WAVEFORMATEX));
The behavior is undefined. Sometimes it hangs, sometimes it terminates... Something is very wrong. However, the problem is not in the DLL, the problem is on the C# side. Is C# capable of working with pointers at all?
Thanks for any feedback you provide.
EDIT (working C++ code):
void * context;
WAVEFORMATEX wfx;
int success = getContext( &context );
success = myFun( context, "C:\\video.wmv", 176400, &wfx );
The equivalent in C# is:
IntPtr context;
WAVEFORMATEX wfx;
int success = getContext( out context );
success = myFun( context, "C:\\video.wmv", 176400, out wfx );
extern "C" __stdcall int getContext( void ** pContext );
[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int getContext(out IntPtr context);
Well, based on the information you have provided I would say that you need to declare the C# function like this:
[DllImport("myLib.dll")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
And call the function like this:
WAVEFORMATEX wfx;
int result = DLLWrapper.myFun(context, #"C:\video.wmv", 176400, out wfx);
There's really no need for manual marshalling of this struct. It's a very simple blittable struct and it is much cleaner to let the framework handle the marshalling.
I am assuming that you are accurate when you state that the final struct parameter does not need to be initialised and its members are filled out by the function.
Packing looks reasonable. I don't think you need to build your DLL in any special way. I hope that you are picking up WAVEFORMATEX from the Windows header files and they already specify packing for that struct.
If you are still stuck then you should show the successful C++ calling code.
Judging from the comments, you still have a bug somewhere in your code. In such a situation, especially when you doubt the interop, it pays to make a simple reproduction to determine whether the interop is the problem, or not. Here is mine:
C++ DLL
#include <Windows.h>
#include <mmsystem.h>
#include <iostream>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
__declspec(dllexport) int __stdcall myFun(void * const context,
const char * const pszFileName, const unsigned int buffSize,
void * const pWaveFormatex)
{
std::cout << context << std::endl
<< pszFileName << std::endl
<< buffSize << std::endl;
WAVEFORMATEX wfx;
wfx.cbSize = 1;
wfx.nAvgBytesPerSec = 2;
wfx.nBlockAlign = 3;
wfx.nChannels = 4;
wfx.nSamplesPerSec = 5;
wfx.wBitsPerSample = 6;
wfx.wFormatTag = 7;
CopyMemory(pWaveFormatex, &wfx, sizeof(wfx));
return 666;
}
C# console app
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication13
{
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
}
[DllImport(#"Win32Project1.dll", EntryPoint = "?myFun##YGHQAXQBDI0#Z")]
public static extern int myFun(
IntPtr context,
string fileName,
uint bufferSize,
out WAVEFORMATEX wfx
);
static void Main(string[] args)
{
WAVEFORMATEX wfx;
int result = myFun((IntPtr)42, #"C:\video.wmv", 176400, out wfx);
Console.WriteLine(result);
Console.WriteLine(wfx.cbSize);
Console.WriteLine(wfx.nAvgBytesPerSec);
Console.WriteLine(wfx.nBlockAlign);
Console.WriteLine(wfx.nChannels);
Console.WriteLine(wfx.nSamplesPerSec);
Console.WriteLine(wfx.wBitsPerSample);
Console.WriteLine(wfx.wFormatTag);
Console.ReadLine();
}
}
}
Output
0000002A
C:\video.wmv
176400
666
1
2
3
4
5
6
7
The problem is passing the IntPtr. You're passing stack allocated variable(the one that holds actual pointer) to your code, but you want to pass the pointer. Just remove the "ref" keyword from your code.
I have a WinForms project, and if the user want's a debug console, I allocate a console with AllocConsole().
All console output works normally with the target architecture set to "Any CPU", but when I change it to "x86" it doesn't output anything (Console.Read() still works as expected). If I open the EXE directly, the output works. It looks like Visual Studio redirects it into it's own "Output" window.
I also tried this answer, but it didn't work, I also tried Console.SetOut(GetStdHandle(-11)), which didn't work either.
Setting the target architecture to 'Any CPU' is no option for me.
So here are my two questions:
Why is this only the case when the target architecture is set to x86?
How can I output to my console when running inside of Visual Studio?
When "Enable native code debugging" is enabled, output from consoles crated with AllocConsole is redirected to the debug output window instead.
The reason this only happens in x86 and not AnyCPU is because you can only debug native code in an x86 application.
Note that this behavior only occurs with consoles created with AllocConsole. A console application's output is not redirected.
EDIT: The other reason for the console not outputting text is when you've written to the console before calling AllocConsole.
Regardless of the reason, this code will restore output if it was redirected, and reopen the console in case it's invalid. It uses the magic number 7 which is what the handle of stdout usually equals to.
using System;
using System.IO;
using System.Runtime.InteropServices;
public static class ConsoleHelper
{
public static void CreateConsole()
{
AllocConsole();
// stdout's handle seems to always be equal to 7
IntPtr defaultStdout = new IntPtr(7);
IntPtr currentStdout = GetStdHandle(StdOutputHandle);
if (currentStdout != defaultStdout)
// reset stdout
SetStdHandle(StdOutputHandle, defaultStdout);
// reopen stdout
TextWriter writer = new StreamWriter(Console.OpenStandardOutput())
{ AutoFlush = true };
Console.SetOut(writer);
}
// P/Invoke required:
private const UInt32 StdOutputHandle = 0xFFFFFFF5;
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(UInt32 nStdHandle);
[DllImport("kernel32.dll")]
private static extern void SetStdHandle(UInt32 nStdHandle, IntPtr handle);
[DllImport("kernel32")]
static extern bool AllocConsole();
}
See How to detect if Console.In (stdin) has been redirected? for another way to detect if the console handles have been redirected.
None of the earlier answers worked well for me with VS2017 and Windows 10 (for instance they failed if launch app in debug mode).
Below you can find a little bit enhanced code. Idea is the same, but magic numbers are removed (Ceztko already mentioned that) and all necessary in\out streams are initialized.
This code works for me if create a new console (alwaysCreateNewConsole = true).
Attaching to console of parent process (alwaysCreateNewConsole = false) has several drawbacks. For example I was unable to completely mimic behavior of console app launched from cmd. And I'm not sure that it is possible at all.
And most important: after revision of Console class I reconsidered general idea of using Console class with manually created console. It works well (I hope) for most of the cases, but can bring a lot of pain in future.
static class WinConsole
{
static public void Initialize(bool alwaysCreateNewConsole = true)
{
bool consoleAttached = true;
if (alwaysCreateNewConsole
|| (AttachConsole(ATTACH_PARRENT) == 0
&& Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED))
{
consoleAttached = AllocConsole() != 0;
}
if (consoleAttached)
{
InitializeOutStream();
InitializeInStream();
}
}
private static void InitializeOutStream()
{
var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
if (fs != null)
{
var writer = new StreamWriter(fs) { AutoFlush = true };
Console.SetOut(writer);
Console.SetError(writer);
}
}
private static void InitializeInStream()
{
var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
if (fs != null)
{
Console.SetIn(new StreamReader(fs));
}
}
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
FileAccess dotNetFileAccess)
{
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
if (!file.IsInvalid)
{
var fs = new FileStream(file, dotNetFileAccess);
return fs;
}
return null;
}
#region Win API Functions and Constants
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
[DllImport("kernel32.dll",
EntryPoint = "AttachConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern UInt32 AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "CreateFileW",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CreateFileW(
string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile
);
private const UInt32 GENERIC_WRITE = 0x40000000;
private const UInt32 GENERIC_READ = 0x80000000;
private const UInt32 FILE_SHARE_READ = 0x00000001;
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
private const UInt32 OPEN_EXISTING = 0x00000003;
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
private const UInt32 ERROR_ACCESS_DENIED = 5;
private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF;
#endregion
}
Following worked for me in vs 2015, none worked from other answers:
Source: https://social.msdn.microsoft.com/profile/dmitri567/?ws=usercard-mini
using System;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace WindowsApplication
{
static class Program
{
[DllImport("kernel32.dll",
EntryPoint = "GetStdHandle",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
private const int STD_OUTPUT_HANDLE = -11;
private const int MY_CODE_PAGE = 437;
static void Main(string[] args)
{
Console.WriteLine("This text you can see in debug output window.");
AllocConsole();
IntPtr stdHandle=GetStdHandle(STD_OUTPUT_HANDLE);
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.WriteLine("This text you can see in console window.");
MessageBox.Show("Now I'm happy!");
}
}
}
I also had this problem. Every time I tried to debug my app, the console was blank. Strangely, launching the exe without the debugger worked fine.
I found that I had to Enable the Visual Studio hosting process from the project's Debug menu.
Stephen is correct that Enable native code debugging does redirect the console to the Output window. However, regardless of the native code debugging setting, I saw absolutely no output in either place until I enabled the Visual Studio hosting process.
This could have been the reason that merely disabling native code debugging did not solve your issue.
Just wanted to post the answer from Visual studio Developer community.
https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html
Go to this link and look at the answer from Ramkumar Ramesh.
I have tested this code in VS 2017.
I spent one day to find this answer. Hope it helps you as well.
Edit--
As suggessted by Mike to include some description. I would like to suggesst some corrections in Zuniar answer. He tested with VS 2015. But that would not work in VS 2017.
Instead of GetStdHandle, Please use CreateFile reference from kernel32.dll
IntPtr stdHandle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);
Before adding above code, please declare
[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;
i have taken this code from the given link.
I'd like to invoke the user's screen saver if such is defined, in a Windows environment.
I know it can be done using pure C++ code (and then the wrapping in C# is pretty simple), as suggested here.
Still, for curiosity, I'd like to know if such task can be accomplished by purely managed code using the dot net framework (version 2.0 and above), without p/invoke and without visiting the C++ side (which, in turn, can use windows API pretty easily).
I've an idea, I'm not sure how consistently this would work, so you'd need to research a bit I think, but hopefully it's enough to get you started.
A screen saver is just an executable, and the registry stores the location of this executable in HKCU\Control Panel\Desktop\SCRNSAVE.EXE
On my copy of Vista, this worked for me:
RegistryKey screenSaverKey = Registry.CurrentUser.OpenSubKey(#"Control Panel\Desktop");
if (screenSaverKey != null)
{
string screenSaverFilePath = screenSaverKey.GetValue("SCRNSAVE.EXE", string.Empty).ToString();
if (!string.IsNullOrEmpty(screenSaverFilePath) && File.Exists(screenSaverFilePath))
{
Process screenSaverProcess = Process.Start(new ProcessStartInfo(screenSaverFilePath, "/s")); // "/s" for full-screen mode
screenSaverProcess.WaitForExit(); // Wait for the screensaver to be dismissed by the user
}
}
I think having a .Net library function that does this is highly unlikely - I'm not aware of any. A quick search returned this Code Project tutorial which contains an example of a managed wrapper which you mentioned in your question.
P/invoke exists so that you're able to access OS-specific features, of which screen savers are an example.
I'm not sure you can use completely managed code to do this.
This uses Windows API but is still very simple: Launch System Screensaver from C# Windows Form
Working on any version of windows...
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace HQ.Util.Unmanaged
{
public class ScreenSaverHelper
{
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
// Signatures for unmanaged calls
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool SystemParametersInfo(int uAction, int uParam, ref int lpvParam, int flags);
// Constants
private const int SPI_GETSCREENSAVERACTIVE = 16;
private const int SPI_SETSCREENSAVERACTIVE = 17;
private const int SPI_GETSCREENSAVERTIMEOUT = 14;
private const int SPI_SETSCREENSAVERTIMEOUT = 15;
private const int SPI_GETSCREENSAVERRUNNING = 114;
private const int SPIF_SENDWININICHANGE = 2;
private const uint DESKTOP_WRITEOBJECTS = 0x0080;
private const uint DESKTOP_READOBJECTS = 0x0001;
private const int WM_CLOSE = 16;
public const uint WM_SYSCOMMAND = 0x112;
public const uint SC_SCREENSAVE = 0xF140;
public enum SpecialHandles
{
HWND_DESKTOP = 0x0,
HWND_BROADCAST = 0xFFFF
}
public static void TurnScreenSaver(bool turnOn = true)
{
// Does not work on Windows 7
// int nullVar = 0;
// SystemParametersInfo(SPI_SETSCREENSAVERACTIVE, 1, ref nullVar, SPIF_SENDWININICHANGE);
// Does not work on Windows 7, can't broadcast. Also not needed.
// SendMessage(new IntPtr((int) SpecialHandles.HWND_BROADCAST), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, (IntPtr)SC_SCREENSAVE, (IntPtr)0);
}
}
}