web-dev-qa-db-ja.com

AllocConsoleおよびターゲットアーキテクチャx86を使用しているときにコンソール出力がない

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

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

私も試しました this 答えましたが、うまくいきませんでした。Console.SetOut(GetStdHandle(-11))も試しましたが、うまくいきませんでした。

ターゲットアーキテクチャを「任意のCPU」に設定することは、私には選択肢がありません。

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

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

「ネイティブコードデバッグを有効にする」が有効になっている場合、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)がリダイレクトされたかどうかを検出する方法 を参照してください。

34
Stephen

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

以下に少し強化されたコードがあります。アイデアは同じですが、マジックナンバーが削除され(Ceztkoが既に言及しています)、必要なすべての入出力ストリームが初期化されます。

このコードは、新しいコンソールを作成する場合に機能します(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
    }
18
Pavlo K

対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!");   
        }   
    }   
}  
5
Zunair

私もこの問題を抱えていました。アプリをデバッグしようとするたびに、コンソールに何も表示されなくなりました。奇妙なことに、デバッガーなしでexeを起動しても問題なく動作しました。

Enable the Visual Studio hosting processプロジェクトのDebugメニューから。

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

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

4
dss539

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

編集-いくつかの説明を含めるようにマイクによって提案されたとおり。 Zuniarの回答でいくつかの修正を提案したいと思います。彼はVS 2015でテストしました。ただし、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;

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

1