How to run an external script using visual basic? - vb.net

I want to make a Windows application using visual basic and visual studio 2017.
Here I want to run an external Python script which will run when 'Button 1' is pressed in the windows form. It will take input from the Textbox in the form and do some calculations and then return the result and put it back in the Textbox in the form.
The python code can be either this :
r = input()
p = 2*3.14*r
print(p) #Instead of print(p) something that can return the value to the form
OR This:
def peri(r):
p = 2*3.14*r
return p

I managed to find this solution for you and i really hope it works:
1.Write your python code , for example : print("Hello world python !!") in text file and save to .py
2.Create project at VB.NET, drag component 1 button and 1 textbox into form.
3.Double click button1, at event click button1 write this code
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim proc As Process = New Process
proc.StartInfo.FileName = "C:\Python34\python.exe" 'Default Python Installation
proc.StartInfo.Arguments = pathmypythonfile.py
proc.StartInfo.UseShellExecute = False 'required for redirect.
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden 'don't show commandprompt.
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.RedirectStandardOutput = True 'captures output from commandprompt.
proc.Start()
AddHandler proc.OutputDataReceived, AddressOf proccess_OutputDataReceived
proc.BeginOutputReadLine()
proc.WaitForExit()
TextBox1.Text = Value
End Sub
Public sub proccess_OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
On Error Resume Next
If e.Data = "" Then
Else
Value = e.Data
End If
End sub
4. Create module file, and write this variable global :
Module module
Public Value As String
End Module
5. Running the application, if textbox1 have populated with some string then your code was success.
HERE is a lick with the full process.

This is the answer by #Dimitar_Georgiev, with small corrections and formatting fixes.
I managed to find this solution for you and i really hope it works:
Write your python code , for example : print("Hello world python !!") in text file and save to .py
Create project at VB.NET, drag component 1 button and 1 textbox into form.
Double click button1, at event click button1 write this code
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim proc As Process = New Process
proc.StartInfo.FileName = "C:\Python34\python.exe" 'Default Python Installation
proc.StartInfo.Arguments = "C:\path\to\my\pythonfile.py"
proc.StartInfo.UseShellExecute = False 'required for redirect.
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden 'don't show commandprompt.
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.RedirectStandardOutput = True 'captures output from commandprompt.
proc.Start()
AddHandler proc.OutputDataReceived, AddressOf proccess_OutputDataReceived
proc.BeginOutputReadLine()
proc.WaitForExit()
TextBox1.Text = Value
End Sub
Public sub proccess_OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
On Error Resume Next
If e.Data = "" Then
Else
Value = e.Data
End If
End sub
Create module file, and write this variable global :
Module module
Public Value As String
End Module
Running the application, if textbox1 have populated with some string then your code was success.
HERE is a lick with the full process.

Related

Cross-thread operation in button property from another function vb.net

I am trying to change the state of a button from another function where I use the AddHandler and AddressOf but it turns out that said function does not allow me to make changes to the UI .
Sub Button1Click(sender As Object, e As EventArgs)
CMD_CS("C:\Users\Gabr\Desktop","dir element.docx /s /p")
End Sub
Private Sub CMD_CS(ByVal path As String, ByVal comand As String)
button1.Enabled = False
Dim p As Process = New Process()
Dim ps As ProcessStartInfo = New ProcessStartInfo()
Environment.CurrentDirectory = path
ps.UseShellExecute = True
ps.WindowStyle = ProcessWindowStyle.Hidden
ps.FileName = "cmd"
ps.Arguments = " /c " + comand
p.StartInfo = ps
p.Start()
p.EnableRaisingEvents = True
AddHandler p.Exited, AddressOf PsExit
End Sub
Public Sub PsExit()
Me.button1.Enabled = True ' <---- error Button1 no access
Console.WriteLine("process end")
' get p.StandardOutput.ReadLine
End Sub
It would also be very useful to know the result that the console is throwing at me but I have no idea.
Here I have two objectives, the first is that I do not know how to change the state of the button from the PsExit() function and the second is that in that same function print the results generated by the cmd
Error
System.InvalidOperationException: Cross-thread operation not valid: Control 'button1' accessed from a thread other than the thread it was created on.
There are better ways to check if a file exists, but perhaps you're just using it for testing.
If one needs to update a property for a Button during cross-threaded operations, one can do either of the following:
Option 1: (Action)
Button1.Invoke(New Action(Sub()
Button1.Enabled = True
End Sub))
Option 2: (MethodInvoker)
Button1.Invoke(New MethodInvoker(Sub()
Button1.Enabled = True
End Sub))
The code below shows how to use System.Diagnostics.Process. I've included two different ways of determining whether or not the command being run by Process completed successfully or not.
Using an event named ProcessCompleted. The value of hasSuccessfullyCompleted lets one know if the the operation successfully completed without errors.
Using a return value from the Function. If the return value is "Success", then the operation successfully completed without errors. If an error occurred, the error message is returned.
Create a WinForms project
VS 2017:
Open Visual Studio
Click File
Select New
Select Project
Expand Installed
Expand Visual Basic
Click Windows Desktop
Select Windows Forms App (.NET Framework)
Specify project name (name: ReadSerialPort)
Click OK
VS 2019:
Open Visual Studio
Click Continue without code
Click File
Select New
Select Project
Visual Basic Windows Desktop
Click Windows Forms App (.NET Framework)
Click Next
Specify project name (name: ReadSerialPort)
Click Create
Note: From this point forward, the process is the same for both VS 2017 and VS 2019.
Create a class (name: HelperProcess.vb)
In VS menu, click Project
Select Add Class
For name, enter "HelperProcess.vb"
Click Add
HelperProcess.vb
Public Class HelperProcess
Public Event ErrorDataReceived(sender As Object, data As String)
Public Event OutputDataReceived(sender As Object, data As String)
Public Event ProcessCompleted(sender As Object, hasSuccessfullyCompleted As Boolean)
Private ProcessError As String = String.Empty
Public Function RunCmd(ByVal exePath As String, ByVal Optional arguments As String = Nothing) As String
Dim errMsg As String = String.Empty
'set value
ProcessError = String.Empty
If String.IsNullOrEmpty(exePath) Then
errMsg = "exePath not specified"
Debug.WriteLine(errMsg)
're-initialize
ProcessError = "Error: " & errMsg
Throw New Exception(errMsg)
End If
Try
'create new instance
Dim psInfo As ProcessStartInfo = New ProcessStartInfo(exePath, arguments)
'set properties
psInfo.Arguments = arguments 'arguments
psInfo.CreateNoWindow = True 'don't create a window
psInfo.RedirectStandardError = True 'redirect standard Error
psInfo.RedirectStandardOutput = True 'redirect standard output
psInfo.RedirectStandardInput = False
psInfo.UseShellExecute = False 'If True, uses 'ShellExecute'; if false, uses 'CreateProcess'
psInfo.WindowStyle = ProcessWindowStyle.Hidden
psInfo.ErrorDialog = False
'create new instance - setting the desired properties
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = psInfo}
'subscribe to events (add event handlers)
AddHandler p.ErrorDataReceived, AddressOf Process_ErrorDataReceived
AddHandler p.OutputDataReceived, AddressOf Process_OutputDataReceived
'start process
p.Start()
p.BeginErrorReadLine() 'begin async reading for standard error
p.BeginOutputReadLine() 'begin async reading for standard output
'waits until the process is finished before continuing
p.WaitForExit()
'unsubscribe from events (remove event handlers)
RemoveHandler p.ErrorDataReceived, AddressOf Process_ErrorDataReceived
RemoveHandler p.OutputDataReceived, AddressOf Process_OutputDataReceived
End Using
Catch ex As System.ComponentModel.Win32Exception
errMsg = "Error (Win32Exception): " & ex.Message
Debug.WriteLine(errMsg)
'set value
ProcessError = errMsg
Throw ex
Catch ex As Exception
errMsg = "Error: " & ex.Message
Debug.WriteLine(errMsg)
'set value
ProcessError = errMsg
Throw ex
End Try
If Not String.IsNullOrEmpty(ProcessError) Then
'raise event
RaiseEvent ProcessCompleted(Me, False)
Return "Error: " & ProcessError
Else
'raise event
RaiseEvent ProcessCompleted(Me, True)
End If
Return "Success"
End Function
Private Sub Process_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs)
'ToDo: add desired code
If Not String.IsNullOrEmpty(e.Data) Then
Debug.WriteLine("Process_ErrorDataReceived: " & e.Data)
If Not String.IsNullOrEmpty(ProcessError) Then
'add space
ProcessError += " "
End If
'append
ProcessError += e.Data
'raise event
RaiseEvent ErrorDataReceived(Me, e.Data)
End If
End Sub
Private Sub Process_OutputDataReceived(sender As Object, e As DataReceivedEventArgs)
'ToDo: add desired code
If Not String.IsNullOrEmpty(e.Data) Then
Debug.WriteLine("Process_OutputDataReceived: " & e.Data)
'raise event
RaiseEvent OutputDataReceived(Me, e.Data)
End If
End Sub
End Class
Open Properties Window
In VS menu, select View
Select Properties Window
Open Solution Explorer
In VS menu, select View
Select Solution Explorer
In Solution Explorer, double-click Form1.vb to open the designer.
Add Buttons to Form1
Add "Run" button to Form1
In VS menu, select View
Select Toolbox
Select Button
Click on Form1 to add the button to the form
In Properties Window, for "button1", set (name): btnRun; set Text: Connect
In Properties Window, click (Events). Double-click Click to add event handler to Form1.vb
Add "Button1" button to Form1
In VS menu, select View
Select Toolbox
Select Button
Click on Form1 to add the button to the form
Add "Load" event handler to Form1
In Properties Window, for "Form1"", click (Events). Double-click Load to add event handler to Form1.vb
Modify Form1.vb code
In Solution Explorer, right-click Form1.vb
Select View Code
Form1.vb
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'set value
Button1.Enabled = False
End Sub
Private Sub btnRun_Click(sender As Object, e As EventArgs) Handles btnRun.Click
'set value
Button1.Enabled = False
Dim helper As New HelperProcess
'subscribe to events (add event handlers)
AddHandler helper.ErrorDataReceived, AddressOf Helper_ErrorDataReceived
AddHandler helper.OutputDataReceived, AddressOf Helper_OutputDataReceived
AddHandler helper.ProcessCompleted, AddressOf Helper_ProcessCompleted
'set value
Dim folderName As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
Dim filename As String = System.IO.Path.Combine(folderName, "element.docx")
Dim arguments As String = String.Format("/c dir ""{0}"" | find /v ""Volume"" | find /v ""Directory of"" | find /v ""bytes"" ", filename)
'execute
Dim result As String = helper.RunCmd("cmd", arguments)
'unsubscribe from events (remove event handlers)
RemoveHandler helper.ErrorDataReceived, AddressOf Helper_ErrorDataReceived
RemoveHandler helper.OutputDataReceived, AddressOf Helper_OutputDataReceived
RemoveHandler helper.ProcessCompleted, AddressOf Helper_ProcessCompleted
'set value
helper = Nothing
Debug.WriteLine("result: " & result)
If result = "Success" Then
Button1.Enabled = True
Else
Button1.Enabled = False
End If
End Sub
Private Sub Helper_ErrorDataReceived(sender As Object, data As String)
'ToDo: add desired code
If Not String.IsNullOrEmpty(data) Then
Debug.WriteLine("Helper_ErrorDataReceived: " & data)
End If
End Sub
Private Sub Helper_OutputDataReceived(sender As Object, data As String)
'ToDo: add desired code
If Not String.IsNullOrEmpty(data) Then
Debug.WriteLine("Helper_OutputDataReceived: " & data)
End If
End Sub
Private Sub Helper_ProcessCompleted(sender As Object, hasSuccessfullyCompleted As Boolean)
'ToDo: add desired code and/or uncomment desired code below
Debug.WriteLine("hasSuccessFullyCompleted: " & hasSuccessfullyCompleted.ToString())
If hasSuccessfullyCompleted Then
'Button1.Invoke(New MethodInvoker(Sub()
'Button1.Enabled = True
'End Sub))
'Button1.Invoke(New Action(Sub()
'Button1.Enabled = True
'End Sub))
Else
'Button1.Invoke(New MethodInvoker(Sub()
'Button1.Enabled = False
'End Sub))
'Button1.Invoke(New Action(Sub()
'Button1.Enabled = False
'End Sub))
End If
End Sub
End Class

Custom InputBox Not Recording Response

I have created a custom InputBox using frmInputBox.ShowDialog and using 2 buttons titled "OK" and 'Cancel", with them set to the AcceptButton & CancelButton respectively. This is simply so I can have the InputBox match the formatting of my main Form. However when I enter a value into the TextBox on frmInputBox the form doesn't disappear and my code gets hung up with the InputBox still open.
I tested my code using a custom MessageBox and with all the same options it works perfectly fine. The issue with the InputBox must have something to do with the TextBox not being recorded properly. But I don't see any options in the TextBox control to set it up as a Dialog option.
Here's sample code for my MessageBox:
frmMessageBox.lblMessageText.Text = "Would You Like To Clear The Event Log?"
frmMessageBox.ShowDialog()
If frmMessageBox.DialogResult = DialogResult.OK Then
txtEventLog.Clear()
Else
Exit Sub
End If
Here's sample code for my InputBox:
frmInputBox.lblDialogText.Text = "Enter Number of times this program should be executed:"
frmInputBox.ShowDialog()
If frmInputBox.DialogResult = DialogResult.OK Then
ProgramCounter = frmInputBox.txtDialogInput.Text
Else
Exit Sub
End If
Is there something I'm missing that I need to do with the InputBox in order to get it to act the way I am expecting it to?
For messagebox, I have answer before, and for inputbox, same as messagebox you need a module1:
Module Module1
Public theResult As String
Public Function myInputBox(ByVal promptText As String) As String
InputBoxForm.lblPrompt.Text = promptText
InputBoxForm.ShowDialog()
myInputBox = theResult
End Function
end Module
You need inputbox form:
Public Class InputBoxForm
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
theResult = txtInputResponse.Text
Me.Close()
Me.Dispose()
End Sub
Private Sub btnCancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
theResult = ""
Me.Close()
Me.Dispose()
End Sub
End Class
And then you can call your input box like this:
Dim theRslt as String = myInputBox("Enter Number of times this program should be executed:")
MsgBox(theRslt)

Have to Click Form To Give Focus

I have a main form with buttons that open a custom message box form. It works fine if it's just a message and the user just needs to click OK. But if the answer to that message box is important, like "Are you sure you want to delete this file?" I use a while loop to wait for the user to respond and once they do then a flag is set from false to true and the response is recorded.
For some reason any response that uses a while loop to wait is causing the message box form to not have focus after being called. Requiring the user to first click on the form, and then click on OK.
So far I've tried using form.Activate() instead of form.Show(), as well as calling Application.DoEvents() inside the while loop since I believed the while loop was taking focus away from the message form immediately after being called. Neither solved the issue.
Code from a message box that works as intended:
If cmbLoadProgram.SelectedItem = "" Then
frmMessageBox.lblHeader.Text = "Set-Up"
frmMessageBox.lblMessageText.Text = "No Program Selected!"
frmMessageBox.Show()
Exit Sub
End If
Code from a message box that needs to be clicked twice:
If btnGetHexStart.Visible = False And cmbStartCondition.SelectedItem = "Pixel" Then
frmMessageBox.lblHeader.Text = "Hex Set-Up"
frmMessageBox.lblMessageText.Text = "Reset Hex Code Data?"
frmMessageBox.Show()
Me.Hide()
While Flag = False
If frmMain.OKCancel = "OK" Then
btnGetHexStart.Visible = True
btnGetHexStart.Enabled = True
btnGetHexStart.PerformClick()
Flag = True
End If
frmMain.delay(20)
End While
End If
I'm wanting both options to only need to be clicked once in order to confirm or cancel the action. Instead of the while loop questions needing to be clicked twice.
This just an idea from me, just how to open msgboxform, here we need MessageForm and one module1, for example:
Public Class MessageForm
'You can assign any variable to show any data in messageform display
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click
theResult = MsgBoxResult.Ok
Me.Close()
Me.Dispose()
End Sub
Private Sub bttcancel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bttcancel.Click
theResult = MsgBoxResult.Cancel
Me.Close()
Me.Dispose()
End Sub
End Class
Module Module1
Public theResult As MsgBoxResult
'You can add some parameter here to submit to MessageForm
Public Function myMessageBox() As MsgBoxResult
myMessageBox = MsgBoxResult.Cancel
MessageForm.ShowDialog()
myMessageBox = theResult
End Function
End Module
and then you can call the myMessageBox anywhere like this:
'if myMessageBox procedure have parameter, apply the paramters too
dim myRslt = myMessageBox()
You don't need to create a form,You can use DialogResult and MessageBox.Show, code:
Dim Result As DialogResult = MessageBox.Show("Set-Up" & vbCrLf & "No Program Selected!", "Warning", MessageBoxButtons.OKCancel)
If Result = DialogResult.OK Then
ElseIf Result = DialogResult.Cancel Then
End If

How to check if a folder exists automatically without ".click" function

I have a problem. I have a program written in Visual Basic using Visual Studio 2013 and it works good. The problem is that I want the program to check if a folder exists on program start-up and return a text value in a label without having to manually "click" the label. I have searched and cannot find anything, maybe I'm not searching for the right thing?
Here is a sample of the check:
''//ASDG_JR check
Private Sub lbl_asdg_jr_pres_Click(sender As Object, e As EventArgs) Handles lbl_asdg_jr_pres.Click
If My.Computer.FileSystem.DirectoryExists(user_txt_dir.Text & "\#ASDG_JR_v0.14") Then
lbl_asdg_jr_pres.Text = "All good!"
btn_asdg_di.Text = ".zip file downloaded already"
btn_asdg_unzip.Text = "unzipped and installed already"
btn_asdg_di.Enabled = False
btn_asdg_unzip.Enabled = False
Else
lbl_asdg_jr_pres.Text = "Use buttons ----->"
btn_asdg_di.Enabled = True
btn_asdg_unzip.Enabled = True
End If
End Sub
Place your code inside the form's load event.
Private Sub myForm_Load(sender As System.Object, e As System.EventArgs) Handles myForm.Load
'If directoryExists Then update label
End Sub

Console application doesn't want to read standard input

I am writing an application to manage other console application(game server - jampded.exe)
When it's running in console it writes data and reads commands with no problem.
In my application I redirected standard I/O to StreamWriter and StreamReader
Public out As StreamReader
Public input As StreamWriter
Dim p As New Process()
p.StartInfo.FileName = My.Application.Info.DirectoryPath & "\" &
TextBox6.Text 'PATH TO JAMPDED.EXE
p.StartInfo.Arguments = TextBox1.Text 'EXTRA PARAMETERS
p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardInput = True
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.UseShellExecute = False
p.Start()
input = p.StandardInput
out = p.StandardOutput
Dim thr As Thread = New Thread(AddressOf updatetextbox)
thr.IsBackground = True
thr.Start()
Sub updatetextbox()
While True
While Not out.EndOfStream
RichTextBox1.AppendText(out.ReadLine())
RichTextBox1.AppendText(vbNewLine)
End While
End While
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) _
Handles Button2.Click
input.WriteLine(TextBox4.Text)
TextBox4.Text = ""
input.Flush()
End Sub
When I am pressing Button2 that should write to STD/I text from my textbox, jampded.exe acts like it wasn't written. Also Output works well at startup, after that new lines are added rarely when there is a lot data in buffer.
Am I doing something wrong, or is it the application's fault?
For the standard input question:
Are you certain that the application you're starting is reading data from standard input (and not trapping keyboard events or something)? To test this, put some text that you're trying to send to the application in a text file (named, for example, commands.txt). Then send it to the application from a command prompt like so:
type commands.txt | jampded.exe
If that application reads those commands, then it is indeed reading from standard input. If it isn't, then redirecting standard input isn't going to help you get data to that application.
For the standard output question:
Instead of launching your own thread to handle the data coming from the other application, I would suggest doing something like this:
AddHandler p.OutputDataReceived, AddressOf OutputData
p.Start()
p.BeginOutputReadLine()
Private Sub AddLineToTextBox(ByVal line As String)
RichTextBox1.AppendText(e.Data)
RichTextBox1.AppendText(vbNewLine)
End Sub
Private Delegate Sub AddLineDelegate(ByVal line As String)
Private Sub OutputData(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
If IsNothing(e.Data) Then Exit Sub
Dim d As AddLineDelegate
d = AddressOf AddLineToTextBox
Invoke(d, e.Data)
End Sub
The Invoke call is required because OutputData may get called on a different thread, and UI updates all have to happen on the UI thread.
I've seen the same issue with data coming in batches when reading from the StandardOutput stream directly. The asynchronous read + event handler combo fixed it.