A Prize Console-ation in VB 2005

Although computer users often view console applications as relics, they are still very much a part of the system administrator's and the power user's toolboxes. They are extremely useful when you need to script highly repetitive, and possibly complex, operations.

Earlier of versions of Visual Basic did not offer much help in creating console apps. VB 6, and earlier versions, could create console apps, but it was not a simple or straightforward process. Creating a console app in VB 6 required making Win32 API calls. Even then, you could not control the cursor position or the color of the text. You were pretty much restricted to writing to the console from left to right and top to bottom.

VB .NET introduced new console app features. It provided not only a console application project in the New Project... dialog, but most importantly it provided a Console object. You no longer had to mess around with the Win32 API. However, you were still mostly restricted to writing to the console left to right and top to bottom. VB .NET lacked the methods or properties for changing the position or color of the cursor or text, as well as additional more advanced features.

New Console App Features in VB 2005

Building on the Console object in those earlier VB .NET versions, VB 2005 added many great features for console applications. The following are just a few of the more exciting capabilities of VB 2005:

  • Clear the console window
  • Get and set cursor position and size
  • Get and set window height and width
  • Get and set foreground and background color
  • Select and move sections of text within the console window

The rest of this article steps through the creation of a console application that demonstrates the use—and usefulness—of these features.

Example Application: DirCopy

I often copy files from one directory to another from a console window. When I'm copying a directory with a large number of files, I often wish that I had some indication as to how close to completion the copy is. Is it nearly done, or should I go get a cup of coffee? To answer that question, I built a console app that displays a progress bar that graphically shows, within the console window, how close the copy is to completion.

Defining the Functionality

To keep things simple for this demonstration, DirCopy will accept two command-line arguments: a source directory and a destination directory. It then will copy all of the files from the source to the destination. It will NOT copy any subdirectories from the source to the destination.

To start, I wrote the DisplayUsage subroutine. If DirCopy is invoked with too many or too few command-line arguments, it will call DisplayUsage and then end. Figure 1 shows the results of running DirCopy without any command-line arguments.

Figure 1: Result of DisplayUsage Subroutine

The following is the code for the DisplayUsage sub:

Private Sub DisplayUsage()
   Dim originalForegroundColor As ConsoleColor =
      Console.ForegroundColor

   Console.Clear()

   Console.ForegroundColor = ConsoleColor.Green

   Console.WriteLine("DirCopy 1.0")
   Console.WriteLine("Written by Josh Fitzgerald")
   Console.WriteLine(New String("-", Console.WindowWidth))
   Console.WriteLine("DirCopy will copy all of the files from the
                      source folder to the")
   Console.WriteLine("destination folder. While the files are
                      copying, a progress bar")
   Console.WriteLine("will display the percent complete.")
   Console.WriteLine()
   Console.WriteLine("If a directory name contains spaces, enclose
                      it in double quotes.")
   Console.WriteLine()
   Console.Write("Example : ")

   Console.ForegroundColor = ConsoleColor.Magenta
   Console.WriteLine("DirCopy C:\MyFolder C:\MyNewFolder")

   Console.ForegroundColor = ConsoleColor.Green
   Console.WriteLine()
   Console.Write("Example : ")

   Console.ForegroundColor = ConsoleColor.Magenta
   Console.WriteLine("DirCopy ""C:\My Folder"" ""C:\My New Folder""")

   Console.ForegroundColor = originalForegroundColor
End Sub

The first thing the subroutine does is create a variable named originalForegroundColor, and then it stores the value of Console.ForegroundColor. At the end of the sub, the code sets the Console.ForegroundColor property back to originalForegroundColor. As you explore the code, you'll notice that it does this in every subroutine that modifies the foreground or background colors. I wanted to make sure that DirCopy always leaves the console with the same colors that were in effect before DirCopy ran.

The next thing DisplayUsage does is clear the console screen by using the Console.Clear method. This is one of the new methods in VB 2005, and it makes it very easy to ensure you have a clear console window.

I wanted to make the help text stand out a little bit, so I set the ForegroundColor property to green. Then, I wrote several lines of text to the screen using the Writeline and Write methods that describe the DirCopy application. At end of the description, I included a couple of examples of how to run DirCopy. I wanted my examples to stand out from the rest of the text, so I set the ForegroundColor to magenta.

The Engine: CopyFiles

The CopyFiles subroutine does most of the work in this application. It is responsible for getting the list of files from the source directory and copying them to the destination directory. It also creates a ConsoleProgressBar object and manages the progress bar:

Private Sub CopyFiles(ByVal srcDir As String, ByVal destDir As String)
   Const BufferSourceTopLine As Integer = 8
   Const BufferDestinationTopLine As Integer = 7

   Dim rowIndex As Integer = 7
   Dim originalForegroundColor As ConsoleColor = Console.ForegroundColor

   Console.CursorVisible = False
   Console.Clear()

   Dim numberOfFiles As Integer
   numberOfFiles = My.Computer.FileSystem.GetFiles(srcDir).Count
   Dim pb As New ConsoleProgressBar(numberOfFiles)

   DisplayHeader(srcDir, destDir)

   Dim fileCounter As Integer = 1
   For Each f As String In My.Computer.FileSystem.GetFiles(srcDir)
      Dim fi As New System.IO.FileInfo(f)
      Console.ForegroundColor = ConsoleColor.Green
      Console.SetCursorPosition(0, rowIndex)
      Console.Write(fi.Name)
      If rowIndex < Console.WindowHeight - 1 Then
         rowIndex += 1
      Else
         Console.MoveBufferArea(0,_
                                BufferSourceTopLine, _
                                Console.WindowWidth, _
                                Console.WindowHeight - _
                                BufferSourceTopLine, _
                                0, _
                                BufferDestinationTopLine)
      End If
      My.Computer.FileSystem.CopyFile(fi.FullName, destDir &"\" & fi.Name)
      pb.Update( fileCounter)
      fileCounter += 1
   Next>

   Console.ForegroundColor = originalForegroundColor
   Console.SetCursorPosition(0, Console.WindowHeight - 1)
     Console.CursorVisible = True
End Sub

Once again, the first thing the code does is save the current ForegroundColor. It then uses another new feature and sets the CursorVisible property to False. After clearing the console window, it retrieves the number of files in the source directory and uses that number as the maximum value for the ConsoleProgressBar constructor. (I'll delve into the details of the ConsoleProgressBar later.)

I called the DisplayHeader subroutine to print some info about the copy operation to the console window. I won't go into detail on it because it is functionally very similar to the DisplayUsage subroutine.

I used a For...Each loop to loop through all of the files in the source directory. I used a rowIndex variable call to keep track of which row in the console to print the file name to. As the loop progresses, rowIndex gets incremented by one each time until it reaches the bottom of the console window. Once I've reached the bottom of the console window, I utilize another new console app feature, the MoveBufferArea method (more about that in the next section).

After updating the display and copying the file, I updated the progress bar by calling the "Update" method of the ConsoleProgressBar class.

Once the loop completed and files were copied, I set the ForegroundColor back to its original color, set the cursor position on the bottom line of the console window, and made the cursor visible again.

Moving Sections of the Console

The MoveBufferArea method allows me to specify a section, or buffer, within in the console window and then specify a set of coordinates to move it to. I wanted the list of file names to appear as a scrolling list, while maintaining the header info and progress bar at the top of the console window. Once I've printed a file name to the last line in the console, I stop updating the rowIndex variable and start using the MoveBufferArea method.

MoveBufferArea takes six parameters:

  • The first parameter specifies the left edge of the area you want to select.
  • The second parameter specifies the top line of your buffer area.
  • The third and fourth parameters specify the width and height of your buffer area.
  • The last two parameters specify the left edge and the top line of the area you want to move your buffer to.
Console.MoveBufferArea(0, _
                       BufferSourceTopLine, _
                       Console.WindowWidth, _
                       Console.WindowHeight - BufferSourceTopLine, _
                       0, _
                       BufferDestinationTopLine)

This code tells the MoveBufferArea method to start at column 0 and line BufferSourceTopLine, which is a constant defined as 8. Then, it sets the buffer area width equal to the width of the console window and sets the buffer area height equal to the difference between the console window height and the top line of the list of file names, BufferSourceTopLine. The last two parameters specify the destination location of my buffer area as column 0 and the constant BufferDestinationTopLine, which is defined as 7.

In simpler terms, the code selects the second file name in the list through the last name and moves that buffer area up one line, effectively removing the top file name and making room at the bottom for the next file name.

A Prize Console-ation in VB 2005

The ConsoleProgressBar Class

I created the ConsoleProgressBar class to handle the details of initializing and updating the progress bar. The constructor takes one parameter, called MaximumValue:

Public Sub New(ByVal MaximumValue As Long)
   m_length          = Console.WindowWidth - 10
   m_left            = 7
   m_right           = m_left + m_length + 1
   m_progressBarRow  = 1
   m_messageBarRow   = m_progressBarRow + 1
   m_percentPosition = 4
   m_maximumValue    = MaximumValue
   m_currentValue    = 0
   Initialize()
End Sub

I hard-coded some of the values and computed others based on the hard-coded values. I made the class simple for this article, but you could extend the constructor to take other values as parameters to make the progress bar more configurable.

After initializing all of the member variables, I call the Initialize method, which in turn calls the InitializePercentComplete, InitializeProgressBar, and InitializeMessageBar methods. These methods don't do a whole lot; they just print a percent sign, a set of brackets to denote the ends of the progress bar, and the minimum and maximum values of the progress bar.

The Progress Bar Progresses

Nothing else happens with the ConsoleProgressBar object until the Update method is invoked:

Public Sub Update(ByVal CurrentValue As Long)
   m_currentValue = CurrentValue
   m_currentBarLength = CInt((m_currentValue / m_maximumValue) _
                              * m_length)
   Refresh()
End Sub

The Update method takes one value as a parameter, which in this case will be the current file number just copied. I set the member variable m_currentValue and then computed m_currentBarLength. The result of the computation gives the number of columns the progress bar should currently cover.

Finally, I call the Refresh method, which in turn calls the UpdatePercentComplete, UpdateProgressBar, and UpdateMessageBar methods.

Because all three methods are similar in function, I will focus on the UpdateProgressBar method:

Private Sub UpdateProgressBar()
   Dim originalForegroundColor As ConsoleColor = _
      Console.ForegroundColor
   Dim originalBackgroundColor As ConsoleColor = _
      Console .BackgroundColor

   Console.ForegroundColor = ConsoleColor.Black
   Console.BackgroundColor = ConsoleColor.Green
   Console.SetCursorPosition(m_left + 1 m_progressBarRow)
   Dim progress As New String("O", m_currentBarLength)
   Console.Write(progress)

   Console.ForegroundColor =originalForegroundColor
   Console.BackgroundColor = originalBackgroundColor
End Sub

Once again, the first thing the code does is save the current foreground and background colors. It then sets the ForegroundColor property to Black and the BackgroundColor property to Green. After positioning the cursor at the left edge of the progress bar, it prints a string of Os with a length of m_currentBarLength.

Running DirCopy

After so much description, you must be eager to see the application run (see Figures 2 and 3) and check out the results (see Figure 4). I've set up a directory with a bunch of dummy files to test copying. I recommend you do the same before you use DirCopy to copy any critical files.

[Josh2.jpg]

Figure 2: Starting DirCopy

[Josh3.jpg]

Figure 3: DirCopy Running

[Josh4.jpg]

Figure 4: DirCopy Finished

What Else?

The DirCopy application, while potentially useful, is not quite production ready. To make it more robust, I would need to add lots more error handling (in other words, the boring, tedious stuff). You also could improve the ConsoleProgressBar class to allow more control. The following are some more possible enhancements:

  • Allow more control of the placement and length of the bar
  • Allow the percent complete area to be placed anywhere the programmer chooses
  • Allow custom messages in the message bar
  • Add an option to choose between a horizontal or vertical progress bar

More to Explore

I hope that this article has been useful in showing you some of the cool new console application features available in VB 2005. Although the features I covered are some of my favorites, I urge you to check the numerous other new console app features to find a few favorites of your own.

About the Author

Josh Fitzgerald is an applications development group leader for a large medical device company in Warsaw, Indiana. Designing and developing Visual Basic .NET applications is only one of his responsibilities, but it is his favorite part of his job. You can reach Josh at josh.fitzgerald@gmail.com.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: April 22, 2014 Database professionals -- whether developers or DBAs -- can often save valuable time by learning to get the most from their new or existing productivity tools. Whether you're responsible for managing database projects, performing database health checks and reporting, analyzing code, or measuring software engineering metrics, it's likely you're not taking advantage of some of the lesser-known features of Toad from Dell. Attend this eSeminar with Dell Software's …

  • Live Event Date: May 7, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT This eSeminar will explore three popular games engines and how they empower developers to create exciting, graphically rich, and high-performance games for Android® on Intel® Architecture. Join us for a deep dive as experts describe the features, tools, and common challenges using Marmalade, App Game Kit, and Havok game engines, as well as a discussion of the pros and cons of each engine and how they fit into your development …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds