A lot of the programming that I do for myself involves the creation of little tools that patch two existing parts of my workspace together so that they work better. For example, I recently had need of an SFTP client that links into a database front end to retrieve a list of files to download. Since the SFTP specification is ridiculously long and convoluted, I decided to try and use WinSCP to take care of the FTP portion of the program instead of re-inventing the wheel. Because I was trying to patch the database layer and FTP client together into a single application, I decided to take a shot at controlling WinSCP directly from the command line so that the two portions of the project were contained in a single application.
Readers who have spent time fooling around in Visual Basic 6 might remember the Shell function that allows a program to launch another process. Generally speaking, if we don’t care about the output of the program, or are just launching something like a browser window, this command is more than sufficient. However in this case, I wanted to capture the output of WinSCP and its exit code so that I could tell if errors had occurred and take appropriate action on them, so I needed something with a little bit more kick.
Since I wrote my code with WinSCP in mind, the rest of the article is going to focus on creating an application that will serve as an FTP front-end that allows the user to upload or download a file to or from a remote SFTP server. That said, the ideas are general and easy to adapt to any other application that supports command-line arguments.
You’ll want to begin by dragging and dropping the executable that you wish to automate directly into your project, and setting its “Copy to Output” property to “Copy to Newer” to ensure that your application can always find it. For WinSCP, that included both the winscp.com and winscp.exe files from the install directory. That done, let’s jump in.
This handy System class provides a beefier version of the Shell functionality, and is really very easy to use, once you get the hang of it. At the top of your class, add the line
imports System.Diagnostics so that we can find the pieces that we’ll need with ease.
When automating a command-line application, we first create a ProcessStartInfo object that contains all of the information about the process that we would like to create. Start by declaring one of these:
Dim startInfo As New ProcessStartInfo("winscp.com")
This line creates a ProcessStartInfo object with the path to the application that we’d like to automate as it’s only argument. Next, we set a few properties of the object:
startInfo.Arguments = commandLineArguments
startInfo.UseShellExecute = False
startInfo.RedirectStandardOutput = True
startInfo.CreateNoWindow = True
The first line passes the string
commandLineArguments to startInfo, telling it to give them to winscp.com as command line arguments. Command line argument options for most programs can be found on their websites. If you’re interested in WinSCP in particular, it has some great online documentation that you can take a look at. The next three lines tell the startInfo object that it should direct the standard output from the process to a
System.IO.StreamReader object where we can intercept it, and that it should run that process in the background, without showing the window to the user.
Next, we create a new Process object, and pass it the information that we just stored in startInfo:
Dim p as New Process
p = Process.Start(startInfo)
This creates a new process for winscp.com, and passes it the command line arguments that we stored earlier in startInfo. Now, we need to catch the standard output from our newly created process and store it somewhere so that our users can see what the application did.
Dim s as IO.StreamReader = p.StandardOutput
On the first line, we create a new
IO.StreamReader object that will catch the standard output from our hosted process. The while loop tries repeatedly to read a line of output from the application and dump it into a list box while the process is still responding to the operating system. When the application finishes its work,
s.ReadLine will return null, killing the loop and allowing the program to continue.
Finally, when a program exits, it returns an exit code that lets the operating system know whether or not it completed its work successfully. We can access this code with the line
In most programs, if this returns zero, the program finished successfully. If an error was encountered, this function should return something other than zero, and depending on the program, the other value may have some meaning that you can use as a status check.
Because this code is purposely general, it should be easy to adapt it to control most any application that provides some kind of command line interface. This method can make for a nice alternative to batch scripts if you need to write batch scripts with some kind of logic in them and don’t feel like installing a python interpreter to get the job done. At the cost of making the code application-specific, you could easily change it to analyze the output from the program and take some action based on those return values.
As always, the source is available for download. Check out Tyler Burton’s Hash Verifier application if you want to ensure that you’re getting the same copy of the code that I claim you are.
Source Code: Click Here
MD5 Hash: 4B5D543E193CAC2B4B9477727E17EFB2
SHA1 Hash: ABD8692CBB20D316DBF35FE650399D034DA57697