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

本文关键字:控制台 输出 x86 AllocConsole 目标 使用 | 更新日期: 2023-09-27 18:05:30

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

所有控制台输出正常工作与目标架构设置为"任何CPU",但当我把它改为"x86"它不输出任何东西(Console.Read()仍然工作如预期)。如果我直接打开EXE,输出工作。看起来Visual Studio将它重定向到它自己的"输出"窗口。

我也试过这个答案,但它没有工作,我也试过Console.SetOut(GetStdHandle(-11)),也没有工作。

将目标架构设置为"任意CPU"对我来说是没有选择的。

这是我的两个问题:

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

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

当启用"Enable native code debugging"时,使用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();
}

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

之前的答案对我来说都不太好VS2017和Windows 10(例如,如果在调试模式下启动应用程序,它们就会失败)。

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

如果创建一个新控制台(alwaysCreateNewConsole = true),这段代码对我有效。

附加到父进程的控制台(always screatenewconsole = 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
    }

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

我也遇到过这个问题。每次我试图调试我的应用程序时,控制台都是空白的。奇怪的是,在没有调试器的情况下启动exe可以正常工作。

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

Stephen是正确的,Enable native code debugging确实将控制台重定向到Output窗口。但是,无论本机代码调试设置如何,在启用Visual Studio托管进程之前,我都看不到任何输出。

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

只是想发布Visual studio开发者社区的答案。https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html点击这个链接,看看Ramkumar Ramesh的回答。我已经在VS 2017中测试了这个代码。我花了一天时间才找到这个答案。希望它对你也有帮助。

编辑—正如迈克建议的那样,加入一些描述。我想对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;