.NETコンソールアプリでキーが押されるのを聞く


224

キーEscが押される(押されるなど)まで、コンソールアプリケーションを引き続き実行するにはどうすればよいですか?

私は、whileループにラップされていると想定しています。ReadKey操作をブロックしてキーを要求するので、キーを押すことを続行して聞くのではなく、気に入らない。

これはどのように行うことができますか?

回答:


343

ブロックしないことがわかってConsole.KeyAvailableいる場合にのみ呼び出すように使用しますReadKey

Console.WriteLine("Press ESC to stop");
do {
    while (! Console.KeyAvailable) {
        // Do something
   }       
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);

6
しかし、readLine()の代わりにこれを行うと、「上」キーを押して履歴を呼び出すという素晴らしい機能が失われます。
v.oddou 2015

3
@ v.oddou最後の行の後に、「your Console.ReadLine()and add you set」を追加するだけです。
Gaffi

1
このループは多くのCPU / RAMを消費しませんか?そうでない場合、どのようにですか?
ソフィア

78

あなたのアプローチを少し変えることができます-を使用Console.ReadKey()してアプリを停止しますが、作業はバックグラウンドスレッドで行います。

static void Main(string[] args)
{
    var myWorker = new MyWorker();
    myWorker.DoStuff();
    Console.WriteLine("Press any key to stop...");
    Console.ReadKey();
}

myWorker.DoStuff()機能しますが、その後(使用してバックグラウンドスレッドで別の関数を呼び出しますAction<>()またはFunc<>()それを行うための簡単な方法です)、その直後に戻ります。


60

最短の方法:

Console.WriteLine("Press ESC to stop");

while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
{
    // do something
}

Console.ReadKey()はブロッキング関数です。プログラムの実行を停止し、キーが押されるのを待ちますが、Console.KeyAvailable最初にチェックすることにより、whileループはブロックされず、Escが押されるまで実行されます。


これまでで最も簡単な例。ありがとうございました。
joelc

18

ビデオの呪いhttp://www.pluralsight.comにある Jason RobertsによるC#での.NETコンソールアプリケーションの構築

以下を実行して、複数の実行中のプロセスを持つことができます

  static void Main(string[] args)
    {
        Console.CancelKeyPress += (sender, e) =>
        {

            Console.WriteLine("Exiting...");
            Environment.Exit(0);
        };

        Console.WriteLine("Press ESC to Exit");

        var taskKeys = new Task(ReadKeys);
        var taskProcessFiles = new Task(ProcessFiles);

        taskKeys.Start();
        taskProcessFiles.Start();

        var tasks = new[] { taskKeys };
        Task.WaitAll(tasks);
    }

    private static void ProcessFiles()
    {
        var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt");

        var taskBusy = new Task(BusyIndicator);
        taskBusy.Start();

        foreach (var file in files)
        {
            Thread.Sleep(1000);
            Console.WriteLine("Procesing file {0}", file);
        }
    }

    private static void BusyIndicator()
    {
        var busy = new ConsoleBusyIndicator();
        busy.UpdateProgress();
    }

    private static void ReadKeys()
    {
        ConsoleKeyInfo key = new ConsoleKeyInfo();

        while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape)
        {

            key = Console.ReadKey(true);

            switch (key.Key)
            {
                case ConsoleKey.UpArrow:
                    Console.WriteLine("UpArrow was pressed");
                    break;
                case ConsoleKey.DownArrow:
                    Console.WriteLine("DownArrow was pressed");
                    break;

                case ConsoleKey.RightArrow:
                    Console.WriteLine("RightArrow was pressed");
                    break;

                case ConsoleKey.LeftArrow:
                    Console.WriteLine("LeftArrow was pressed");
                    break;

                case ConsoleKey.Escape:
                    break;

                default:
                    if (Console.CapsLock && Console.NumberLock)
                    {
                        Console.WriteLine(key.KeyChar);
                    }
                    break;
            }
        }
    }
}

internal class ConsoleBusyIndicator
{
    int _currentBusySymbol;

    public char[] BusySymbols { get; set; }

    public ConsoleBusyIndicator()
    {
        BusySymbols = new[] { '|', '/', '-', '\\' };
    }
    public void UpdateProgress()
    {
        while (true)
        {
            Thread.Sleep(100);
            var originalX = Console.CursorLeft;
            var originalY = Console.CursorTop;

            Console.Write(BusySymbols[_currentBusySymbol]);

            _currentBusySymbol++;

            if (_currentBusySymbol == BusySymbols.Length)
            {
                _currentBusySymbol = 0;
            }

            Console.SetCursorPosition(originalX, originalY);
        }
    }

2
私がコンソールアプリケーションで同じことを行おうとしていたアプローチについて少し説明してもらえますか?
nayef har、2015

@nayefharbは一連のタスクをセットアップして開始し、WaitAllはすべてが返されるまで待機します。したがって、個々のタスクは返されず、CancelKeyPressがトリガーされるまで永遠に続きます。
wonea

Console.CursorVisible = false;カーソルの点滅エラーを回避するのが最善です。この行をstatic void Main(string[] args)ブロックの最初の行として追加します。
Hitesh

12

ここでは、別のスレッドで何かを行い、別のスレッドで押されたキーのリスニングを開始する方法を示します。そして、実際のプロセスが終了するか、ユーザーがEscキーを押してプロセスを終了すると、コンソールはその処理を停止します。

class SplitAnalyser
{
    public static bool stopProcessor = false;
    public static bool Terminate = false;

    static void Main(string[] args)
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Split Analyser starts");
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Press Esc to quit.....");
        Thread MainThread = new Thread(new ThreadStart(startProcess));
        Thread ConsoleKeyListener = new Thread(new ThreadStart(ListerKeyBoardEvent));
        MainThread.Name = "Processor";
        ConsoleKeyListener.Name = "KeyListener";
        MainThread.Start();
        ConsoleKeyListener.Start();

        while (true) 
        {
            if (Terminate)
            {
                Console.WriteLine("Terminating Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }

            if (stopProcessor)
            {
                Console.WriteLine("Ending Process...");
                MainThread.Abort();
                ConsoleKeyListener.Abort();
                Thread.Sleep(2000);
                Thread.CurrentThread.Abort();
                return;
            }
        } 
    }

    public static void ListerKeyBoardEvent()
    {
        do
        {
            if (Console.ReadKey(true).Key == ConsoleKey.Escape)
            {
                Terminate = true;
            }
        } while (true); 
    }

    public static void startProcess()
    {
        int i = 0;
        while (true)
        {
            if (!stopProcessor && !Terminate)
            {
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Processing...." + i++);
                Thread.Sleep(3000);
            }
            if(i==10)
                stopProcessor = true;

        }
    }

}

5

Visual Studioを使用している場合は、[デバッグ]メニューの[デバッグなしで開始]を使用できます。

「続行するには任意のキーを押してください...」と自動的に書き込まれます。アプリケーションが完了するとコンソールが表示され、キーが押されるまでコンソールが開いたままになります。


3

他の回答のいくつかがうまく処理できない場合の対処:

  • 応答性:キープレス処理コードの直接実行。ポーリングまたはブロッキング遅延の気まぐれを回避します
  • オプション:グローバルキープレスはオプトインです。それ以外の場合、アプリは正常に終了します
  • 懸念の分離:侵襲性の低いリスニングコード。通常のコンソールアプリのコードとは独立して動作します。

このページのソリューションの多くには、でのポーリングConsole.KeyAvailableまたはブロックが含まれConsole.ReadKeyます。Consoleここでは.NET があまり協調的でないことは事実ですがTask.Run、よりモダンなAsyncリスニングモードに移行するために使用できます。

注意すべき主な問題は、デフォルトでは、コンソールスレッドがAsync操作用に設定されていないことです。つまり、main関数の下部からAsync抜けると、完了を待つのではなく、AppDomanとプロセスが終了します。 。これに対処する適切な方法は、Stephen Clearyの AsyncContextAsyncして、シングルスレッドコンソールプログラムで完全なサポートを確立することです。しかし、キーを押すのを待つような単純なケースでは、完全なトランポリンをインストールするのはやり過ぎかもしれません。

以下の例は、ある種の反復バッチファイルで使用されるコンソールプログラムの例です。この場合、プログラムはその作業を行った場合、通常はそれが終了する必要がありますせずにキー入力を必要とし、その後、我々は、オプションキーを押してできるようにする予防出るのアプリを。サイクルを一時停止して状況を調査し、場合によっては再開するか、または一時停止をバッチファイルから適切に抜ける既知の「制御点」として使用できます。

static void Main(String[] args)
{
    Console.WriteLine("Press any key to prevent exit...");
    var tHold = Task.Run(() => Console.ReadKey(true));

    // ... do your console app activity ...

    if (tHold.IsCompleted)
    {
#if false   // For the 'hold' state, you can simply halt forever...
        Console.WriteLine("Holding.");
        Thread.Sleep(Timeout.Infinite);
#else                            // ...or allow continuing to exit
        while (Console.KeyAvailable)
            Console.ReadKey(true);     // flush/consume any extras
        Console.WriteLine("Holding. Press 'Esc' to exit.");
        while (Console.ReadKey(true).Key != ConsoleKey.Escape)
            ;
#endif
    }
}

1

以下のコードを使用すると、コンソールの実行中にSpaceBarを押すのをリッスンし、別のキーが押されるまで一時停止し、メインループを中断するためにEscapeKeyをリッスンすることもできます

static ConsoleKeyInfo cki = new ConsoleKeyInfo();


while(true) {
      if (WaitOrBreak()) break;
      //your main code
}

 private static bool WaitOrBreak(){
        if (Console.KeyAvailable) cki = Console.ReadKey(true);
        if (cki.Key == ConsoleKey.Spacebar)
        {
            Console.Write("waiting..");
            while (Console.KeyAvailable == false)
            {
                Thread.Sleep(250);Console.Write(".");
            }
            Console.WriteLine();
            Console.ReadKey(true);
            cki = new ConsoleKeyInfo();
        }
        if (cki.Key == ConsoleKey.Escape) return true;
        return false;
    }

-1

私の経験によれば、コンソールアプリで最後に押されたキーを読み取る最も簡単な方法は次のとおりです(矢印キーの例):

ConsoleKey readKey = Console.ReadKey ().Key;
if (readKey == ConsoleKey.LeftArrow) {
    <Method1> ();  //Do something
} else if (readKey == ConsoleKey.RightArrow) {
    <Method2> ();  //Do something
}

ループを回避するために使用し、代わりにメソッド内に上記のコードを記述し、「Method1」と「Method2」の両方の最後に呼び出します。したがって、「Method1」または「Method2」を実行した後、Console.ReadKey() .Keyは再度キーを読み取る準備ができています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.