Handling Commandline Arguments in VB.NET

This article was posted more than 1 year ago. Please keep in mind that the information on this page may be outdated, insecure, or just plain wrong today.

Handling commandline arguments in VB.NET (or really anything) can be tricky. I came across the following class the other day while looking for a drop-in solution to a RAD I was developing.

Here’s what my usage shows for my application, thanks to the .NET class.

portscan.exe

portscan 0.9.2 by Rich Kreider

Usage: portscan -h <host> -s <startport> -e <endport> -f -d -t <timeoutms>

Options:
        -h      Host.  IP address or hostname.
        -s      Start port.  Begin scan at the specified port.
        -e      End port.  End scan at the specified port (max 65535).
        -f      Fast scan.  Scan without a timeout.  May be unreliable.
        -t      Timeout.  Timeout in ms to confirm port open (not used with -f)
        -p      Single port.  Scan single port.  Ignores -e and -s.
Examples:
        Normal Scan:
                portscan -h 192.168.1.50
        Normal Scan Custom Port Range:
                portscan -h 192.168.1.50 -s 1 -e 1024
        Fast Scan All Ports:
                portscan -h 192.168.1.50 -f
        Normal Scan Single Port:
                portscan -h 192.168.1.50 -p 80

InputArguments.vb

Public Class InputArguments
#Region "fields & properties"
    Public Const DEFAULT_KEY_LEADING_PATTERN As String = "-"

    Protected _parsedArguments As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
    Protected ReadOnly _keyLeadingPattern As String

    Default Public Property Item(key As String) As String
        Get
            Return GetValue(key)
        End Get
        Set(ByVal value As String)
            If key IsNot Nothing Then
                _parsedArguments(key) = value
            End If
        End Set
    End Property
    Public ReadOnly Property KeyLeadingPattern() As String
        Get
            Return _keyLeadingPattern
        End Get
    End Property
#End Region

#Region "public methods"
    Public Sub New(args() As String, pkeyLeadingPattern As String)
        _keyLeadingPattern = If(Not String.IsNullOrEmpty(pkeyLeadingPattern), pkeyLeadingPattern, DEFAULT_KEY_LEADING_PATTERN)

        If args IsNot Nothing AndAlso args.Length > 0 Then
            Parse(args)
        End If
    End Sub
    Public Sub New(args() As String)
        Me.New(args, Nothing)
    End Sub

    Public Function Contains(key As String) As Boolean
        Dim adjustedKey As String = Nothing
        Return ContainsKey(key, adjustedKey)
    End Function

    Public Overridable Function GetPeeledKey(key As String) As String
        Return If(IsKey(key), key.Substring(_keyLeadingPattern.Length), key)
    End Function
    Public Overridable Function GetDecoratedKey(ByVal key As String) As String
        Return If(Not IsKey(key), (_keyLeadingPattern & key), key)
    End Function
    Public Overridable Function IsKey(str As String) As Boolean
        Return str.StartsWith(_keyLeadingPattern)
    End Function
#End Region

#Region "internal methods"
    Protected Overridable Sub Parse(args() As String)
        For index As Integer = 0 To args.Length - 1
            If args(index) Is Nothing Then
                Continue For
            End If

            Dim key As String = Nothing
            Dim val As String = Nothing

            If IsKey(args(index)) Then
                key = args(index)

                If index + 1 < args.Length AndAlso Not IsKey(args(index + 1)) Then
                    val = args(index + 1)
                    index += 1
                End If
            Else
                val = args(index)
            End If

            ' adjustment
            If key Is Nothing Then
                key = val
                val = Nothing
            End If
            _parsedArguments(key) = val
        Next
    End Sub

    Protected Overridable Function GetValue(key As String) As String
        Dim adjustedKey As String = Nothing
        If ContainsKey(key, adjustedKey) Then
            Return _parsedArguments(adjustedKey)
        End If

        Return Nothing
    End Function

    Protected Overridable Function ContainsKey(ByVal key As String, <Runtime.InteropServices.Out()> ByRef adjustedKey As String) As Boolean
        adjustedKey = key

        If _parsedArguments.ContainsKey(key) Then
            Return True
        End If

        If IsKey(key) Then
            Dim peeledKey As String = GetPeeledKey(key)
            If _parsedArguments.ContainsKey(peeledKey) Then
                adjustedKey = peeledKey
                Return True
            End If
            Return False
        End If

        Dim decoratedKey As String = GetDecoratedKey(key)
        If _parsedArguments.ContainsKey(decoratedKey) Then
            adjustedKey = decoratedKey
            Return True
        End If
        Return False
    End Function
#End Region
End Class

To make use of the class, I have the following code sample.

Module Module1

Public ReadOnly Property CommandLineArguments As String()
        Get
            Return Environment.GetCommandLineArgs
        End Get
End Property

Dim host as String

Sub Main() {
    Dim arguments As New InputArguments(CommandLineArguments)

    If arguments.Contains("-h") Then
        host = arguments.Item("-h")
    End If
}

End Module

Supplying portscan.exe -h 127.0.0.1 results in the host variable being assigned the commandline argument value of 127.0.0.1.