We usually think of Visual Basic as a tool for writing graphical user interface applications. Most of the time this is precisely what we want. Sometimes though it can be handy to make an old-style console application though.
This discussion doesn't cover communicating with a shelled external program. It describes a way to create a VB program that is meant to be run from the command prompt and communicates with the user via console I/O operations.
There is a lot about this elsewhere of course, but since I want to build on it elsewhere in another topic I thought it might be worth presenting the information here.
Console programs have a few standard characteristics:
Many of you are quite familiar with those characteristics from your MS-DOS, Unix, or other command line work. The same rules hold true from a console program written in VB 6 (or for that matter 5).
If you haven't tried creating true console programs in VB you may not have encountered this one before. The VB compiler normally produces a standard EXE project's executable as a Windows subsystem program.
To avoid the undesired effects this causes in a command line program, we'll need to post-process the EXE created when we do a VB compile. The easiest way is to edit the EXE by using the LINK.EXE utility that comes with VB.
Since we're creating a console program, we can't really use VB forms. Since we will be writing most code in standard code modules instead, for the most part we can't use VB events either then.
While there are exceptions to this, from here I am making the assumption we're writing straight-line code that never pauses except on blocking operations like simple I/O, Sleep() API calls, or blocking method calls on ActiveX objects.
This is one of the easiest things we have to handle. Easy, because VB happens to already have this mechanism in place. Just call the Command$() function:
This gives us a simple form of command line parsing that assumes the parameters are simple values separated by spaces. Obviously one can perform more complex parsing on the string returned by Command$() as well.
In most cases a VB program will just exit out of the bottom of Sub Main() and let the return code default to zero. Sometimes though it is useful to return other values, typically when the program is run from a .BAT or .CMD file, or executed by another program or a WSH script.
Returning other values can be done through a simple Win32 API call: ExitProcess(uExitCode) .
This will of course immediately terminate your program, so use it carefully. The value to be returned is an unsigned Long.
The console streams are the input and output (normally text) streams that are used to communicate with the outside world. In most cases this is the console window that is executing your program.
There is one for input, and two for output. The reason for two outputs is primarily to permit the programs normal output to be redirected to a file or piped to the input of a follow-on program, while allowing errors to still be reported back to the console.
Performing the console I/O requires the use of several API calls. One alternative is to use capabilities provided by the FileSystemObject ActiveX object, that is part of scrrun.dll, the Microsoft Scripting Runtime.
I have written a simple VB code module called modConIO that defines many of the API calls, constants, and so forth that will make simple synchronous console I/O easier. This module makes several Public items available:
The Public routines here are wrappers for the raw I/O APIs.
You call SetHandles() once before doing any console I/O. Use the handle variables in your calls to ConWrite() and ConWriteLine(). Calls to the read functions use hStdIn internally.
The four I/O functions here return a success or error result value.
The BytesWritten parameters return the number of bytes actually written, though for console I/O these results can typically be ignored.
The Bytes parameters are used to request a certain number of bytes of input, though typically you ask for more than you actually get back. ConRead() will return once an end-of-line is encountered or when Bytes of input have been received. ConReadLine() will request input in Bytes-sized chunks until it encounters an end-of-line.
The API calls for Sleep() and ExitProcess() are Public in this module for convenience. Sleep() gets used by ConReadLine() too, but ExitProcess() is meant to be called from your own code.
You can download this module at modConIO.
Here are a couple of very simple programs, and their operation should be quite clear:
That's a pretty simple program, I'm sure you'll admit.
Another incredibly trivial program. This one reads lines of input until EOF. To signal EOF from the console the easiest method is to simply type CTRL-Z and press ENTER.
One reason for this example is to demonstrate redirection. I created a small junk.txt file and then entered:
This produced a simple line count as a result, written back to the console.
This part isn't too bad. Even easier if you use one of the 3rd party VB IDE add-ins that "hooks" the compilation process to keep VB from compiling your project as a Windows subsystem program.
Most of us don't have one of these though. So what do we do?
Well, the simplest thing is to compile your program as an EXE via the usual File|Make menu selection in the VB IDE. Then you need to open a command prompt window, set the approriate PATHs to reach LINK.EXE and your compiled EXE, and then enter:
LINK /EDIT /SUBSYSTEM:CONSOLE MYNEW.EXE
I got tired of doing this, especially since I usually ended up typing a long pathname to reach either LINK.EXE, my compiled VB program, or both.
Instead I wrote a small VBScript that I just copy into the project folder of any VB console project I work on.
Option Explicit 'LinkConsole.vbs ' 'This is a WSH script used to make it easier to edit 'a compiled VB6 EXE using LINK.EXE to create a console 'mode program. ' 'Drag the EXE's icon onto the icon for this file, or 'execute it from a command prompt as in: ' ' LinkConsole.vbs
' 'Be sure to set up strLINK to match your VB6 installation. Dim strLINK, strEXE, WSHShell strLINK = _ """C:\Program Files\Microsoft Visual Studio\VB98\LINK.EXE""" strEXE = """" & WScript.Arguments(0) & """" Set WSHShell = CreateObject("WScript.Shell") WSHShell.Run _ strLINK & " /EDIT /SUBSYSTEM:CONSOLE " & strEXE Set WSHShell = Nothing WScript.Echo "Complete!"
This can be run from the command line using CScript just fine. I usually just drag and drop, to handle this job quickly. I typically:
Voila! All ready to run.
Just don't forget to alter this script to point to the location where you keep your copy of the LINK.EXE utility.
You can download this script at LinkConsole.
Lots of options here. The simple ones include such things as making a form that simulates the console and detecting whether or not execution is stand-alone or via the IDE's Run option.
Other people get a little more tricky, and open a console window from inside of the program and direct their I/Os there when run via the IDE.
Karl Peterson offers an approach over at Karl E. Peterson's Classic VB Code. Heck, Karl pretty much obsoletes most of what I've covered here with his downloadable goodies.