2009-05-11 22 views
6

私はサーバーアプリケーションを書いており、コンソールベースにしたいと思っています。私は、異なるコマンドを入力することができるようにユーザを必要とするが、同時にユーザが書いている間に何かがコンソールに出力される可能性がある。これにより、バッファが壊れてしまいます。これを行うきれいな方法はありますか?C#の同時入力と出力?

ありがとうございました。

+0

私は簡単な方法はありません。私は非常に成熟した製品(gdb、windbg)を見てきましたが、一般的に画面に非同期情報をダンプしません。あなたはどんなインタラクションを探していますか?あなたは、あなたがどのようにコンソールを動作させるべきだと思ったかを理解しましたか? –

+0

本当に複雑なコンセプトではなく、ユーザーがテキストを入力できるようにするだけで、最後の行が書き込まれた後にプロンプ​​トが表示されます。ユーザーが入力中に他の行が書き込まれると、この行の後にプロンプ​​トが再描画されます。 –

答えて

4

コンソールを出力領域と入力領域に分割する方法を示すテストプログラムの作業を開始しました。この領域では、出力領域が増えて入力領域が下に移動します。それはまだ完璧ではないですが、あなたが探している答えにそれを開発することができる場合があります

static int outCol, outRow, outHeight = 10; 

    static void Main(string[] args) 
    { 
    bool quit = false; 
    System.DateTime dt = DateTime.Now; 
    do 
    { 
     if (Console.KeyAvailable) 
     { 
      if (Console.ReadKey(false).Key == ConsoleKey.Escape) 
       quit = true; 
     } 
     System.Threading.Thread.Sleep(0); 
     if (DateTime.Now.Subtract(dt).TotalSeconds > .1) 
     { 
      dt = DateTime.Now; 
      WriteOut(dt.ToString(" ss.ff"), false); 
     }    
    } while (!quit); 
    } 

    static void WriteOut(string msg, bool appendNewLine) 
    { 
    int inCol, inRow; 
    inCol = Console.CursorLeft; 
    inRow = Console.CursorTop; 

    int outLines = getMsgRowCount(outCol, msg) + (appendNewLine?1:0); 
    int outBottom = outRow + outLines; 
    if (outBottom > outHeight) 
     outBottom = outHeight; 
    if (inRow <= outBottom) 
    { 
     int scrollCount = outBottom - inRow + 1; 
     Console.MoveBufferArea(0, inRow, Console.BufferWidth, 1, 0, inRow + scrollCount); 
     inRow += scrollCount; 
    } 
    if (outRow + outLines > outHeight) 
    { 
     int scrollCount = outRow + outLines - outHeight; 
     Console.MoveBufferArea(0, scrollCount, Console.BufferWidth, outHeight - scrollCount, 0, 0); 
     outRow -= scrollCount; 
     Console.SetCursorPosition(outCol, outRow); 
    } 
    Console.SetCursorPosition(outCol, outRow); 
    if (appendNewLine) 
     Console.WriteLine(msg); 
    else 
     Console.Write(msg); 
    outCol = Console.CursorLeft; 
    outRow = Console.CursorTop; 
    Console.SetCursorPosition(inCol, inRow); 
    } 

    static int getMsgRowCount(int startCol, string msg) 
    { 
    string[] lines = msg.Split('\n'); 
    int result = 0; 
    foreach (string line in lines) 
    { 
     result += (startCol + line.Length)/Console.BufferWidth; 
     startCol = 0; 
    } 
    return result + lines.Length - 1; 
    } 
+0

ありがとう、私はそれで動作します! –

0

OpenStandardInputを呼び出して、入力を読み取り、その位置をリセットしてから、出力ストリームに書き込みを試みましたか?その後、OpenStandardInputを再度呼び出して、データをストリームに戻すことができます。

+0

私はちょうど試みたが、私は心配している。私はそれをする方法が本当にわからない...私はちょうど試したとそれは例外を投げた "ストリームはシークをサポートしていません。 –

1

入力中に出力が届くようにする必要がある場合は、出力を新しいウィンドウに送信することをおすすめします。したがって、アプリケーションを起動するために使用される1つのウィンドウを持つことができます。次に、新しいコンソールを開いて入力用のスレッドを生成し、元のウィンドウに出力メッセージを送信し続けます。私はあなたが同じウィンドウ内にすべてを保持しようとすると、あまりにも多くのリソースロックの問題にぶつかると思います。

+0

+1。とにかく、貧困層のユーザは、入力を押した直後に実行されるいくつかの不吉なバックグラウンドプロセスによって生じる入力と出力の違いを、どのように伝えるのでしょうか? – MarkJ

0

これを達成する完璧な方法はありません。私が使った最後のバージョンのtelnetは何も入力していませんでした(ちょうどキーストロークを読んでいました)。代わりに、コンソールに出力する必要のあるデータをバッファに格納し、ユーザーがコマンドの入力を完了した後にのみ印刷する方法があります。 (あなたは出力をタイムスタンプして、それをより明白にすることもできます)私は実際にはこれ以上の代替手段を見ることはできません - 必然的に同期I/Oインタフェース(コマンドライン)バックエンドの非同期操作

1

この種のことは、サーバーをクライアント/サーバーアプリケーションとして扱うと、やや単純な問題になります。サーバーに、コマンドを送信して出力を受け取るクライアント管理アプリケーションへの「n」接続を許可します。クライアントアプリケーションは、入力と出力を完全に分離することができ、入力を処理するスレッドと出力を処理するスレッドを持つことができます。

入力スレッドが行を入力しているときに出力スレッドがブロックされ、行がキャンセルまたはコミットされたときにブロックが解除される可能性があります。

2

個人的に私は、同時に入力とoutupの両方を扱う管理コンソールにイベントハンドラを使用することになり、クラスScreenManagerを作成するか、そのクラス内にvoid RunProgram()mthodを追加して、入力キー "Console.ReadKey(bool).key"を読み込むためのハンドラと必要な変数を持つイベントを作成します。 「あなたはそれを呼ばwhatev」

メインプログラムの
static Consolekey newKey; 

、あなたのクラスのインスタンスをクリート、その後、coreThread =新しいスレッド(デリゲート(){myinstance.myProgramMrthod()をスレッド、そのインスタンス内部メソッドのスレッドを作成});

スレッドが起動して実行されるまでメインにループします。 while(!Thread.IsAlive); while

メインプログラムループを作成します。

while (true) 
{ 
} 

次に、saftyの場合は、カスタムスレッドに参加して、カスタムスレッドが閉じられ/破棄されるまでメインプログラムが継続しないようにします。

customThread.Join(); 

ここで、2つのスレッドが別々に動作しています。

クラスに戻って、イベントハンドラメソッド内にスイッチを作成します。

switch (newkey) 
{ 
case Consolekey.enter 
Console.WriteLine("enter pressed"); 
break; 

ect, ect. 

default: 
Console.write(newkey); // writes character key that dont match above conditions to the screen. 
break; 
} 

キーをどのように処理したいかに関わらずロジックをスティックします。 How to use multiple modifier keys in C# 何か助けになるかもしれません。

RunProgram()またはあなたがそれを呼び出すことを選択した場合は、必要なコードを実行した後、無限ループを作成してキーの変更をチェックします。

while (true) 
{ 
newKey = Console.ReadKey(true).Key; 
if (newKey != oldKey) 
{ 
KeyChange.Invoke(); 
} 
} 

このループは、押されたキーを保存してから、新しいキーがtrueかどうかを確認してイベントを発生させます。

あなたはあなたの探しているもののコアを持っています。メインループには表示するテキストを自由に表示することができますが、新しいキーを求めるループがあります。

私が考えることができるこの2つの修正可能なバグは、スイッチ内の「デフォルト」がキャップまたは文字列でコンソールに印刷されることです。もう1つは、コンソールに追加されたテキストがカーソルポイントに追加されるため、ユーザーが入力したばかりのテキストに追加されます。

私はちょうど作ったので、コンソールに追加されたテキストを管理する方法を教えてください。再びイベントを使用しています。私は全体を通してメソッドと関数を使用することができますが、イベントはプログラムへの移動の柔軟性を追加します。

大丈夫ですので、私たちが入力した入力を怒らせることなく、コンソールにテキストを追加できます。入力を下に保つ。

文字列引数のdelegate myDelegate(文字列Arg)を持つ署名付きの新しいデリゲートを作成します。このデリゲートを使ってイベントを作成し、newline、newinput、whatevと呼んでください。

イベントハンドラは、コンソール更新テキストに反して、ユーザ入力の上にコンソールに挿入したいものを文字列引数とし、ユーザがコンソールに入力して保存したテキストを取得しますparamiterの文字列をコンソールに出力してから、ユーザーの入力を下に出力します。

私は、メソッドの外側の最上部に静的な文字列を作成することを選択しました。それを頻繁に使用するため空にして初期化し、メソッドを作成するたびに新しい識別子を作成したいメソッドの終わりにそれを破棄し、新しいものを再作成するだけです。

文字列「入力」などを呼び出します。

keychangeイベントハンドルのデフォルト領域add input + = newkey。

Consolekey.enterセクションのコンソールwritline input次にinput = string.emptyまたはstring = ""。

イベントハンドラでロジックを追加します。

public void OnInsert(string Argument) 
     { 
      Console.CursorTop -= 1; 

    // moves the cursor to far left so new input overwrites the old. 



    // if arg string is longer, then print arg string then print input // string. 

      if (Argument.Length > input.Length) 
      { 
       Console.WriteLine(Argument); 
       Console.WriteLine(input); 
      } 
      else 
      { 

    // if the users input if longer than the argument text then print 
    // out the argument text, then print white spaces to overwrite the 
    // remaining input characters still displayed on screen. 


       for (int i = 0; i < input.Length;i++) 
       { 
        if (i < Argument.Length) 
        { 
         Console.Write(Argument[i]); 
        } 
        else 
        { 
         Console.Write(' '); 
        } 
       } 
       Console.Write(Environment.NewLine); 
       Console.WriteLine(input); 
      } 
     } 

hope this helps some of you, its not perfect, a quick put together test that works enough to be built on. 
+1

oldKeyはconst = 0です。 – Gorlykio

関連する問題