tags:

views:

1175

answers:

5

Hey guys,

I am writing a server app and I want it to be console based. I need the user to be able to input different commands, but at the same time there is a possibility that something will be output to the console while the user is writing. This messes the buffer up. Is there any clean way of doing this?

Thanks.

A: 

Have you tried calling OpenStandardInput, reading any input and resetting it's position, then writing to the output stream. Afterwards, you can call OpenStandardInput again and fill the data back into the stream.

Richard Szalay
I just tried but I'm affraid I'm not really sure how to do it... I just tried and it threw an exception "Stream does not support seeking.".
Xeon06
A: 

If you need to allow output to arrive while the user is typing I recommend sending the output to a new window. So, you could have one window that is used to start the application and then it spawns a thread to open a new console for input and then it continues to send any output messages to the original window. I think you will run in to too many resource locking issues if you try to keep everything in the same window.

Lucas B
A: 

There's no perfect way of accomplishing this, I think. What telnet does (at least the last version I used) was not print any input (just read the keystrokes) and simply print the output as it arrives. The alternative is to store any data that needs to be output to the console in a buffer, and only print it once the user has finished entering their command. (You could even timestamp the output, to make it more obvious.) I really can't see any better alternative here - you're inevitably going to run into problems using a synchronous I/O interface (i.e. the command line) together with asynchronous operations in the backend.

Noldorin
+1  A: 

This sort of thing becomes a somewhat simpler problem if you treat the server as a client/server application. Let the server have "n" connections to client admin applications that send commands and receive output. The client application could completely separate input and output, having one thread to handle input, and one to handle output.

The output thread could block if the input thread is in the middle of entering a line, and unblock when the line is either cancelled or committed.

John Saunders
+2  A: 

I started work on a test program to show how you could divide the console into an output area and an input area, where the input area is moved down as the output area expands with more output. It's not perfect yet, but you may be able to develop it into the answer you're looking for:

  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;
  }
BlueMonkMN
Thanks a lot, I'll work with that!
Xeon06