I had previously created a Command Line Parser from Ray Hayes codeproject article Automatic Command Line Parsing in C# . I had adapted it to VB.NET and upgraded it to .NET 3.5 but I recently ran into the problem with wanting a single command prompt application to handle multiple processes and multiple parameters. This would allow you to group all of a particular tasks commands into a single application. With the advent of Power Shell this format is increasingly less relevant, but with the proliferation of Power Shell many people still prefer to use the good old command line.
So, staring from the original Command Line Parser v1.0 code I wanted to be able to add multiple commands, or even nest commands. The result is a nice simple commanding architecture conducive to creating multiple commands.
Using this model I can create a simple command…
Imports Hinshlabs.CommandLineParser
Imports System.IO
Imports System.Collections.ObjectModel
Imports System.Net
Public Class Demo1Command
Inherits CommandBase(Of Demo1CommandLine)
Private m_PortalLocation As Uri
Public Overrides ReadOnly Property Description() As String
Get
Return "demo 1 command demonstrates a sinle nested command"
End Get
End Property
Public Overrides ReadOnly Property Name() As String
Get
Return "Demo1"
End Get
End Property
Protected Overrides Function ValidateCommand() As Boolean
Return True
End Function
Public Overrides ReadOnly Property Title() As String
Get
Return "demo 1"
End Get
End Property
Public Overrides ReadOnly Property Synopsis() As String
Get
Return "demo 1 command"
End Get
End Property
Public Overrides ReadOnly Property Switches() As ReadOnlyCollection(Of SwitchInfo)
Get
Return CommandLine.Switches
End Get
End Property
Public Overrides ReadOnly Property Qualifications() As String
Get
Return String.Empty
End Get
End Property
Protected Overrides Function RunCommand() As Integer
Try
CommandOut.Warning("running Demo1")
Return -1
Catch ex As Exception
CommandOut.Error("Failed: {0}", ex.ToString)
Return -1
End Try
End Function
End Class
Or something more substantial:
Protected Overrides Function RunCommand() As Integer
Try
Dim x As New Proxies.MyApp.Configuration.ConfigurationServiceClient("BasicHttpBinding_IConfigurationService", m_PortalLocation.ToString)
x.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Delegation
Select Case CommandLine.Action
Case QuiesceAction.Offline
x.QuiesceSource(CommandLine.Source, CommandLine.Message, New TimeSpan(0))
Case QuiesceAction.Online
x.RestoreSource(CommandLine.Source)
End Select
CommandOut.Info("Source {0} has been made {1}", CommandLine.Source, CommandLine.Action.ToString)
Return 0
Catch ex As EndpointNotFoundException
CommandOut.Error("Unable to locate site. Check the value you selected for /Portal:{0}", CommandLine.Portal)
Return -1
Catch ex As Exception
CommandOut.Error("Failed: {0}", ex.ToString)
Return -1
End Try
End Function
If you are wondering where the variables come from, you can see form Demo1Command that a generic type of Demo1CommandLine is passed in. The application creates an instance of this which wraps the Ray Hayes parser to provide the values from Environment.CommandLine used on the shared methods on the CommandLineBase class.
''' <summary>
''' Created a command line object using the Environment.CommandLine information
''' </summary>
''' <typeparam name="TCommandLine">The concrete type of object to create</typeparam>
''' <returns>An instance of the object</returns>
''' <remarks></remarks>
Public Shared Function CreateCommandLine(Of TCommandLine As {New, CommandLineBase})() As TCommandLine
Return CreateCommandLine(Of TCommandLine)(Environment.CommandLine)
End Function
''' <summary>
''' Created a command line object using the Environment.CommandLine information
''' </summary>
''' <typeparam name="TCommandLine">The concrete type of object to create</typeparam>
''' <param name="CommandLine">The command line arguments to parse</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function CreateCommandLine(Of TCommandLine As {New, CommandLineBase})(ByVal CommandLine As String) As TCommandLine
Dim instance As New TCommandLine
Dim parser As New Parser(CommandLine, instance)
parser.Parse()
instance.Parser = parser
Return instance
End Function
This parser then populates the CommandLine object with values from the CommandLine passed in. For example:
Imports Hinshlabs.CommandLineParser
Imports System.Collections.ObjectModel
Public Class Demo3CommandLine
Inherits CommandLineBase
Private m_value1 As String
Private m_value2 As Value2Values = Value2Values.Value1
<CommandLineSwitch("Value1", "Adds a string value named value1"), CommandLineAlias("v1")> _
Public Property Value1() As String
Get
Return Me.m_value1
End Get
Set(ByVal value As String)
Me.m_value1 = value
End Set
End Property
<CommandLineSwitch("Value2", "Adds and enum value called value2"), CommandLineAlias("v2")> _
Public Property Value2() As Value2Values
Get
Return Me.m_value2
End Get
Set(ByVal value As Value2Values)
Me.m_value2 = value
End Set
End Property
Public Enum Value2Values
Enum1
Enum2
Enum3
End Enum
End Class
Would allow you to call [consoleApp] Demo3 /v1:”Any value you like” /Value2:Enum3 and have the correct values populated at runtime.
I have also updated with a DelegateCommand class that would allow you to call a function in the right format from anywhere:
New DelegateCommand(Of Demo3CommandLine)("Demo2", AddressOf OnDemo2Run, "demo 2", "no additional information", "demo 2 command", "This command shows how to delegate the run method using the delegate command")
The delegate command is really easy in .NET 3.5 with the only change being the addition of a variable declared as a Func in the class:
Imports Hinshlabs.CommandLineParser
Imports System.IO
Imports System.Collections.ObjectModel
Imports System.Net
Public Class DelegateCommand(Of TCommandLine As {New, CommandLineBase})
Inherits CommandBase(Of TCommandLine)
Private m_Description As String
Private m_Title As String
Private m_Synopsis As String
Private m_Qualifications As String
Private m_name As String
Private m_RunCommand As Func(Of Integer)
Public Overrides ReadOnly Property Description() As String
Get
Return m_Description
End Get
End Property
Public Overrides ReadOnly Property Name() As String
Get
Return m_name
End Get
End Property
Protected Overrides Function RunCommand() As Integer
Try
Return m_RunCommand.Invoke
Catch ex As Exception
CommandOut.Error("Failed: {0}", ex.ToString)
Return -1
End Try
End Function
Protected Overrides Function ValidateCommand() As Boolean
Return True
End Function
Public Overrides ReadOnly Property Title() As String
Get
Return m_title
End Get
End Property
Public Overrides ReadOnly Property Synopsis() As String
Get
Return Synopsis
End Get
End Property
Public Overrides ReadOnly Property Switches() As ReadOnlyCollection(Of SwitchInfo)
Get
Return CommandLine.Switches
End Get
End Property
Public Overrides ReadOnly Property Qualifications() As String
Get
Return String.Empty
End Get
End Property
Public Sub New(ByVal name As String, ByVal runCommand As Func(Of Integer), ByVal title As String, ByVal qualifications As String, ByVal synopsis As String, ByVal description As String)
m_name = name
m_RunCommand = runCommand
m_Title = title
m_Qualifications = qualifications
m_Synopsis = synopsis
m_Description = description
End Sub
End Class
If you were wondering why there are so many properties, it is to allow the help to be created automatically. For example if you call the help function on Demo3Command you will get…

With the values coming from the relevant places:

It will also support inherited CommandLine objects to minimize duplication.
I hope that if you are building command line apps that you will have a look, just remember not to spend too much effort on cmd, when Power Shell is much more suitable and accessible to non developers.
Technorati Tags: .NET CodeProject
Smart Classifications
Each classification [Concepts, Categories, & Tags] was assigned using AI-powered semantic analysis and scored across relevance, depth, and alignment. Final decisions? Still human. Always traceable. Hover to see how it applies.
What to read next
Advice on using XamRibbon with Composite WPF
Provides guidance and code examples for integrating Infragistics XamRibbon with Composite WPF, including custom region adapters for dynamic …
Configuring a PowerShell Adapter for the TFS Integration Platform
Explains how to set up and configure a PowerShell adapter in the TFS Integration Platform to automate source control migrations, with custom …
Wpf Drag & Drop behaviour
Explains how to implement flexible drag and drop in WPF using MVVM, with customisable drop behaviour and bindable options for ItemsControls, …
The Evolution of My Journey with Azure DevOps: Lessons and Insights
Personal experiences and lessons on using Azure DevOps, covering its evolution, migration strategies, custom tools, and practical advice for …
Detecting agile theatre with real delivery signals
Why Most Companies Operating Models Fail in Dynamic Markets
A concise comparison of Predictive and Adaptive Operating Models, explaining why traditional structures fail in dynamic markets and how …
Don’t Manage Dependencies, Remove Them
Explains why dependencies are a sign of poor system design and outlines steps to eliminate them by aligning teams, clarifying ownership, and …
The Estimation Trap: How Tracking Accuracy Undermines Trust, Flow, and Value in Software Delivery
Tracking estimation accuracy in software delivery leads to mistrust, fear, and distorted behaviours. Focus on customer value, flow, and …
Flow of Value vs Flow of Work – Misnomer or Useful Shorthand?
Compares “flow of value” and “flow of work” in Kanban, explaining why only validated outcomes count as value and stressing the need for …
Why Outsourcing DevOps Fails, and How Real Engineering Excellence Starts With Your Team
Avoid DevOps vendor lock-in, discover how true engineering excellence starts with partnership, not outsourcing. Ready to transform your …
Detecting agile theatre with real delivery signals
Don’t Manage Dependencies, Remove Them
Explains why dependencies are a sign of poor system design and outlines steps to eliminate them by aligning teams, clarifying ownership, and …
The Estimation Trap: How Tracking Accuracy Undermines Trust, Flow, and Value in Software Delivery
Tracking estimation accuracy in software delivery leads to mistrust, fear, and distorted behaviours. Focus on customer value, flow, and …
Flow of Value vs Flow of Work – Misnomer or Useful Shorthand?
Compares “flow of value” and “flow of work” in Kanban, explaining why only validated outcomes count as value and stressing the need for …
Why Outsourcing DevOps Fails, and How Real Engineering Excellence Starts With Your Team
Avoid DevOps vendor lock-in, discover how true engineering excellence starts with partnership, not outsourcing. Ready to transform your …
Estimating Better in an Overloaded System Is a Poor Man’s Strategy
High work in progress (WIP) causes delays and unpredictability; improving estimates won’t help. Limiting WIP and focusing on flow is key to …