2017-11-29 35 views
1

ボタンクリックからコンソールをwinformアプリケーションから開こうとしています。私は以下のコードでこれをやっています。コンソールを2回開いた後でConsole.Clear()を呼び出すときに例外が発生する

[DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool AllocConsole(); 

    [DllImport("kernel32.dll")] 
    static extern Boolean FreeConsole(); 

private void button1_Click(object sender, EventArgs e) 
    { 
     int userInput = 0; 
     AllocConsole(); 

     do 
     { 
      Console.Clear(); 
      Console.WriteLine("Hello World"); 
      Console.WriteLine("Select 1 or 2"); 
      int.TryParse(Console.ReadLine(), out userInput); 
     } while (userInput != 1 && userInput != 2); 

     FreeConsole(); 
    } 

初めてコンソールを開いたときに正常に動作します。二度目は正常に動作しますコンソールを開こうとしたが、Console.Clear();が呼ばれたら、私が取得:

型「System.IO.IOException」の未処理の例外が

がmscorlib.dllで発生しました追加情報ハンドルが無効です。

Console.WriteLine();Console.ReadLine();にも同じ例外がスローされます。 私はthisthis、およびthisから提案されたソリューションを試しましたが、私は同じ "ハンドルが無効です。" Console.Clear();のエラーです。

私が試したいくつかの追加コード:

[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(); 

    [DllImport("kernel32.dll")] 
    static extern Boolean FreeConsole(); 


    private const int STD_OUTPUT_HANDLE = -11; 
    private const int MY_CODE_PAGE = 437; 

private void button1_Click(object sender, EventArgs e) 
    { 
     int userInput = 0; 
     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); 
     Stream streamIn = Console.OpenStandardInput(); 
     TextReader readerIn = new StreamReader(streamIn); 
     Console.SetIn(readerIn); 

     do 
     { 
      Console.Clear(); 
      Console.WriteLine("Hello World"); 
      Console.WriteLine("Select 1 or 2"); 
      int.TryParse(Console.ReadLine(), out userInput); 
     } while (userInput != 1 && userInput != 2); 

     FreeConsole(); 
    } 

これはConsole.WriteLine();Console.ReadLine();ためのOKのようですが、私はまだConsole.Clear();によってスローされた例外を回避することはできません。私は何かが欠けている場合誰でも私に教えてくれる?

+0

プロセスは1つのコンソールにのみ関連付けることができます。https://docs.microsoft.com/en-us/windows/console/allocconsole – Tony

+0

@Tony「プロセスはFreeConsole関数を使用して、現在のコンソールから自分自身を切り離すことができますAllocConsoleを呼び出して新しいコンソールまたはAttachConsoleを作成し、別のコンソールにアタッチすることができます。あなたのソースから... – Sudsy1002

+0

AllocConsole()が成功したかどうかを確認していません – BugFinder

答えて

2

コンソールに複数回接続したり取り外したりすることはできません。私が理解する限り、the code of private static IntPtr ConsoleInputHandle(同じはConsoleOutputHandleになります)は、最初の使用時にハンドルが一度初期化されるため、コンソールからプロセスを切り離してもう一度ハンドルを再接続するとハンドルが無効になります。

Consoleクラスは、あなたが望むように使用することはできません(また、コンソールとWin32アプリケーションの両方で動作するプログラムの実例もわかりません - 私が見たほとんどのアプリケーションでは、2つのバージョンの.exeが提供されます)。

Windowsのコンソールが本当に必要な場合は、Windows APIの上に独自のコンソールラッパーを用意してみてください。コンソールの外観だけが必要な場合は、自分の "コンソールのような"ウィンドウのレンダリングを行うことができます。

+0

この場合、AllocConsoleが失敗することが予想されます。代わりに、それは成功し、私はConsole.WriteLineとConsole.ReadLineを使用することができます。これらのメソッドの両方を使用できるのであれば、Console.Clear()を使用してください。以前の2つの方法で使用されていたハンドルとは別のハンドルを使用していますか? – Sudsy1002

+0

@ Sudsy1002 as far [私が見るところ](http://referencesource.microsoft.com/#mscorlib/system/console.cs,5a089586e22b4062) 'WriteLine'は無条件にハンドルを使います(私にとっては不思議ですが、安全ではないようです)。'Clear'メソッドは、ハンドルが有効かどうかをチェックします(上記のリンクを参照してください)、おそらくこのチェックがスローされます。私はなぜそれがこのようにコード化されたのかわかりません。 –

関連する問題