How to create a VB6 console program

Visual Basic 6 programs can be run as console programs, if configured correctly. There are four basic requirements to create a useful console program in VB6:

  • Remove all forms and dialogs
  • Provide access to standard input, output, and error streams
  • Provide access to the command-line arguments
  • Re-link the program for the Windows Console subsystem

Remove forms and dialogs

By default, a VB6 project has “forms” or “windows,” which can contain application code. When running a program in the console, you don’t want anything but the console, ever. When you create a VB6 project, just remove all the forms from it, and add a module. You need at least one module, which will contain a subroutine called Main(). When you look at the project properties, you will see the “startup object” set to Sub Main.

There is still a possibility that some dialogs can be created. For example, a runtime error will pop up a dialog. To avoid this, choose the “Unattended Execution” checkbox in the Project Properties dialog. By default, dialogs will now be shunted to the Windows Event Log. You can control this with the App.StartLogging method, if desired.

Get access to stdio streams

A console app usually needs to work with standard input and output. There are at least two ways to accomplish this: by using the Win32 API, and by using the Scripting.FileSystemObject’s text streams. In either case, the streams will not be available when running the app in the debugger, so it may be a good idea to create a wrapper around the calls and only try to use them if they are available. The Win32 API calls are easy to use, and I have posted sample code for your reading pleasure. The Scripting.FileSystemObject’s text streams are equally easy to use. Microsoft’s FileSystemObject documentation should help you get started on those. You will need to add a reference to “Microsoft Scripting Runtime” in your project to use the FileSystemObject.

Get access to command-line arguments

The text of the command-line arguments with which the VB6 console app was invoked is available by calling the Command() function, but it is non-trivial to parse the text into individual arguments such as those C programmers are used to using. It’s not impossible; depending on your needs you may be able to use regular expressions, the Split() function, a tokenizer (finite state machine), or invoke the Win32 API again by calling the CommandLineToArgvW function. The latter uses Unicode, so you will need to convert between VB strings and Unicode. The StrConv() function will help here, but on the reverse conversion you will need to do a bit more. Google will provide many links to examples of using these two functions for this job.

Re-link the program for the Windows Console subsystem

There seems to be no option in the VB project properties or compile options to do this automatically when making the program, so you will need to re-link after compilation. If you don’t do this, your program will not run correctly. The standard streams will not be available, for one thing. Fortunately, it is quite easy to do:

"C:\Program Files\Microsoft Visual Studio\vb98\LINK.EXE" /EDIT /SUBSYSTEM:CONSOLE <yourfile.exe> (this code should all be on one line).

A handy shortcut is to create a batch file with the command in it. You can then drag your .EXE file onto the batch file. Assuming LINK.EXE is in your path, the following will work:

LINK.EXE /EDIT /SUBSYSTEM:CONSOLE %1

Don’t name the batch file “link.bat” or it will call itself! Another of Microsoft’s insecure default behaviors.

Acknowledgements

I have gleaned this code from all over the Internet. Very little of it is my own.

Technorati Tags:No Tags

You might also like:

  1. How to use the Visual SourceSafe automation interface

23 Responses to “How to create a VB6 console program”


  1. 1 Brad

    Wow, an on-point article that is easy to read with a quick answer to the question I’ve got. That, and the suggestions really work, too! Kudos!

    Brad

  2. 2 Marcos

    Excellent article, easy to follow, thank you!

  3. 3 Rob

    Yes - very handy and easy to follow… good job!

  4. 4 Arun

    One of the best useful solution to quite simple but insufficiently documented problems. Thanks.

  5. 5 Robert Tuan

    Yeah, I like this code. Many thanks ;)

  6. 6 Diogo Costa

    Thanks man. That REALLY help me a lot. Thank you very much!!

  7. 7 srosh

    Thanks, that’s just the thing I was looking for.

  8. 8 David

    Thanks, this was exactly what I was looking for. Just one little “glitch” remains. I have an app that I would like to be gui if run without cmd line arguments, but console if the user requests it. So if I link with the console subsystem but run the program without arguments (like dbl-clicking it in explorer), I get a console that pops up and then my app… Any ideas how to avoid this?

    Thanks

  9. 9 Xaprb

    David, I’ve not been programming in the Windows world for a while so I can’t test this, but as I recall I’ve never been able to make any program linked with the console NOT start the console. I don’t know the internals of how it works, though.

  10. 10 David

    Thanks Xaprb. I thought about it some more and couldn’t actually find an example of an app that did what I was asking… guess it doesn’t really matter.

  11. 11 tuanpa

    Nice trick!

  12. 12 Ramin

    Great article.

  13. 13 Craig

    Thanks for the concise article.

    I was hoping to send commands to the console via my VB6 application. It appears that these commands are treated as text, rather than commands. I tried testing this by sending ‘pause’ to console. It simply writes the word pause, rather than executing the Pause command.

    Thoughts?

  14. 14 Xaprb

    I think you can create an object that represents the system shell and tell it to do things. Something like CreateObject(”System.Shell”)? I’m sorry I can’t be more help, as I’ve been away from VB6 and Windows for quite a while. I can’t recall much about it now.

  15. 15 Bram

    In response to Craig:

    Maybe you can use the CallByName function? Unfortunately it’s not possible (as far as I know) to call standard Visual Basic functions using this function, but if you don’t need very much commands, it might be a possibility to create wrapper-functions for each function and call these wrappers using CallByName.

  16. 16 Martin

    To Craig:

    The commands in this example are supposed to display the text on the console only. Most of common “DOS-style” commands are actually handled by a special console application (in Windows it is usually cmd.exe, the exact location is given by the ComSpec environment variable). You would have to execute that application and use your command as a command line parametr, probably together with “/C”:

    C:\WINDOWS\system32\cmd.exe /C pause

    It is possible to execute such command by the VB6 Shell function, but I’m afraid that this function is not aware that your application might be a console application and will actually create a new console for the CMD.EXE. (I didn’t try this, however). In that case you would have to use Windows API to run other console applications (namely CreateProcess and probably WaitForSingleObject) - but that’s a good amount of work. It’s really not worth for “pause” commands, I’d say its not worth for “dir” either.

    However, you can use Shell even in normal VB6 application and run a DOS command as above, only using /K instead of /C on the CMD.EXE command line. This will open a new console, execute the given command (it may even be any other console application, eg. xcopy, or a batch file) and leave the console window open. Until this console is closed (try “EXIT” command), it can be used for entering additional commands by hand.

  17. 17 Martin

    A small update to my previous post:

    Using the Shell function to execute another console application will not create another console, if the current VB application already owns one (as in this case). So principially this will work well. The only caveat is that the Shell function runs the application in paralell to current process. All we need is to wait until the child process terminates. A few API calls will do it. An example follows (note the differences when run as a normal VB application and when run as a console app):

    Option Explicit
    
    Const PROCESS_QUERY_INFORMATION = &H400&
    Const SYNCHRONIZE = &H100000
    Const INFINITE = -1&
    
    Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
    Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
    Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    
    Public Sub ShellWait(PathName As String, WindowStyle As VbAppWinStyle)
        Dim ID As Long, Proc As Long
    
        ID = Shell(PathName, WindowStyle)
        Proc = OpenProcess(SYNCHRONIZE + PROCESS_QUERY_INFORMATION, 0, ID)
        WaitForSingleObject Proc, INFINITE
        CloseHandle Proc
    End Sub
    
    Sub Main()
        ShellWait "CMD.EXE /C dir C:\", vbMinimizedFocus
        ShellWait "CMD.EXE /C pause", vbMinimizedFocus
    End Sub

    I’d also like to thank the author of the original article. It was extremely helpful to me. I was basically aware of all the needed API functions. The only elusive thing was the relink trick.

  18. 18 Karl E. Peterson

    Well, yeah, that’s the general outline, alright. For all the code, ready to roll right into your applications, see my Console application sample and Command Line processing sample. On the first page, there are links to several free addins that eliminate the awkward Link step above, as well. One aspect of the Console sample that folks really appreciate is that it fully supports debugging within the VB IDE.

  19. 19 Timo

    Excellent article. I keep coming back to this link over the years each time I need to do this. Thankyou.

  20. 20 Tony

    Using the Shell function to execute another console application will not create another console, if the current VB application already owns one (as in this case). So principially this will work well. The only caveat is that the Shell function runs the application in paralell to current process. All we need is to wait until the child process terminates. A few API calls will do it. An example follows (note the differences when run as a normal VB application and when run as a console app):Wow, an on-point article that is easy to read with a quick answer to the question I’ve got. That, and the suggestions really work, too! Kudos!

  21. 21 Adam

    Hi
    i am tearing my hair of because i don’t know how to get the return value after that i have executed an exe file from within an VB application. The language used is VB6

    The code that is already written looks as follow:

    ShellId = Shell("C:WINDOWSsystem32cmd.exe /c " & command & " > " & fileName, vbHide)
    ShellHandle = OpenProcess(SYNCHRONIZE, 0, ShellId)

    I wonder how to get the returned value from the executed file if it’s possible to do so. In this example it is netdom.exe that i hope to catch the return value from.

    Many thanks..
    Adam

  22. 22 B0B

    Cool ;)

  23. 23 David Bowen

    Fab - great job.

Leave a Reply

Please do not use this blog to get help with problems or bugs in Maatkit or innotop: use the Sourceforge forums, mailing list, or bug trackers. If you're asking for help with MySQL, please use the MySQL mailing list instead. I'm writing a book and my time is extremely limited :-)