Visual Basic 2013 - Console Input Tokens? - vb.net

I'm trying to grab user input from a console window, and break into array which works fine with split. I've recently read about split. I have created the following arrays to check for 'input' possibilities in each position of the split index.. but can't figure out how to actually check each position for valid input, let alone do something with the users 'command'.
Dim l1args as Array = {"help","exit","start"} 'first position possibilities
Dim l2args as Array = {"server","exit"} 'second position possibilities
Console.Write("console# ")
Dim input = Console.ReadLine()
Dim tokens As String() = input.Split(CChar(" "))
Can anyone give me some guidance? I want to be able to check for a combo of user input and do something with it. Then, if no possibilities exist or is off, I want to write an error message to console like "command not recognized"....
Thanks....

This should be a "recurrent algorithm".
you should think like this "start" is function that gets a variable (what to start?).
This variable is also a command and so you need to run it before starting the "start command".
for example the command: "start print 1"
lets say that print just prints 1, so basically what you are doing is getting the value of print 1 (the value of this is 1 because print 1 just prints 1) and then you will do start 1 (you will start 1, if there is no "1" make an error msg to the user.
The way I would do it is like this:
A command has a name and the amount of arguments it needs.
for example the start command will have the name "start" and it need 1 argument.
OR the command help will have a name "help" and will need 0 arguments.
OR the command print will have a name "print" and will need 1 arguments.
Now lets say the user entered the command "start print chrome"
you will look on the first word and see that start is a recognized command, so now you know you need to get its argument so you will go to the next word and run it as a command and give the result to the start command. So when going to the next word you see print this is also a recognized command so you will go to the next command to get the argument to the print command, now chrome is not a recognized command so you will assume that it is the value "chrome" (can be anythings else string, double ...) and you will run the command print with the argument "chrome" after that you will run the command start with the result of the command print.
Run Example:
Requested implementation:
This is a console application in vb.net .
Usually I am now putting all the classes in one file, but to post it here I had to do so.
try using the command "print sum 9 10", this will print 19
Option Strict On
Option Explicit On
Module Module1
Public AllCommand As Dictionary(Of String, ConsoleCommand)
Sub Main()
GenerateAllCommands()
Dim UserCommand() As String
While True
UserCommand = Console.ReadLine().Split({" "c}, System.StringSplitOptions.RemoveEmptyEntries)
RunCommand(UserCommand, True, 0)
End While
End Sub
Public Function RunCommand(FullCommand() As String, IsFirst As Boolean, CommandIndex As Integer) As CommandReturnedValue
If CommandIndex > FullCommand.Length - 1 Then
Return Nothing
End If
Dim CurrentCommand As String
If FullCommand.Length = 0 Then
If IsFirst Then
Console.WriteLine("Error - Empty String is not a valid command")
End If
Return New CommandReturnedValue("", CommandIndex)
End If
CurrentCommand = FullCommand(CommandIndex).Trim().ToLower()
If CurrentCommand.Length = 0 Then
If IsFirst Then
Console.WriteLine("Error - Empty String is not a valid command")
End If
Return New CommandReturnedValue("", CommandIndex)
End If
Dim TheCommnad As ConsoleCommand
If AllCommand.ContainsKey(CurrentCommand) Then
TheCommnad = AllCommand.Item(CurrentCommand)
Dim TempArgs(TheCommnad.Args - 1) As CommandReturnedValue
Dim TempIndex As Integer = CommandIndex + 1
For i As Integer = 0 To TheCommnad.Args - 1 Step 1
If TempIndex > FullCommand.Length - 1 Then
Console.WriteLine("Error - Need more arguments for """ & TheCommnad.CommandName & """ Command, You entered " & i & " Arguments, This command need " & TheCommnad.Args & " Arguents")
Return New CommandReturnedValue("", CommandIndex)
End If
TempArgs(i) = RunCommand(FullCommand, False, TempIndex)
If TempArgs(i) Is Nothing Then
Console.WriteLine("Error - Need more arguments for """ & TheCommnad.CommandName & """ Command, You entered " & i & " Arguments, This command need " & TheCommnad.Args & " Arguents")
Return New CommandReturnedValue("", CommandIndex)
End If
If TempArgs(i).CommandIndex <> -1 Then
TempIndex = TempArgs(i).CommandIndex + 1
Else
TempIndex += 1
End If
Next
If TempArgs.Length = 0 Then
Return TheCommnad.DoCommand(TempArgs, CommandIndex)
Else
Return TheCommnad.DoCommand(TempArgs, TempArgs(TempArgs.Length - 1).CommandIndex)
End If
Else
If IsFirst Then
Console.WriteLine("Error - Unknown Command """ & CurrentCommand & """")
End If
Return New CommandReturnedValue(FullCommand(CommandIndex), CommandIndex)
End If
End Function
Public Sub GenerateAllCommands()
'NOTE - ALL COMMAND MUST BE LOWER CASE
AllCommand = New Dictionary(Of String, ConsoleCommand)
AllCommand.Add("help", New HelpCommand())
AllCommand.Add("start", New StartCommand())
AllCommand.Add("print", New PrintCommand())
AllCommand.Add("sum", New SumCommand())
End Sub
End Module
Public Class CommandReturnedValue
Public Value As String
Public CommandIndex As Integer
Public Sub New(Value As String, Optional index As Integer = -1)
Me.Value = Value
Me.CommandIndex = index
End Sub
End Class
Public MustInherit Class ConsoleCommand
Public ReadOnly CommandName As String
Public ReadOnly Args As Integer
Public Sub New(Name As String, args As Integer)
Me.CommandName = Name
Me.Args = args
End Sub
Public MustOverride Function DoCommand(AllArgs() As CommandReturnedValue, CommandIndex As Integer) As CommandReturnedValue
End Class
Public Class StartCommand
Inherits ConsoleCommand
Public Sub New()
MyBase.New("start", 1)
End Sub
Public Overrides Function DoCommand(AllArgs() As CommandReturnedValue, CommandIndex As Integer) As CommandReturnedValue
If System.IO.File.Exists(AllArgs(0).Value) Then
Shell(AllArgs(0).Value, AppWinStyle.NormalFocus)
Else
Console.WriteLine("Error - Unknown file (Start Command)")
End If
Return Nothing
End Function
End Class
Public Class HelpCommand
Inherits ConsoleCommand
Public Sub New()
MyBase.New("help", 0)
End Sub
Public Overrides Function DoCommand(AllArgs() As CommandReturnedValue, CommandIndex As Integer) As CommandReturnedValue
Console.WriteLine("I see you tryed doing the help commnad ;)")
Console.WriteLine("Commands: ")
Console.WriteLine(" Start <FileName> - will try to start the <filename>")
Console.WriteLine(" help - will show a help list")
Console.WriteLine(" print <value> - will print (and return) <value>")
Return New CommandReturnedValue("Help Command", CommandIndex)
End Function
End Class
Public Class SumCommand
Inherits ConsoleCommand
Public Sub New()
MyBase.New("sum", 2)
End Sub
Public Overrides Function DoCommand(AllArgs() As CommandReturnedValue, CommandIndex As Integer) As CommandReturnedValue
Dim x As Integer
Dim y As Integer
Try
x = Convert.ToInt32(AllArgs(0).Value)
Catch ex As Exception
Console.WriteLine("Error - Arguments to sum must be an integer, """ & AllArgs(0).Value & """ is not an integer")
Return Nothing
End Try
Try
y = Convert.ToInt32(AllArgs(1).Value)
Catch ex As Exception
Console.WriteLine("Error - Arguments to sum must be an integer, """ & AllArgs(1).Value & """ is not an integer")
Return Nothing
End Try
Return New CommandReturnedValue((x + y).ToString, CommandIndex)
End Function
End Class
Public Class PrintCommand
Inherits ConsoleCommand
Public Sub New()
MyBase.New("print", 1)
End Sub
Public Overrides Function DoCommand(AllArgs() As CommandReturnedValue, CommandIndex As Integer) As CommandReturnedValue
Console.WriteLine("Print Command: """ & AllArgs(0).Value & """")
Return AllArgs(0)
End Function
End Class
English is not my mother tongue; please excuse any errors on my part.

Related

Parse custom language syntax

I am developing a server-side scripting language which I intend to use on my private server. It is similar to PHP, and I know that I could easily use PHP instead but I'm just doing some programming for fun.
The syntax of basic commands in my language is as follows:
command_name "parameter1" : "parameter2" : "parameter3"
But it can also be like this when I want to join values for a parameter:
command_name "parameter1" : "param" & "eter2" : "par" & "amet" & "er3"
How would I go about parsing a string like the ones shown above (it will be perfectly typed, no syntax errors) to an object that has these properties
Custom class "Request"
Property "Command" as String, should be the "command_name" part
Property "Parameters" as String(), should be an array of Parameter objects
Shared Function FromString(s As String) as Request, this should accept a string in the language above and parse it to a Request object
Custom class "Parameter"
Property "Segments" as String(), for example "para", "mete", and "r3"
Sub New(ParamArray s as String()), this is how it should be generated from the code
It should be done in VB.NET and I am a moderate level programmer, so even if you just have an idea of how to attack this then please share it with me. I am very new to parsing complex data like this so I need a lot of help. Thanks so much!
Here is another method that is simpler.
Module Module1
Sub Main()
Dim inputs As String() = {"command_name ""parameter1"" : ""parameter2"" : ""parameter3""", "command_name ""parameter1"" : ""param"" & ""eter2"" : ""par"" & ""amet"" & ""er3"""}
For Each _input As String In inputs
Dim commandStr As String = _input.Substring(0, _input.IndexOf(" ")).Trim()
Dim parameters As String = _input.Substring(_input.IndexOf(" ")).Trim()
Dim parametersA As String() = parameters.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(x) x.Trim()).ToArray()
Dim parametersB As String()() = parametersA.Select(Function(x) x.Split("&".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(Function(y) y.Trim(" """.ToCharArray())).ToArray()).ToArray()
Dim newCommand As New Command() With {.name = commandStr, .parameters = parametersB.Select(Function(x) New Parameter(x)).ToArray()}
Command.commands.Add(newCommand)
Next (_input)
Dim z = Command.commands
End Sub
End Module
Public Class Command
Public Shared commands As New List(Of Command)
Public name As String
Public parameters As Parameter()
End Class
Public Class Parameter
Sub New()
End Sub
Sub New(names As String())
Me.names = names
End Sub
Public names As String()
End Class
I figured it out myself
Module Module1
Sub Main()
Dim r As Request = Request.Parse(Console.ReadLine())
Console.WriteLine("The type of request is " & r.Name)
For Each p As Parameter In r.Parameters
Console.WriteLine("All segments inside of parameter " & r.Parameters.IndexOf(p).ToString)
For Each s As String In p.Segments
Console.WriteLine(" Segment " & p.Segments.IndexOf(s).ToString & " is " & s)
Next
Next
Main()
End Sub
Public Class Request
Public Name As String
Public Parameters As New List(Of Parameter)
Public Shared Function Parse(line As String)
Dim r As New Request
r.Name = line.Split(" ")(0)
Dim u As String = line.Substring(line.IndexOf(" "), line.Length - line.IndexOf(" "))
Dim p As String() = u.Split(":")
For Each n As String In p
Dim b As String() = n.Split("&")
Dim e As New List(Of String)
For Each m As String In b
Dim i As Integer = 0
Do Until i > m.Length - 1
If m(i) = ControlChars.Quote Then
Dim s As String = ""
i += 1
Do Until i > m.Length - 1 Or m(i) = ControlChars.Quote
s &= m(i)
i += 1
Loop
e.Add(s)
End If
i += 1
Loop
Next
r.Parameters.Add(New Parameter(e.ToArray))
Next
Return r
End Function
End Class
Public Class Parameter
Public Segments As New List(Of String)
Public Sub New(ParamArray s As String())
Segments = s.ToList
End Sub
End Class
End Module

Threading Problems (I don't understand it)

There's lots and lots of pages on the internet regarding threading but I can't seem to get my head around it.
I have a Form, which on the click of a button, loops through a file and reads it line by line. Each line is the login details for different FTP sites.
When it reads a line, it Dim's a variable as a new instance of a class named CallFTP using the login details.
It then Dim's a variable as a new Thread using a function in CallFTP named PerformFTP.
PerformFTP returns a string with the results of the FTP and I want to add this to a ListBox on the form that began it all.
The code for the button goes like this...
Private Sub cmdRun_Click(sender As Object, e As EventArgs) Handles cmdRun.Click
For Each _FTPLine As String In Split(_FTPDetails, vbNewLine)
Dim _Active As Boolean = CBool(Split(_FTPLine, "|")(7))
If _Active Then
_CurNum += 1
_ID = Format(Now.Year, "0000") & Format(Now.Month, "00") & Format(Now.Day, "00") & Format(Now.Hour, "00") & Format(Now.Minute, "00") & Format(Now.Second, "00") & Format(Now.Millisecond, "000") & Format(_CurNum, "00000")
Dim _FTP As New CallFTP(_ID, Split(_FTPLine, "|")(0), Split(_FTPLine, "|")(1), Split(_FTPLine, "|")(2), Split(_FTPLine, "|")(3), Split(_FTPLine, "|")(4), Split(_FTPLine, "|")(5), Split(_FTPLine, "|")(6))
Dim _Thread = New Thread(New ThreadStart(AddressOf _FTP.PerformFTP))
With _Thread
.IsBackground = True
.Start()
End With
End If
Next _FTPLine
End Sub
The class is as below (not quite but you don't need the rest of the code lol)
Public Class CallFTP
Private _ID As String = ""
Private _Response As String = ""
Private _IPAddress As String = ""
Private _Port As String = ""
Private _User As String = ""
Private _Pass As String = ""
Private _Remote As String = ""
Private _Local As String = ""
Private _InOut As String = ""
Public Sub New(ID As String, Server As String, PortNum As String, Username As String, Password As String, RemoteDir As String, LocalDir As String, InOrOut As String)
_ID = ID
_IPAddress = Server
_Port = PortNum
_User = Username
_Pass = Password
_Remote = RemoteDir
_Local = LocalDir
_InOut = InOrOut
End Sub
Public Function PerformFTP() As String
Return "This is a test"
End Function
End Class
Could anyone explain how I would call a sub named LogMessage on a module named modMisc (which adds a string to a ListBox on the main form)?
I've read that you need to invoke it but everything I read seems to give me a headache and make me need to lie down in a dark room for a few hours.
Is anyone capable of explaining as though you're speaking to a 2 year old? :)
Any help would be much appreciated.
You need to invoke a delegate to update your GUI if you're going to update it from another thread that from where it was created.
1º Your delegate must match (have the same signature) than the method you'll use:
Delegate Sub LogMessageExampleDelegate(ByVal x As Integer, ...)
Signature means that the delegate must return and receive the same types than your function/method.
2º Call your function to update GUI using delegate. This for example inside your update GUI function:
If yourListBox.InvokeRequired Then
yourListBox.Invoke(New LogMessageExampleDelegate(AddressOf THE_FUNCTION_WHICH_UPDATES_THE_GUI_NAME), parameter_value)
Else
'Just call your function
End If
With, as example:
sub addToListBox(byval text as string)
myListBox.Items.add(text)
end sub
So your invoke would be:
If yourListBox.InvokeRequired Then
yourListBox.Invoke(New LogMessageExampleDelegate(AddressOf addToListBox), "Item 1")
Else
'Just call your function
addToListBox("Item 1")
End If
PS: I wrote it two times so hope I didn't mess up with something without noticing it.

How do I get VBCodeProvider to return an object

I have a program that allows users to setup a client/server to control/run commands from a remote location. Now i'm trying to implement server plugins, and I'm doing that by loading every .vb file in a folder contained inside the current running directory. Everything is great, and the code from the external files compiles just fine... Only problem is, Its returning nothing when I try to compile the script and use one of the methods inside it.
Here's some code for you to check out. My error is in the 2nd. Any idea on how to fix this?
The Interaction Interface:
Public Interface LinkingInterface
Property name As String
Property statetag As String
Sub selected(ByVal Sock As Integer)
Sub deselected(ByVal Sock As Integer)
Sub load()
Function generateOutput(ByVal input As String, ByVal Sock As Integer) As String
End Interface
Detection/Loading of the "Modes" (add-ins):
For Each file In My.Computer.FileSystem.GetFiles("modes\")
Dim thisMode As LinkingInterface = LoadMode(My.Computer.FileSystem.ReadAllText(file))
thisMode.load() '<---------------------------My error is here, saying its a null ref.
modes_InterfaceCollection.Add(thisMode) 'Public modes_InterfaceCollection As New Microsoft.VisualBasic.Collection()
modes_nameIndex.Add(thisMode.name) 'Public modes_nameIndex As New Specialized.StringCollection()
Next
'LoadMode' Function
Public Function LoadMode(ByVal code As String) As LinkingInterface
Using provider As New VBCodeProvider()
Dim parameters As New CompilerParameters()
parameters.GenerateInMemory = True
parameters.ReferencedAssemblies.Add(Reflection.Assembly.GetExecutingAssembly().Location)
parameters.MainClass = "Remote_Command_Line.MainModule"
Dim interfaceNamespace As String = GetType(LinkingInterface).Namespace
Dim codeBuilder As New Text.StringBuilder
Dim namespaces() As String = New String() {"Microsoft.VisualBasic", "System", "System.Console", "System.Collections", "System.Collections.Generic", _
"System.Data", "System.Diagnostics", "Remote_Command_Line.MainModule"}
Dim codeString As New StringBuilder
For Each namespacestring As String In namespaces
codeString.AppendLine("Imports " & namespacestring)
Next
codeString.AppendLine(code)
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, codeString.ToString)
'I commented out this just for debugging purposes
'If results.Errors.HasErrors Then
'For Each scriptError As CompilerError In results.Errors
'WriteLine(scriptError.ToString)
'Next
'Else
Return CType(results.CompiledAssembly.CreateInstance(results.CompiledAssembly.GetType.Name), LinkingInterface)
'End If
End Using
End Function
The Testing file 'test.vb':
Public Class test_mode
'Designed for RCL mode 1.0b
'Matthew 2013
'used for managing the local filesystem.
'####################################
#Region "Properties"
'all of these properties listed are required --------------------
Implements LinkingInterface
Property name As String Implements LinkingInterface.name 'the name the client refers to you in the 'modeswitch' command
Property statetag As String Implements LinkingInterface.statetag 'short tag displayed on the client when active before the input signal '>'
'----------------------------------------------------------------
Public curDirDatabank As New Specialized.StringCollection()
#End Region
'####################################
'####################################
#Region "Subs"
'Its required to have, but not required to do anything. This load sub is here for any modes that may require an initialization
Private Sub load() Implements LinkingInterface.load 'REQUIRED
name = "file" : statetag = "file"
MsgBox("Testing: It's loaded")
End Sub
Private Sub selected(ByVal Sock As Integer) Implements LinkingInterface.selected
MsgBox("Testing: '" & Sock & "' selected the File mode")
End Sub
Private Sub deselected(ByVal Sock As Integer) Implements LinkingInterface.deselected
MsgBox("Testing: '" & Sock & "' deselected the File mode")
End Sub
Private Function generateOutput(ByVal input As String, ByVal Sock As Integer) As String Implements LinkingInterface.generateOutput 'REQUIRED
Return ("Testing: '" & Sock & "' said '" & input & "'")
End Function
#End Region
'####################################
End Class
the following line is wrong.
Return CType(results.CompiledAssembly.CreateInstance(results.CompiledAssembly.GetType.Name), LinkingInterface)
You need to search through the loaded classes for one implementing your interface (it being VB you automatically get classes generated for the My namespace objects such as My.Computer)
Try this instead
For Each t As Type In results.CompiledAssembly.GetTypes()
If t.GetInterface(GetType(LinkingInterface).Name) IsNot Nothing Then
Return CType(results.CompiledAssembly.CreateInstance(t.Name), LinkingInterface)
End If
Next
Return Nothing

VB.NET Class and list(of issue

I'm using couple of custom classes and need to write data in one function and read data in another.
In order to do that, I'm using an XM file.
My issue is when I write the data.
Here is my code:
Imports System.IO
Imports System
Public Class MainForm
Public Class MyClass
Private _id As Integer
Private _somestring As String
Public Property Id() As Integer
Get
Return _id
End Get
Set(ByVal Value As Integer)
_id = Value
End Set
End Property
Public Property Somestring() As String
Get
Return _somestring
End Get
Set(ByVal Value As String)
_somestring = Value
End Set
End Property
End Class
Private Sub mysub()
Dim ListVar As New List(Of MyClass)
' code...code.....code....
Dim newvar As New MyClass()
console.writeline("==== within the loop ====")
For i As Integer = 0 To 2
newvar.Id = I
newvar.Somestring = "hello - " & cstr(I)
ListVar.add(newvar)
console.writeline("listvar(" & i & ")=" & listvar(i).Id)
Next
console.writeline("==== outside the loop ====")
console.writeline("listvar(0)=" & listvar(0).Id)
console.writeline("listvar(1)=" & listvar(1).Id)
console.writeline("listvar(2)=" & listvar(2).Id)
End Sub
End Class
Output:
==== within the loop ====
listvar(0)=0
listvar(1)=1
listvar(2)=2
==== outside the loop ====
listvar(0)=2
listvar(1)=2
listvar(2)=2
I must be doing something stupid but I just figure out what it is....
Do this:
console.writeline("==== within the loop ====")
For i As Integer = 0 To 2
Dim newvar As New MyClass()
newvar.Id = I
newvar.Somestring = "hello - " & cstr(I)
ListVar.add(newvar)
console.writeline("listvar(" & i & ")=" & listvar(i).Id)
Next
newvar continues to be the same instance, so all three references in the List point to the same instance, and its Id value is what you set it last in your For loop.

Access variables of sub in VBA

I have the following sub
Public Static Sub Varib()
Device_ = Sheet1.DeviceType_.Text
Model_ = Sheet1.Model_.Text
Security_ = Sheet1.SecurityGroup_.Text
Catagory_ = Application.Index(Worksheets("Temp_for_varible_lists").Range("b:b"), Application.Match(x, Worksheets("Temp_for_varible_lists").Range("A:A"), 0))
End Sub
It in fact carries on and in total produces a whole bunch of vaules of various datatypes based on the users input.
So the user choses from a few check boxes, list boxes, fills in some text boxes and hits a submit button and this sub populates a number of varibles from that, that are then uterlised by other funcation and sub in the application.
Now I could make all the varibles Global and access them in that fassion. But I was hoping for something more like what I have seen with c# and VB.net
where you can get the value by using
sub.varible name
example for the code above.
Sub Main()
x = Varib.Device_
msgbox(x)
end sub
is there a simmular way to do this in VBA?
Cheers
aaron
What you're asking cannot be done. The solution is not to make your variables global either (generally a bad idea, with some exceptions, this case not being one of them).
One possibility is to create a user-defined type:
Type Varib
Device_ As String
Model_ As String
Security_ As String
Category_ As String
End Type
and a sub to populate it from your sheet:
Sub LoadVaribFromSheet(v As Varib)
With v
.Device_ = Sheet1.DeviceType_.Text
.Model_ = Sheet1.Model_.Text
.Security_ = Sheet1.SecurityGroup_.Text
.Category_ = _
Application.Index(Worksheets("Temp_for_varible_lists").Range("b:b"), _
Application.Match(x, _
Worksheets("Temp_for_varible_lists").Range("A:A"), 0))
End With
End Sub
You can then use this as follows:
Sub Main()
Dim myVarib As Varib
LoadVaribFromSheet myVarib
' Now do stuff with myVarib ...
MsgBox myVarib.Device_
End Sub
you can use encapsulation for this
Private value As String
Private value1 As String
Public Function setValue(val As String)
value = val
End Function
Public Function setValue1(val As String)
value1 = val
End Function
Public Function getValue() As String
getValue = value
End Function
Public Function getValue1() As String
getValue1 = value1
End Function
-------------------------------------------------------------------------
Sub test()
MsgBox getValue & vbCrLf & getValue1
setValue "myValue"
setValue1 "myValue1"
MsgBox getValue & vbCrLf & getValue1
End Sub