Writing Console-Mode Applications in Visual Basic

Tuesday Nov 19th 2002 by Scott Lloyd
Share:

Writing Console-Mode Applications in Visual Basic

Visual Basic has always been there for those of us that have wanted to write windowed applications. What a lot of people don't know is that VB can also be used to write windowless applications, where the application has no standard user interface of it's own. This is all fine but what about the good old console? Sometimes it is the best way to provide a simple interface to a user. It doesn't use up much memory and console applications tend to be smaller than windowed applications. Most people think it is impossible to write a console-based application in VB because there isn't a project for it in the New Project dialog but I'm going to show you how it can indeed be done.


Blow Up!

A console-based program in Visual Basic starts off with a windowless base. This is because since there is no built-in support by VB for console work, we have to write all of the low-level stuff by hand. So, start a new Standard EXE project and remove the default form (Form1) from the project. Add a module and you're set. That one module is the only component that is needed in a console project, just like a windowless project.

Visual Basic, as was mentioned earlier, has no built-in support for console work of any kind. This means that all of our interfacing with the console will be done through calls to the Win32 API. Our application has no attachment to the console box. It's not a resource of any kind, it's a process that we request the operating system to allocate for us. This is done by a call to the API function AllocConsole, whose prototype is as follows:

Private Declare Function _
AllocConsole Lib "kernel32" () As Long

This will create a console window in which we can print and receive input. This console window - this is important to remember - is a process controlled by the operating system. Think of all operations that we perform on it as "requests" (and these requests can be denied). Since it is a process, it must always be destroyed when we are done with it, or before the application terminates. We destroy the console process with the FreeConsole function.

Private Declare Function _
FreeConsole Lib "kernel32" () As Long

With this done, one can already write a program that brings up a console box and closes it:

Private Declare Function _
AllocConsole Lib "kernel32" () As Long

Private Declare Function _
FreeConsole Lib "kernel32" () As Long

Private Sub Main()
AllocConsole
FreeConsole
End Sub

Before we can call any other console functions, we'll need a handle to the console. Handles to the console window are just like handles to standard windows. When you use an API call to interact with a form window, you'll almost always have to provide the form's hWnd. When you use an API call to interact with a console window, you'll now have to provide one of its three handles--that's right, three. A console window has an input, output, and error handle. This is because each can be redirected, but that doesn't matter to us. We retrieve a handle with a call to GetStdHandle.

Private Declare Function _
GetStdHandle Lib "kernel32" (ByVal _
nStdHandle As Long) As Long

The argument nStdHandle determines which handle it returns. The mnemonic constants have three possible values to pass are:

Private Const STD_INPUT_HANDLE = -10&
Private Const STD_OUTPUT_HANDLE = -11&
Private Const STD_ERROR_HANDLE = -12&

Writing to the Console

So far you have learned how to create a console process and how to get a reference to it but you haven't learned anything terribly practical. To perform output to the console you must, of course, first have a handle to the output buffer. There are a few ways to print to the console, the most simplistic of them all being WriteConsole.

IMPORTANT NOTE: The Microsoft API Text Viewer declaration is incorrect. lpBuffer must be passed by value, even though it is of the long pointer type. This is because of a technical issue with the way strings are passed and stored in C.

Private Declare Function WriteConsole _
    Lib "kernel32" _
    Alias "WriteConsoleA" _
    (ByVal hConsoleOutput As Long, _
    ByVal lpBuffer As Any, 
    ByVal nNumberOfCharsToWrite As Long, _
    lpNumberOfCharsWritten As Long, _
    lpReserved As Any) As Long

This function is slightly more complicated than the previous ones. Here, the first argument is the console's output handle. This is obtained by passing STD_OUTPUT_HANDLE to GetStdHandle. The second argument is set to the length of lpBuffer. The third argument is the string buffer. The third argument is a pointer to a variable that will be set to the number of charactors that were actually written. The fourth and final argument is always vbNull. This may all seem complicated, but the example should set you straight.

The console's title may not always be automatically set to what you would like it to be. This is easily remedied by a call to SetConsoleTitle. If you do not, it is set to the name of the process that created the console window. If you are in the Visual Basic, it sets to "VB6", which almost certainly won't do.

Private Declare Function _
SetConsoleTitle Lib "kernel32" Alias _
"SetConsoleTitleA" (ByVal lpConsoleTitle _
As String) As Long

This is fairly self-explanatory. Notice that this function is one of the very few that don't require you to pass a handle as an argument. All that is passed in this function is a string that contains the new title for the console window.

Making Your Output More Colorful

If you were to just call the WriteConsole function, your output would always appear in the same bland gray text. You can change the color of the text printed by subsequent calls to WriteOutput by calling the SetConsoleTextAttribute function.

Private Declare Function _
SetConsoleTextAttribute Lib "kernel32" _
(ByVal hConsoleOutput As Long, _
ByVal wAttributes As Long) As Long

' These values are passed in the wAttributes
' argument
Private Const FOREGROUND_BLUE = &H1
Private Const FOREGROUND_GREEN = &H2
Private Const FOREGROUND_RED = &H4
Private Const FOREGROUND_INTENSITY = &H8
Private Const BACKGROUND_BLUE = &H10
Private Const BACKGROUND_GREEN = &H20
Private Const BACKGROUND_RED = &H40
Private Const BACKGROUND_INTENSITY = &H80

This is a simple function. It takes the console window's output handle and one or more color values. You can combine these values by using Visual Basic's bitwise Or keyword. For example, to create a yellow value, you would define the following constant after the ones listed above.

Private Const FOREGROUND_YELLOW = _
    FOREGROUND_RED _
Or FOREGROUND_GREEN

Other possible colors include cyan, magenta, black, dark-gray, and light-gray (default). Experiment with different combinations using the bitwise Or operator.

There is little point in writing a program that provides no means by which the user can interface with it. For simple input retrieval, the ReadConsole function can be used. This function is a blocking function, in other words it doesn't return until it has something to return. In the case of our example, which is in the final section of this article, the ReadConsole function will not return until the user presses enter.

Below are the declarations of two of the functions that we will need. First we must set the console's input buffer mode. This is done by a call to SetConsoleMode where we pass the input handle and a flag indicating which types of input will be recorded. Note that in the example we will be passing ENABLE_ECHO_INPUT to SetConsoleMode. To actually receive the user's input, we will be using ReadConsole. It is important to remember to use fixed-length strings when using this function. A good practice here is to fix the string to a length of 256 charactors. How to do this will be shown in the example at the end of the article.

IMPORTANT NOTE: Yet again, the Microsoft API Text Viewer has incorrect declarations. In the ReadConsole declaration, lpBuffer is a string passed by value, not an any-type reference pass.

Private Declare Function _
SetConsoleMode Lib "kernel32" _
(ByVal hConsoleOutput As Long, _
dwMode As Long) As Long

Private Declare Function _
ReadConsole Lib "kernel32" Alias _
"ReadConsoleA" (ByVal hConsoleInput As Long, _
ByVal lpBuffer As String, ByVal _
nNumberOfCharsToRead As Long, _
lpNumberOfCharsRead As Long, _
lpReserved As Any) As Long

'For SetConsoleMode (input)
Private Const ENABLE_LINE_INPUT = &H2
Private Const ENABLE_ECHO_INPUT = &H4
Private Const ENABLE_MOUSE_INPUT = &H10
Private Const ENABLE_PROCESSED_INPUT = &H1
Private Const ENABLE_WINDOW_INPUT = &H8
'For SetConsoleMode (output)
Private Const ENABLE_PROCESSED_OUTPUT = &H1
Private Const ENABLE_WRAP_AT_EOL_OUTPUT = &H2

You don't always have to use SetConsoleMode before you receive input, but it is a good idea. If you start delving into the deaper and uglier depths of programming console apps, you'll need it.

To set everything straight, here is the example program. This program will print a prompt asking a user to enter a name. After a user enters his name the program prints a greeting and waits until the user is ready to quit.

'+================================================
' File: Console.bas
'
' Summary: Demonstrates how it is possible to
' create and manipulate a console box in Visual
' Basic.
'
' Classes: None
'
' Functions: Main, ConsolePrint, ConsoleRead
'
' Origin: Visual Basic 6.0 For Windows
'
'------------------------------------------------------
' (C) Copyright 1999 by Scott Lloyd. All Rights
' Reserved.
' Scott Lloyd 
'=====================================================+

Option Explicit
'''''D E C L A R A T I O N S'''''''''''''''''''''''''''

Private Declare Function AllocConsole Lib "kernel32" _
    () As Long

Private Declare Function FreeConsole Lib "kernel32" _
    () As Long

Private Declare Function GetStdHandle Lib "kernel32" _
(ByVal nStdHandle As Long) As Long

Private Declare Function ReadConsole Lib "kernel32" _
    Alias "ReadConsoleA" (ByVal hConsoleInput As Long, _
    ByVal lpBuffer As String, ByVal nNumberOfCharsToRead _
    As Long, _
lpNumberOfCharsRead As Long, lpReserved As Any) As Long

Private Declare Function SetConsoleMode Lib "kernel32" (ByVal _
hConsoleOutput As Long, dwMode As Long) As Long

Private Declare Function SetConsoleTextAttribute Lib _
"kernel32" (ByVal hConsoleOutput As Long, ByVal _
wAttributes As Long) As Long

Private Declare Function SetConsoleTitle Lib "kernel32" Alias _
"SetConsoleTitleA" (ByVal lpConsoleTitle As String) As Long

Private Declare Function WriteConsole Lib "kernel32" Alias _
"WriteConsoleA" (ByVal hConsoleOutput As Long, _
ByVal lpBuffer As Any, ByVal nNumberOfCharsToWrite As Long, _
lpNumberOfCharsWritten As Long, lpReserved As Any) As Long

''''C O N S T A N T S'''''''''''''''''''''''''''''''''''''
'I/O handlers for the console window. These are much like
'the hWnd handlers to form windows.

Private Const STD_INPUT_HANDLE = -10&
Private Const STD_OUTPUT_HANDLE = -11&
Private Const STD_ERROR_HANDLE = -12&

'Color values for SetConsoleTextAttribute.
Private Const FOREGROUND_BLUE = &H1
Private Const FOREGROUND_GREEN = &H2
Private Const FOREGROUND_RED = &H4
Private Const FOREGROUND_INTENSITY = &H8
Private Const BACKGROUND_BLUE = &H10
Private Const BACKGROUND_GREEN = &H20
Private Const BACKGROUND_RED = &H40
Private Const BACKGROUND_INTENSITY = &H80

'For SetConsoleMode (input)
Private Const ENABLE_LINE_INPUT = &H2
Private Const ENABLE_ECHO_INPUT = &H4
Private Const ENABLE_MOUSE_INPUT = &H10
Private Const ENABLE_PROCESSED_INPUT = &H1
Private Const ENABLE_WINDOW_INPUT = &H8
'For SetConsoleMode (output)
Private Const ENABLE_PROCESSED_OUTPUT = &H1
Private Const ENABLE_WRAP_AT_EOL_OUTPUT = &H2

'''''G L O B A L S'''''''''''''''''''''''''''''''''''
Private hConsoleIn As Long ' The console's input handle
Private hConsoleOut As Long ' The console's output handle
Private hConsoleErr As Long ' The console's error handle

'''''M A I N'''''''''''''''''''''''''''''''''''''''''
Private Sub Main()
Dim szUserInput As String

AllocConsole ' Create a console instance

SetConsoleTitle "VB Console Example" 'Set the title on
                                     'the console window

'Get the console's handle
hConsoleIn = GetStdHandle(STD_INPUT_HANDLE)
hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE)
hConsoleErr = GetStdHandle(STD_ERROR_HANDLE)

'Print the prompt to the user. Use the vbCrLf to get
'to a new line.
SetConsoleTextAttribute hConsoleOut, _
FOREGROUND_RED Or FOREGROUND_GREEN _
Or FOREGROUND_BLUE Or FOREGROUND_INTENSITY _
Or BACKGROUND_BLUE

ConsolePrint "VB Console Example" & vbCrLf
SetConsoleTextAttribute hConsoleOut, _
FOREGROUND_RED Or FOREGROUND_GREEN _
Or FOREGROUND_BLUE
ConsolePrint "Enter your name--> "

'Get the user's name
szUserInput = ConsoleRead()
If Not szUserInput = vbNullString Then
ConsolePrint "Hello, " & szUserInput & "!" & vbCrLf
Else
ConsolePrint "Hello, whoever you are!" & vbCrLf
End If

'End the program
ConsolePrint "Press enter to exit"
Call ConsoleRead

FreeConsole ' Destroy the console
End Sub

'''''F U N C T I O N S''''''''''''''''''''''''''''''''''

'F+F+++++++++++++++++++++++++++++++++++++++++++++++++++
' Function: ConsolePrint
'
' Summary: Prints the output of a string
'
' Args: String ConsolePrint
' The string to be printed to the console's ouput buffer.
'
' Returns: None
'
'-----------------------------------------------------

Private Sub ConsolePrint(szOut As String)
WriteConsole hConsoleOut, szOut, Len(szOut), vbNull, vbNull
End Sub

'F+F++++++++++++++++++++++++++++++++++++++++++++++++++++
' Function: ConsoleRead
'
' Summary: Gets a line of input from the user.
'
' Args: None
'
' Returns: String ConsoleRead
' The line of input from the user.
'---------------------------------------------------F-F

Private Function ConsoleRead() As String
Dim sUserInput As String * 256
Call ReadConsole(hConsoleIn, sUserInput, _
    Len(sUserInput), vbNull, vbNull)
'Trim off the NULL charactors and the CRLF.
ConsoleRead = Left$(sUserInput, InStr(sUserInput, _
    Chr$(0)) - 3)
End Function

That about does it. The code is well documented so there isn't much to be explained. If you have any questions or comments, you can contact me at scott@talker.com.

It is possible to retrieve mouse events, set window sizes, and more but that goes beyond the scope of this document. Some of these things are difficult to do in Visual Basic, such as mouse event handling because of VB's limited support for pointers and advanced structure functionality such as unions. With a little work and writing some of the functionality in a C++ DLL, it is possible to make a great console program.

For more information, check the Console Functions page in the MSDN Library section, under the Reference section of the Platform SDK.

© Copyright 1999 by Scott Lloyd. All Rights Reserved. Scott Lloyd (scott@talker.com)

Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved