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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15604014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 17:21:31  来源:igfitidea点击:

No console output when using AllocConsole and target architecture x86

c#.netpinvoke

提问by teamalpha5441

I have a WinForms project, and if the user want's a debug console, I allocate a console with AllocConsole().

我有一个 WinForms 项目,如果用户想要一个调试控制台,我会分配一个带有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.

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

I also tried thisanswer, but it didn't work, I also tried Console.SetOut(GetStdHandle(-11)), which didn't work either.

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

Setting the target architecture to 'Any CPU' is no option for me.

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

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?
  • 为什么只有在目标架构设置为 x86 时才会出现这种情况?
  • 在 Visual Studio 中运行时如何输出到我的控制台?

采纳答案by Stephen

When "Enable native code debugging" is enabled, output from consoles crated with AllocConsoleis redirected to the debug output window instead.

启用“启用本机代码调试”后,来自创建的控制台的输出AllocConsole将重定向到调试输出窗口。

The reason this only happens in x86 and not AnyCPU is because you can only debug native code in an x86 application.

这只发生在 x86 而不是 AnyCPU 的原因是因为您只能在 x86 应用程序中调试本机代码。

Note that this behavior only occurs with consoles created with AllocConsole. A console application's output is not redirected.

请注意,此行为仅发生在使用AllocConsole. 控制台应用程序的输出不会被重定向。

EDIT: The other reason for the console not outputting text is when you've written to the console before calling AllocConsole.

编辑:控制台不输出文本的另一个原因是您在调用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 7which is what the handle of stdoutusually equals to.

无论出于何种原因,此代码将在重定向后恢复输出,并在无效时重新打开控制台。它使用幻数 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();
}

See How to detect if Console.In (stdin) has been redirected?for another way to detect if the console handles have been redirected.

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

回答by dss539

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.

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

I found that I had to Enable the Visual Studio hosting processfrom the project's Debugmenu.

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

Stephen is correct that Enable native code debuggingdoes 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.

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

This could have been the reason that merely disabling native code debugging did not solve your issue.

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

回答by Zunair

Following worked for me in vs 2015, none worked from other answers:

以下在 vs 2015 中对我有用,其他答案都没有:

Source: https://social.msdn.microsoft.com/profile/dmitri567/?ws=usercard-mini

来源: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!");   
        }   
    }   
}  

回答by Pavlo K

None of the earlier answers worked well for me with VS2017 and Windows 10 (for instance they failed if launch app in debug mode).

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

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.

您可以在下面找到一些增强的代码。想法是相同的,但删除了幻数(Ceztko 已经提到过)并且所有必要的输入\输出流都被初始化。

This code works for me if create a new console (alwaysCreateNewConsole = true).

如果创建一个新控制台(总是CreateNewConsole = 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.

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

And most important: after revision of Console classI 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.

最重要的是:在修改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
    }

回答by Senthilnathan PL

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.htmlGo 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.

只是想从 Visual Studio 开发人员社区发布答案。 https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html转到此链接并查看 Ramkumar Ramesh 的答案。我已经在 VS 2017 中测试了这段代码。我花了一天时间才找到这个答案。希望它也能帮助你。

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

编辑 - 正如迈克所建议的,包括一些描述。我想在 Zuniar 答案中提出一些更正。他用 VS 2015 进行了测试。但这在 VS 2017 中不起作用。请使用 kernel32.dll 中的 CreateFile 引用而不是 GetStdHandle

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.

我从给定的链接中获取了此代码。