views:

3490

answers:

5

What is the correct way to implement and architect a command line tool as a C# console application?

Concerns to address include proper parsing of command line variables, and the proper way to output text. While Console.WriteLine() is the most obvious choice for output, what are the circumstances in which one should instead opt to write to the standard error stream, .Error, .SetErrorStream, etc?

What is the proper way for the application to exit while returning a proper return code to the calling command?

How should the the CancelKeyPress event be implemented to interrupt the program? Is it only for use when an asynchronous operation is occurring on a separate thread?

Is there a concise guide to command line tool programming in C#, or even better an open source project or template which I could use to properly implement a relatively simple tool?

+1  A: 

As for command line arguments, you'll find various schemes, but I've always been a fan of

app.exe "self-explanetory arg" /noArgumentSwitch /argumentSwitch="argument"

As for the return code, you can change the signature of your Main() function to return an int rather than void. This will allow you to return a code to the calling process, if necessary.

As for the error stream, I've never personally used it, and I don't think that it should come at the expense of outputting error information in the standard output stream. It's probably better used for specific error debugging information.

Adam Robinson
+10  A: 

Error messages should be written to stderr aka Console.Error, and normal output to stdout aka Console.Out. This is particularly important for "filter" type console apps whose output (stdout) can be piped to another process, e.g. in a batch file.

Generally if you encounter an error, write an error message to Console.Error and return a non-zero result. Or if it's an exception, just don't bother handling it.

To return a result code, you can either pass it as an argument to Environment.Exit, set the Environment.ExitCode property, or return a non-zero value from main.

For simple console apps I would:

  • have a helper class to parse the command line.

  • have a facade class that provides a testable API for the functionality implemented by your command line tool. Like most .NET APIs, this would normally throw an exception if an error occurs.

  • the main program simply uses the helper to parse the command line and calls the API passing the arguments passed from the command line. It optionally catches exceptions thrown from the API, logs them, writes a user-oriented error message to Console.Error and sets a non-zero return code.

But I wouln't consider this to be the one true way: there isn't really such a thing which is why you're unlikely to find the book you're looking for.

Joe
+3  A: 

As to how to implement command parsing, I've succesfully used reflection and delegates before. They way it works is to decorate command methods with a special attribute you make yourself that states the method should be user-invokable, either through the method's name or a string specified in the attribute, i.e.:

[Command("quit")]
public void QuitApp()
{
    ...
}

On program startup you can scan a class for such methods, and store delegates that target them in a dictionary where the keys are the commands. This makes it easy to parse commands based on looking up the first word in a dictionary (amortized O(1) ) and easily extensible and maintanable for the future, as new commands are added simply adding separate methods.

Cecil Has a Name
+1  A: 

For command-line handling, check out Mono.GetOptions. It makes it easy to populate variables from short (-f style) and long (--file style) command line options.

Todd
A: 

I've opted to write a number of console utility apps as windows form applications instead of console apps. Generally, I add a timer to delay initial start and just add a cancel button with a progress meter--thus allowing a more intuitive cancel option. You can still output to the console that way too.

Josh