使用AllocConsole和目标体系结构x86时没有控制台输出

我有一个WinForms项目,如果用户想要一个调试控制台,我会分配一个带AllocConsole()的控制台。

所有控制台输出都正常工作,目标架构设置为“任何CPU”,但当我将其更改为“x86”时,它不会输出任何内容( Console.Read()仍然按预期工作)。 如果我直接打开EXE,则输出有效。 看起来Visual Studio将其重定向到它自己的“输出”窗口。

我也尝试了这个答案,但它没有用,我也试过Console.SetOut(GetStdHandle(-11)) ,但也Console.SetOut(GetStdHandle(-11))

将目标体系结构设置为“任何CPU”对我来说是没有选择的。

所以这是我的两个问题:

  • 为什么只有当目标体系结构设置为x86时才会出现这种情况?
  • 在Visual Studio中运行时如何输出到我的控制台?

启用“启用本机代码调试”时,将使用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(); } 

请参阅如何检测Console.In(stdin)是否已重定向? 用于检测控制台句柄是否已重定向的另一种方法。

以下在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!"); } } } 

我也有这个问题。 每次我尝试调试我的应用程序时,控制台都是空白的。 奇怪的是,没有调试器启动exe工作正常。

我发现我必须从项目的Debug菜单Enable the Visual Studio hosting process

Stephen是正确的, Enable native code debugging会将控制台重定向到“输出”窗口。 但是,无论本机代码调试设置如何,在我启用Visual Studio托管过程之前,我都看到两个地方都没有输出。

这可能是仅仅禁用本机代码调试无法解决您的问题的原因。

对于VS2017和Windows 10,我的早期答案都不适合我(例如,如果在调试模式下启动应用程序,它们就失败了)。

您可以在下面找到一些增强的代码。 想法是一样的,但魔术数字被删除(Ceztko已经提到过),并且\ out流中的所有必要条件都被初始化。

如果创建一个新控制台(alwaysCreateNewConsole = true),此代码适用于我。

附加到父进程的控制台(alwaysCreateNewConsole = false)有几个缺点。 例如,我无法完全模仿从cmd启动的控制台应用程序的行为。 而且我不确定它是否可行。

最重要的是:在修改Console类之后,我重新考虑了使用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 }