Showing Progress in a Console Windows

Recently I needed to show some progress indicator on some long running console application.  I recall the good old days of my college days with console based SMTP clients such as elm and pine… as I recall these clients showed progress via a simple ASCII spiner.    


I was impressed with how simple this is to do with .NET Framework 2.0.. I started off writing the usage code I wanted to enable, then built a simple class that meet those requirements.. I highly recommend this process especially for more complex designs.
Here is the client code: 


        static void Main(string[] args)
        {
            ConsoleSpiner spin 
= new ConsoleSpiner()
;
            
Console.Write("Working....")
;
            while 
(true

            {
                spin.Turn()
;
            
}
        }


And here is the class I came up with. 

    public class ConsoleSpiner
    {
        
int counter
;
        public 
ConsoleSpiner()
        {
            counter 
0
;
        
}
        
public void 
Turn()
        {
            counter++
;        
            switch 
(counter % 4
)
            {
                
case 0: Console.Write("/")
; break;
                case 
1: Console.Write("-")
; break;
                case 
2: Console.Write("\\")
; break;
                case 
3: Console.Write("-")
; break;
            
}
            Console.SetCursorPosition(Console.CursorLeft - 
1, Console.CursorTop)
;
        
}
    }


PS – does anyone still use elm\pine?  Are their clients that work with exchange?  Also, I was always told that pine was an acronym for “Pine Is Not Elm”, but accounting to the official site it is not. 

Published 11 June 05 04:41 by BradA

Comments

# Darren Neimke said on June 11, 2005 5:26 PM:
> I was always told that pine was an
> acronym for “Pine Is Not Elm”,

That's one of those nasty chicken and egg things that can lead to nasty, recursive stack-overflow ;-)

PINE
Pine is not Elm
(Pine is not Elm) is not Elm
(Pine is not Elm (Pine is not Elm) is not Elm) is not Elm

etc...
# David Smith said on June 11, 2005 5:57 PM:
WOW! Look at that spinner spin!
# Slim Shady said on June 11, 2005 6:48 PM:
I'm not buying this one Brad.

You need to make time the spinner based on milliseconds since last turn... you can't just turn it on every call. It's like an old videogame running as fast as it can instead of using the clock to make sure it runs at the same speed on any CPU.

I would fix it for you but I am too busy.
# LarryOsterman said on June 11, 2005 8:33 PM:
Pine is still used (and developed) at the UW.

It's still the premier IMAP client out there (it does IMAP better than all the others IMHO).

It's a bit klunky but it DOES show what the protocol can do.
# LarryOsterman said on June 11, 2005 8:36 PM:
Btw, from the project history:
With Pine 3.90, significant new functionality has been added, notably aggregate operations for manipulating groups of messages at once, the first (alpha) release of PC-Pine for the Winsock network interface standard, and greatly improved Usenet (News) support. One of the early interpretations of the name "Pine" was "Pine Is No-longer Elm"; today a "Program for Internet News and Email" seems more apropos. Laurence Lundblade (Pine author emeritus) has more insight into the issue of what "Pine" really stands for.


So Pine DOES stand for "Pine is not Elm". Just like FINE is "FINE is not Emacs" etc.
# edn said on June 11, 2005 9:09 PM:
<nitpick>
Any reason to use a switch() statement when a string/char array would allow you to easily change the animation to anything you want while also allowing you to reduce that statement to a single line?

string [] frame = new string [] = {
"/", "-", "\\", "-" };

Console.WriteLine (frame[counter % animFrame.Length]);
Console.SetCursorPosition(Console.CursorLeft - frame[counter % animFrame.Length].Length, Console.CursorTop);
</nitpick>
# Leonardo Constantino said on June 11, 2005 10:21 PM:
I have a question about your style. I used to initialize every variable on the declaration. Every single one. Until the day I adopted FXCop and it suggested not initializing a variable to its default value. Can you elaborate on this rule?

Btw, my version (also since college days) is a little more 'ANSI', I would use 'Console.Write("\b");' instead of SetCursorPosition-fancy-thing :)

--Leo
# S said on June 12, 2005 12:29 AM:
BTW I thing the characters |/-\ make a better spinner than /-\-.
# Andreas Haeber said on June 12, 2005 4:14 AM:
I used pine last session at a university :) Don't know if it works with Exchange, I assume they used some *NIX-mailserver there.
# Judah Himango said on June 12, 2005 3:10 PM:
For the record, running this code in the debugger causes an IOException to be thrown...maybe due to VS trapping the output of the app in its own console?
# BradA said on June 12, 2005 7:33 PM:
Judah, sorry about the exception you are seeing… This is actually a result of a “feature” of VS called Quick Console which is intended to replace the system console allowing for a better developer experience dealing with console output. Unfortunately, Quick Console doesn’t support many console features (such as SetCursorPosition, ConsoleColor, etc). Regrettably, it fails in a difficult to understand way… you get an IOException which could lead developers to believe it is a bug in their code when it is really a bug.. ahh, I mean feature of VS.

As such, it is my advice that you turn off this “feature” on all console applications… Luckily it is pretty easy to do:
Tools->Options->Debugging->General – “Redirect all console output to Quick Console window”
# Matt said on June 12, 2005 7:49 PM:
Brad,

Small spelling correction: "Spiner" should be "spinner"

Personally, I prefer my spinning characters to be: |, /, -, \, |, /, -, \. It's a bit smoother :)

HTH.
# mihailik said on June 13, 2005 5:05 AM:
This Whidbey SetCursorPosition stuff is not implemented in .NET 1.x.

To make progress version-independent I use "\r" char. Pseudo-code:

Console.Write("\rWorking: " + <indicator> );
# mihailik said on June 13, 2005 5:11 AM:
And the most clean and user-friendly case is:

DateTime nextDump = DateTime.Now;
for( ... )
{
// working

if( DateTime.Now>=nextDump )
{
Console.Write("\rFormatting: "+percent+"%...");
nextDump = DateTime.Now.AddSecond(0.2);
}
}

Spinner is not great, user likes to see what amount of work done. Also, you should not update console too fast, because it is exceptionally slow operation.
# Jeremy Wiebe said on June 13, 2005 8:03 AM:
"I started off writing the usage code I wanted to enable, then built a simple class that meet those requirements.".... or if you are into the latest buzzwords it's called Test Driven Development. :-)
# Greg Ennis said on June 13, 2005 9:28 AM:
"Tools->Options->Debugging->General – Redirect all console output to Quick Console window"

I don't have this option. Is this only available in VS 2005?
# BradA said on June 13, 2005 9:47 AM:
Yes, Quick Console is a VS2005 feature...
# mihailik said on June 13, 2005 11:16 AM:
It would be better to disable QuickConsole by default, for better backward compatibility.

When I start with new Console Project in VS2005 I often in doubt, where my WriteLines gone?

At least VS team should apply white-on-black colors to this tool window to make it visually equals to standalone console.
# William said on June 13, 2005 12:08 PM:
Thanks Brad. I did this once, but used the Title bar to show the spinner. Then the status does not get mixed up with other ouput and you don't need to use position or backspace, etc. You could also use "|" bars to show a console-progress bar deal. Cheers.
# RichB said on June 13, 2005 3:44 PM:
People use Mutt nowadays and only corporations use Exchange, everyone else uses IMAP for server-side email systems.

Novell's Evolution mail client can connect to Exchange, but it's a GUI application.
# danielfe said on June 13, 2005 9:04 PM:
Nice spinner :)

I blogged about this a while back, but to your question on Pine clients, Andrew Troelsen wrote a C# "Pine" client for an article on Programming Outlook 2003 using C#.

http://blogs.msdn.com/danielfe/archive/2004/06/07/150625.aspx
# vBogey said on June 14, 2005 3:21 PM:
I can only hope no one else will consider this approach usefull.
# aspZone.com said on June 15, 2005 3:17 PM:
I read Brad Abrams post on Showing Progress in a Console Window. I did not like the way he implemented...
# RGabo said on July 9, 2005 8:20 PM:
Brad, why do you do a counter = 0 in the constructor?

I have no problem with explicitly declaring the default constructor (you talked about that in your guidelines ;) but a managed System.Int32 is always 0 when the object is constructed.

So what's the deal? :)
# BradA said on July 10, 2005 11:13 PM:
RBabo -- You are right, not really needed, but the cost is almost zero as well and I wanted it for explicit documentation.. that is make sure the reader understands explicity how I am using this field..
New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker