我有一个WinForms项目,如果用户想要一个调试控制台,我会用AllocConsole()
分配一个控制台。
所有控制台输出都正常工作,目标架构设置为“任何CPU”,但当我将其更改为“x86”时,它不输出任何内容(Console.Read()
仍然按预期工作)。如果我直接打开EXE,则输出有效。看起来Visual Studio将其重定向到它自己的“输出”窗口。
我也尝试了this回答,但它没有用,我也试过Console.SetOut(GetStdHandle(-11))
,但也没有用。
将目标架构设置为“任何CPU”对我来说都是不可选择的。
所以这是我的两个问题:
答案 0 :(得分:30)
启用“启用本机代码调试”时,来自使用AllocConsole
创建的控制台的输出将重定向到调试输出窗口。
这只发生在x86而不是AnyCPU的原因是因为你只能在x86应用程序中调试本机代码。
请注意,只有使用AllocConsole
创建的控制台才会出现此问题。控制台应用程序的输出未被重定向。
编辑:控制台不输出文本的另一个原因是,在调用AllocConsole
之前写入控制台时。
无论是什么原因,如果重定向,此代码将恢复输出,并在控制台无效时重新打开控制台。它使用幻数7 ,这是stdout
的句柄通常等于。
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();
}
请参阅How to detect if Console.In (stdin) has been redirected?以了解另一种检测控制台句柄是否已被重定向的方法。
答案 1 :(得分:10)
对于VS2017和Windows 10,我的早期答案都不适用(例如,如果在调试模式下启动应用程序,它们会失败)。
下面你可以找到一些增强的代码。想法是一样的,但魔术数字被删除(Ceztko已经提到过),并且\ out流中所有必需的数据都被初始化。
如果创建一个新控制台(alwaysCreateNewConsole = true),此代码适用于我。
附加到父进程的控制台(alwaysCreateNewConsole = false)有几个缺点。例如,我无法完全模仿从cmd启动的控制台应用程序的行为。而且我不确定它是否有可能。
最重要的是:在修改Console class之后,我重新考虑了将Console类与手动创建的控制台一起使用的一般想法。对于大多数情况,它(我希望)效果很好,但将来可能带来很多痛苦。
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
}
答案 2 :(得分:5)
以下在vs 2015中为我工作过,没有人能从其他答案中找到答案:
来源: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!");
}
}
}
答案 3 :(得分:4)
我也有这个问题。每次我尝试调试我的应用程序时,控制台都是空白的。奇怪的是,在没有调试器的情况下启动exe工作正常。
我发现我必须从项目的Enable the Visual Studio hosting process
菜单中Debug
。
Stephen认为Enable native code debugging
将控制台重定向到“输出”窗口是正确的。但是,无论本机代码调试设置如何,在我启用Visual Studio托管过程之前,我都看到两个地方都没有输出。
这可能是仅仅禁用本机代码调试无法解决您的问题的原因。
答案 4 :(得分:1)
只想发布Visual Studio开发人员社区的答案。 https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html 转到此链接,查看来自Ramkumar Ramesh的答案。 我已经在VS 2017中测试了此代码。 我花了一天时间找到这个答案。希望它对您也有帮助。
编辑- 迈克(Mike)建议包含一些描述。我想建议Zuniar回答一些更正。他在VS 2015上进行了测试。但这在VS 2017中不起作用。 请使用kernel32.dll中的CreateFile引用代替GetStdHandle
IntPtr stdHandle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);
在添加上述代码之前,请声明
[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;
我已经从给定的链接中获取了这段代码。