2009-05-11 7 views
6

J'écris une application serveur et je veux qu'elle soit basée sur la console. J'ai besoin que l'utilisateur puisse entrer différentes commandes, mais en même temps il y a une possibilité que quelque chose soit produit sur la console pendant que l'utilisateur écrit. Cela gâche le tampon. Y a-t-il un moyen propre de le faire?C# entrée et sortie simultanées de la console?

Merci.

+0

Je ne connais aucun moyen facile, non.J'ai vu des produits terriblement matures (gdb, windbg) ne pas déranger avec cela, bien qu'ils ne jettent généralement pas d'informations asynchrones à l'écran. Quel type d'interaction recherchez-vous? Avez-vous compris comment vous pensez que la console devrait se comporter? –

+0

Ce n'est pas un concept complexe vraiment, tout ce que je veux, c'est que l'utilisateur puisse saisir du texte, l'invite étant après la dernière ligne écrite. Si une autre ligne est écrite pendant que l'utilisateur tape, l'invite est redessinée après cette ligne. –

Répondre

4

J'ai commencé à travailler sur un programme de test pour montrer comment vous pourriez diviser la console en une zone de sortie et une zone d'entrée, où la zone d'entrée est déplacée vers le bas. Il est pas encore parfait, mais vous pouvez être en mesure de le développer dans la réponse que vous cherchez:

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

Merci beaucoup, je vais travailler avec ça! –

0

Avez-vous essayé d'appeler OpenStandardInput, en lisant toute entrée et en réinitialisant sa position, puis en écrivant dans le flux de sortie. Ensuite, vous pouvez appeler à nouveau OpenStandardInput et réintégrer les données dans le flux.

+0

J'ai juste essayé mais j'ai peur que je ne sois pas vraiment sur comment le faire ... J'ai juste essayé et il a jeté une exception "Stream ne supporte pas la recherche.". –

1

Si vous devez autoriser la sortie pendant que l'utilisateur tape, je recommande d'envoyer la sortie vers une nouvelle fenêtre. Ainsi, vous pouvez avoir une fenêtre qui est utilisée pour démarrer l'application, puis elle génère un thread pour ouvrir une nouvelle console pour l'entrée, puis continue à envoyer les messages de sortie à la fenêtre d'origine. Je pense que vous allez rencontrer trop de problèmes de verrouillage de ressources si vous essayez de tout garder dans la même fenêtre.

+0

+1. Quoi qu'il en soit, comment le pauvre utilisateur va-t-il faire la différence entre la sortie causée par sa frappe et la sortie causée par un processus d'arrière-plan farfelu qui a couru juste après avoir appuyé sur Entrée? – MarkJ

0

Il n'y a pas de façon parfaite d'accomplir cela, je pense. Ce que fait telnet (au moins la dernière version que j'ai utilisée) n'était pas imprimer n'importe quelle entrée (il suffit de lire les frappes) et simplement imprimer la sortie quand elle arrive. L'alternative consiste à stocker toutes les données devant être envoyées à la console dans un tampon, et de ne les imprimer qu'une fois que l'utilisateur a fini d'entrer sa commande. (Vous pouvez même horodater la sortie, pour le rendre plus évident.) Je ne vois vraiment pas de meilleure alternative ici - vous allez inévitablement rencontrer des problèmes en utilisant une interface d'E/S synchrone (c'est-à-dire la ligne de commande) avec opérations asynchrones dans le backend.

1

Ce genre de chose devient un problème un peu plus simple si vous traitez le serveur comme une application client/serveur. Laissez le serveur avoir "n" connexions aux applications d'administration client qui envoient des commandes et reçoivent la sortie. L'application cliente peut séparer complètement les entrées et les sorties, ayant un thread pour gérer l'entrée, et un pour gérer la sortie.

Le thread de sortie peut être bloqué si le thread d'entrée est en train d'entrer une ligne et de se débloquer lorsque la ligne est annulée ou validée.

2

Personnellement, j'utiliser des gestionnaires d'événements pour géré une console qui gère à la fois l'entrée et outup en même temps, créer une classe ScreenManager ou quoi que ce soit, à l'intérieur de cette classe, ajoutez un objet RunProgram() mthod, créez un événement avec handler et les variables requises pour lire la clé d'entrée "Console.ReadKey (bool) .key".

static Consolekey newKey; 

sur votre programme principal, CREAT une instance de votre classe "whatev vous l'avez appelé", puis créer un fil de cette méthode des instances internes, fil coreThread = new Thread (délégué() {myinstance.myProgramMrthod() });

boucle dans votre main jusqu'à ce que les discussions sont en cours. while (! Thread.IsAlive);

puis créer la boucle du programme principal. Ensuite, pour safty, joignez votre thread personnalisé afin que le programme principal ne continue pas jusqu'à ce que le thread personnalisé est fermé/éliminé.

customThread.Join(); 

vous avez maintenant deux threads en cours d'exécution.

retour à votre classe, créez un commutateur dans votre méthode de gestionnaire d'événements.

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; 
} 

coller votre logique avec la façon dont vous voulez gérer les clés. How to use multiple modifier keys in C# peut être utile. Dans la méthode de votre instance RunProgram() ou que vous choisissiez de l'appeler, une fois que vous avez effectué le code nécessaire, créez une boucle infinie pour vérifier le changement de clé.

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

cette boucle stocke toute pression et appuyer vérifie ensuite s'il y a une nouvelle clé, si vrai, déclenche l'événement.

vous avez maintenant le cœur de ce que vous cherchez, une chaîne qui demande une nouvelle clé, tandis que la boucle principale est libre pour afficher le texte que vous souhaitez afficher.

deux bugs réparables avec ce que je peux penser, l'un est "par défaut" à l'intérieur du commutateur imprimera à la console dans des chapeaux ou des chaînes. et l'autre est tout texte ajouté à la console est ajouté au point du curseur afin qu'il ajoute au texte que l'utilisateur vient d'entrer.

hwoever je vais, depuis que je viens de le faire, comment vous devez manager le texte a été ajouté à la console. encore Im en utilisant un événement. Je pourrais utiliser des méthodes et des fonctions tout au long, mais les événements ajoutent la flexibilité de mouvement au programme, je pense. Ok, donc nous voulons être en mesure d'ajouter du texte à la console, sans perturber l'entrée que nous entrons. garder l'entrée au fond;

créer un nouveau délégué ayant une propriété avec un argument de chaîne, void delegate myDelegate (chaîne Arg). puis créez un événement avec ce délégué, appelez-le newline, newinput, whatev que vous aimez. Le gestionnaire d'événements prendra un argument de chaîne (en repérant le texte de mise à jour de la console: ce que vous voulez insérer dans la console au-dessus de l'entrée des utilisateurs) il saisira le texte que l'utilisateur a entré dans la console, imprimez la chaîne de paramétrage sur la console, puis imprimez le sous-texte d'entrée des utilisateurs. Personnellement, j'ai choisi de créer une chaîne statique en haut à l'extérieur de la méthode, de l'initialiser pour la vider, car elle va être fréquemment utilisée et vous ne voulez pas créer un nouvel identifiant, puis initialiser la variable à chaque fois que la méthode est appelé, puis en disposer à la fin de la méthode, seulement pour en recréer un nouveau, et encore.

appelle la chaîne "entrée" ou autre.

Dans la zone par défaut de la poignée d'événement keychange, ajoutez l'entrée + = newkey.

dans la console Consolekey.enter section console writline input puis input = string.empty Ou string = "".

dans le gestionnaire d'événements ajouter une certaine logique.

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 est un const = 0; – Gorlykio

Questions connexes