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…
1Imports Hinshlabs.CommandLineParser
2Imports System.IO
3Imports System.Collections.ObjectModel
4Imports System.Net
5
6Public Class Demo1Command
7 Inherits CommandBase(Of Demo1CommandLine)
8
9 Private m_PortalLocation As Uri
10
11 Public Overrides ReadOnly Property Description() As String
12 Get
13 Return "demo 1 command demonstrates a sinle nested command"
14 End Get
15 End Property
16
17 Public Overrides ReadOnly Property Name() As String
18 Get
19 Return "Demo1"
20 End Get
21 End Property
22
23 Protected Overrides Function ValidateCommand() As Boolean
24 Return True
25 End Function
26
27 Public Overrides ReadOnly Property Title() As String
28 Get
29 Return "demo 1"
30 End Get
31 End Property
32
33 Public Overrides ReadOnly Property Synopsis() As String
34 Get
35 Return "demo 1 command"
36 End Get
37 End Property
38
39 Public Overrides ReadOnly Property Switches() As ReadOnlyCollection(Of SwitchInfo)
40 Get
41 Return CommandLine.Switches
42 End Get
43 End Property
44
45 Public Overrides ReadOnly Property Qualifications() As String
46 Get
47 Return String.Empty
48 End Get
49 End Property
50
51 Protected Overrides Function RunCommand() As Integer
52 Try
53 CommandOut.Warning("running Demo1")
54 Return -1
55 Catch ex As Exception
56 CommandOut.Error("Failed: {0}", ex.ToString)
57 Return -1
58 End Try
59 End Function
60
61End Class
Or something more substantial:
1Protected Overrides Function RunCommand() As Integer
2 Try
3 Dim x As New Proxies.MyApp.Configuration.ConfigurationServiceClient("BasicHttpBinding_IConfigurationService", m_PortalLocation.ToString)
4 x.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Delegation
5 Select Case CommandLine.Action
6 Case QuiesceAction.Offline
7 x.QuiesceSource(CommandLine.Source, CommandLine.Message, New TimeSpan(0))
8 Case QuiesceAction.Online
9 x.RestoreSource(CommandLine.Source)
10 End Select
11 CommandOut.Info("Source {0} has been made {1}", CommandLine.Source, CommandLine.Action.ToString)
12 Return 0
13
14 Catch ex As EndpointNotFoundException
15 CommandOut.Error("Unable to locate site. Check the value you selected for /Portal:{0}", CommandLine.Portal)
16 Return -1
17 Catch ex As Exception
18 CommandOut.Error("Failed: {0}", ex.ToString)
19 Return -1
20 End Try
21End 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.
1''' <summary>
2''' Created a command line object using the Environment.CommandLine information
3''' </summary>
4''' <typeparam name="TCommandLine">The concrete type of object to create</typeparam>
5''' <returns>An instance of the object</returns>
6''' <remarks></remarks>
7Public Shared Function CreateCommandLine(Of TCommandLine As {New, CommandLineBase})() As TCommandLine
8 Return CreateCommandLine(Of TCommandLine)(Environment.CommandLine)
9End Function
10
11''' <summary>
12''' Created a command line object using the Environment.CommandLine information
13''' </summary>
14''' <typeparam name="TCommandLine">The concrete type of object to create</typeparam>
15''' <param name="CommandLine">The command line arguments to parse</param>
16''' <returns></returns>
17''' <remarks></remarks>
18Public Shared Function CreateCommandLine(Of TCommandLine As {New, CommandLineBase})(ByVal CommandLine As String) As TCommandLine
19 Dim instance As New TCommandLine
20 Dim parser As New Parser(CommandLine, instance)
21 parser.Parse()
22 instance.Parser = parser
23 Return instance
24End Function
This parser then populates the CommandLine object with values from the CommandLine passed in. For example:
1Imports Hinshlabs.CommandLineParser
2Imports System.Collections.ObjectModel
3
4Public Class Demo3CommandLine
5 Inherits CommandLineBase
6
7 Private m_value1 As String
8 Private m_value2 As Value2Values = Value2Values.Value1
9
10 <CommandLineSwitch("Value1", "Adds a string value named value1"), CommandLineAlias("v1")> _
11 Public Property Value1() As String
12 Get
13 Return Me.m_value1
14 End Get
15 Set(ByVal value As String)
16 Me.m_value1 = value
17 End Set
18 End Property
19
20 <CommandLineSwitch("Value2", "Adds and enum value called value2"), CommandLineAlias("v2")> _
21 Public Property Value2() As Value2Values
22 Get
23 Return Me.m_value2
24 End Get
25 Set(ByVal value As Value2Values)
26 Me.m_value2 = value
27 End Set
28 End Property
29
30 Public Enum Value2Values
31 Enum1
32 Enum2
33 Enum3
34 End Enum
35
36End 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:
1New 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:
1Imports Hinshlabs.CommandLineParser
2Imports System.IO
3Imports System.Collections.ObjectModel
4Imports System.Net
5
6Public Class DelegateCommand(Of TCommandLine As {New, CommandLineBase})
7 Inherits CommandBase(Of TCommandLine)
8
9 Private m_Description As String
10 Private m_Title As String
11 Private m_Synopsis As String
12 Private m_Qualifications As String
13 Private m_name As String
14 Private m_RunCommand As Func(Of Integer)
15
16 Public Overrides ReadOnly Property Description() As String
17 Get
18 Return m_Description
19 End Get
20 End Property
21
22 Public Overrides ReadOnly Property Name() As String
23 Get
24 Return m_name
25 End Get
26 End Property
27
28 Protected Overrides Function RunCommand() As Integer
29 Try
30 Return m_RunCommand.Invoke
31 Catch ex As Exception
32 CommandOut.Error("Failed: {0}", ex.ToString)
33 Return -1
34 End Try
35 End Function
36
37 Protected Overrides Function ValidateCommand() As Boolean
38 Return True
39 End Function
40
41 Public Overrides ReadOnly Property Title() As String
42 Get
43 Return m_title
44 End Get
45 End Property
46
47 Public Overrides ReadOnly Property Synopsis() As String
48 Get
49 Return Synopsis
50 End Get
51 End Property
52
53 Public Overrides ReadOnly Property Switches() As ReadOnlyCollection(Of SwitchInfo)
54 Get
55 Return CommandLine.Switches
56 End Get
57 End Property
58
59 Public Overrides ReadOnly Property Qualifications() As String
60 Get
61 Return String.Empty
62 End Get
63 End Property
64
65 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)
66 m_name = name
67 m_RunCommand = runCommand
68 m_Title = title
69 m_Qualifications = qualifications
70 m_Synopsis = synopsis
71 m_Description = description
72 End Sub
73
74End 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
No related videos found.
If you've made it this far, it's worth connecting with our principal consultant and coach, Martin Hinshelwood, for a 30-minute 'ask me anything' call.
We partner with businesses across diverse industries, including finance, insurance, healthcare, pharmaceuticals, technology, engineering, transportation, hospitality, entertainment, legal, government, and military sectors.
NIT A/S
NIT A/S