2012-03-08 3 views
5

次のプログラムは、Console.ReadKey()でエラーをスローします。Win32呼び出しを使用してC#内でコンソールを閉じる/開く方法は?

コンソールを無効にした後、コンソールを再び有効にするにはどうすればよいですか?専門家

エキストラ

using System; 
    using System.Threading; 
    using System.Runtime.InteropServices; 

    namespace ConsoleApplication 
    { 
     class Program 
     { 
      static void Main(string[] args) 
      { 
       ThreadPool.QueueUserWorkItem((o) => 
       { 
        Thread.Sleep(1000); 
        IntPtr stdin = GetStdHandle(StdHandle.Stdin); 
        CloseHandle(stdin); 
       }); 
       Console.ReadLine(); 
       Console.Write("ReadLine() successfully aborted by background thread.\n"); 
       Console.Write("[any key to exit]"); 
       Console.ReadKey(); // Throws an exception "Cannot read keys when either application does not have a console or when console input has been redirected from a file. Try Console.Read." 
      } 

      // P/Invoke: 
      private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 }; 
      [DllImport("kernel32.dll")] 
      private static extern IntPtr GetStdHandle(StdHandle std); 
      [DllImport("kernel32.dll")] 
      private static extern bool CloseHandle(IntPtr hdl); 
     } 
    } 

あなたが迷っている場合は、私はC#内ReadLineメソッドを()、実行中のバックグラウンドスレッドを殺すことができるようにする必要があります。これは唯一の方法です(ReadLine()はオペレーティングシステムの内部で、アンマネージコードで深く実行されるためthreadAbortは機能しません)。 StackOverflowにはこのトピックに関する話がたくさんありますが、誰も実際にConsole.ReadLine()を中止する満足のいく方法を発見(または投稿)していません。私は、このコードが正しい軌道にあると思う - もしそれを無効にした後にコンソールを再び有効にできるのであれば。

+0

ハンドルを閉じると、手でもう一度開く必要があります。ハンドルを閉じただけで、同じハンドルを再び使用できる保証はありません。正しいハンドルを閉じてもよろしいですか?私は、Readlineを中止して標準入力の血まみれのハンドルを閉じるより簡単な方法があると信じなければなりません。 –

+0

@Ramhound私はReadLineを中止する簡単な方法があったと思う。私はすべてを試してみました - .Abort、アプリケーションにキーを送る(現在のフォーカスのためだけに働く)など。より良い方法があれば、私はすべて耳にします。 – Contango

+0

@Ramhound私が考えることができる唯一の他の方法は、100%管理されたC#でReadLineのクローンを作成することです.Abortは動作しますが、Win32コードの数行に比べて確かに複雑です。 – Contango

答えて

0

それは場合に役立ちますか分からないのですが、私は勝利フォームアプリでコンソールに書き込みできるようにするために必要なてきたとき、私はこのクラスを使用しました:

public class ConsoleHelper 
{ 
    /// <summary> 
    /// Allocates a new console for current process. 
    /// </summary> 
    [DllImport("kernel32.dll")] 
    public static extern Boolean AllocConsole(); 

    /// <summary> 
    /// Frees the console. 
    /// </summary> 
    [DllImport("kernel32.dll")] 
    public static extern Boolean FreeConsole(); 
} 

コールAllocConsoleを作成しますコンソールを作成し、それからそれに書き込むことができます。その後、FreeConsoleを呼び出します。

+1

チップをありがとうございます。私はこれを試しました。 FreeConsole()を呼び出してAllocConsole()を呼び出すと、既存のReadLine()呼び出しは "保護されたメモリを読み書きしようとしました"という例外がスローされ、他のメモリが壊れていることがよくあります。この例外は、C#のtry/catchによって捕らえられません。それはまた悪い副作用を持っています。それはコンソールの歴史を失って、効果的に新鮮なコンソールを開始します。別の方法があるのだろうか? – Contango

6

使用のPostMessageは、現在のプロセスの中に[ENTER]送信するために:コードは、管理対象外で実行されているように、この答えはまた、)(ReadLineメソッドに.Abortの呼び出しは動作しませんという事実を回避します

class Program 
    { 
     [DllImport("User32.Dll", EntryPoint = "PostMessageA")] 
     private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam); 

     const int VK_RETURN = 0x0D; 
     const int WM_KEYDOWN = 0x100; 

     static void Main(string[] args) 
     { 
      Console.Write("Switch focus to another window now to verify this works in a background process.\n"); 

      ThreadPool.QueueUserWorkItem((o) => 
      { 
       Thread.Sleep(4000); 

       var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; 
       PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0); 
      }); 

      Console.ReadLine(); 

      Console.Write("ReadLine() successfully aborted by background thread.\n"); 
      Console.Write("[any key to exit]"); 
      Console.ReadKey(); 
     } 
    } 

Windowsカーネル内の深いコード。

この回答は、現在のプロセスにフォーカスがある場合(例えば、SendKeysInput Simulatorなど)にのみ有効な回答より優れています。

このアンサーは、現在のコンソールハンドルを閉じる操作により、将来のReadLine()呼び出しでエラーが発生するため、現在のコンソールハンドルを閉じる方法より優れています。

+2

これは私が好むソリューションです - ありがとう! –

+0

私はこのソリューションを使用しましたが、CTRL + F5(ビジュアルスタジオ2017)でプロジェクトを実行した場合、F5または実行可能ファイルであれば正常に動作しません – Nikita

関連する問題