好吧,当需要 Winform 时,我一直使用
AllocConsole()
方法进行控制台输出,因为在写入控制台时我使用了多种颜色。
使用VS 2015及以下版本,调试模式下的AllocConsole始终正常工作,
Console.WriteLine
可以正确写入。现在使用 VS 2017,当 AllocConsole 被调用时,控制台会显示,但是,console.WriteLine 输出不是转到该控制台,而是转到 Visual Studio 的输出窗口。
我更喜欢使用 AllocConsole 而不是输出窗口,因为我严重依赖颜色。我已经做了很多关于如何解决这个问题的搜索,但我似乎找不到答案。
AllocConsole()
本身不起作用,因为 VS 2017 做了一些“调试标准输出重定向魔法”。要解决此问题,您需要使用 AllocConsole()
创建一个控制台并修复标准输出句柄。
这是我找到的截图:
[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 Ramesh 的解决方法: VS2017 中控制台输出消失了
基于 wischi 的答案,如果您希望静态 Console 类的属性正常工作,例如 Console.ForegroundColor,则将desiredAccess 设置为 GENERIC_READ | 非常重要。 GENERIC_WRITE。我认为原因是 Console 在内部使用 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);
}
}