28

WinFormsプロジェクトがあり、ユーザーがデバッグコンソールを希望する場合は、。を使用してコンソールを割り当てAllocConsole()ます。

すべてのコンソール出力は、ターゲットアーキテクチャが「AnyCPU」に設定されている場合は正常に機能しますが、「x86」に変更しても何も出力されません(Console.Read()期待どおりに機能します)。EXEを直接開くと、出力が機能します。VisualStudioがそれを独自の「出力」ウィンドウにリダイレクトしているように見えます。

私もこの答えを試しましたが、うまくいきませんでした。私も試しましConsole.SetOut(GetStdHandle(-11))たが、どちらもうまくいきませんでした。

ターゲットアーキテクチャを「任意のCPU」に設定することは、私にとってオプションではありません。

だからここに私の2つの質問があります:

  • ターゲットアーキテクチャがx86に設定されている場合にのみ、これが当てはまるのはなぜですか?
  • Visual Studio内で実行しているときに、コンソールに出力するにはどうすればよいですか?
4

5 に答える 5

39

「ネイティブコードのデバッグを有効にする」が有効になっている場合、作成されたコンソールからの出力は、AllocConsole代わりにデバッグ出力ウィンドウにリダイレクトされます。

これがAnyCPUではなくx86でのみ発生する理由は、x86アプリケーションでのみネイティブコードをデバッグできるためです。

この動作は、で作成されたコンソールでのみ発生することに注意してくださいAllocConsole。コンソールアプリケーションの出力はリダイレクトされません。

編集:コンソールがテキストを出力しないもう1つの理由は、を呼び出す前にコンソールに書き込んだ場合です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)がリダイレクトされたかどうかを検出する方法を参照してください。コンソールハンドルがリダイレクトされたかどうかを検出する別の方法。

于 2013-04-11T23:16:35.027 に答える
25

以前の回答はいずれも、VS2017とWindows 10ではうまく機能しませんでした(たとえば、アプリをデバッグモードで起動すると失敗しました)。

以下に、少し拡張されたコードを示します。アイデアは同じですが、魔法数が削除され(Ceztkoはすでに述べています)、必要なすべてのin\outストリームが初期化されます。

このコードは、新しいコンソールを作成する場合に機能します(alwaysCreateNewConsole = true)。

親プロセスのコンソールへの接続(alwaysCreateNewConsole = false)には、いくつかの欠点があります。たとえば、cmdから起動したコンソールアプリの動作を完全に模倣することはできませんでした。そして、それが可能かどうかはまったくわかりません。

そして最も重要なのは、コンソールクラスの改訂後、手動で作成したコンソールでコンソールクラスを使用するという一般的な考え方を再考したことです。ほとんどの場合はうまく機能しますが(私は願っています)、将来的には多くの苦痛をもたらす可能性があります。

    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
    }
于 2018-02-19T11:13:01.323 に答える
5

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!");   
        }   
    }   
}  
于 2015-12-09T04:31:46.597 に答える
4

私もこの問題を抱えていました。アプリをデバッグしようとするたびに、コンソールが空白になりました。不思議なことに、デバッガなしでexeを起動しても問題なく動作しました。

Enable the Visual Studio hosting processプロジェクトのDebugメニューからやらなければならないことがわかりました。

Enable native code debuggingスティーブンは、コンソールを出力ウィンドウにリダイレクトするのは正しいです。ただし、ネイティブコードのデバッグ設定に関係なく、Visual Studioホスティングプロセスを有効にするまで、どちらの場所にも出力はまったく表示されませんでした。

これが、ネイティブコードのデバッグを無効にするだけでは問題が解決しなかった理由である可能性があります。

于 2015-02-07T02:12:51.920 に答える
1

VisualStudioDeveloperコミュニティからの回答を投稿したかっただけです。 https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html このリンクに移動して、RamkumarRameshからの回答を確認してください。私はVS2017でこのコードをテストしました。私は、この答えを見つけるために1日を費やしました。それがあなたにも役立つことを願っています。

編集-いくつかの説明を含めるためにマイクによって提案されたように。Zuniarの回答でいくつかの修正を提案したいと思います。彼はVS2015でテストしました。しかし、それはVS 2017では機能しません。GetStdHandleの代わりに、kernel32.dllのCreateFile参照を使用してください。

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;

私は与えられたリンクからこのコードを取りました。

于 2019-08-16T00:23:07.610 に答える