I have the following code to create a combo box that shows a list of ports in use on the PC
What I want to do is take the users choice of port and assign it to a system variable using the SETX command.
At the moment the user enters the port number manually in a batch file, the aim is to skip this step and replace it with the choice from the combo box
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
myPort = IO.Ports.SerialPort.GetPortNames()
ComboBox1.Items.AddRange(myPort)
The batch file looks like this at present and works fine
set /p port= "What is your port number "
setx port "%port%"
So is there a way to remove the user having to enter the port number and have the batch file run its script using the choice from the combo box
Thanks
According to this post
set modifies the current shell's (the window's) environment values,
and the change is available immediately, but it is temporary. The
change will not affect other shells that are running, and as soon as
you close the shell, the new value is lost until such time as you run
set again.
setx modifies the value permanently, which affects all future shells,
but does not modify the environment of the shells already running. You
have to exit the shell and reopen it before the change will be
available, but the value will remain modified until you change it
again.
For set /p port= "What is your port number: "
one can use the following in VB.NET:
Environment.SetEnvironmentVariable("port", "COM5", EnvironmentVariableTarget.Process)
where "COM5" is your port number (replace this with the value from your ComboBox).
For setx port "%port%":
Permanent environment variables are stored in the registry: HKEY_CURRENT_USER\Environment.
To set an environment variable in HKEY_CURRENT_USER\Environment, do one of the following.
Option 1:
'by specifying 'EnvironmentVariableTarget.User', the value will be updated in the registry (HKEY_CURRENT_USER\Environment)
Environment.SetEnvironmentVariable("port", "COM5", EnvironmentVariableTarget.User)
where "COM5" is your port number (replace this with the value from your ComboBox).
Option 2:
Add Imports
Imports Microsoft.Win32
SetUserEnvironmentVarReg:
Private Sub SetUserEnvironmentVarReg(name As String, val As String, Optional regView As RegistryView = RegistryView.Registry64)
Using currentUserKey As RegistryKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, regView)
Using envKey As RegistryKey = currentUserKey.OpenSubKey("Environment", True)
If envKey IsNot Nothing Then
'set value
envKey.SetValue(name, val)
End If
End Using
End Using
End Sub
Usage:
SetUserEnvironmentVarReg("port", "COM5")
where "COM5" is your port number (replace this with the value from your ComboBox).
Let's create a test batch script. The test batch script will allow one command-line argument which will allow it to be used with the different options shown below. If a command-line argument is supplied, the batch script will used the specified value, otherwise it will use the value inherited from the environment variable.
TestEnvVar.bat:
#echo off
if not "%1" equ "" (
set port=%1
)
echo port: %port%
One can use System.Diagnostics.Process to run the batch script.
RunProcessInheritEnvironmentVar:
Private Sub RunProcessInheritEnvironmentVar(filename As String, portName As String)
'set environment variable (process-level)
Environment.SetEnvironmentVariable("port", portName, EnvironmentVariableTarget.Process)
'set environment variable (user-level)
'sets environment variable in HKEY_CURRENT_USER\Environment
Environment.SetEnvironmentVariable("port", portName, EnvironmentVariableTarget.User)
Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}
AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("error: " & e.Data)
End If
End Sub
AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("output: " & e.Data)
End If
End Sub
p.Start()
p.BeginErrorReadLine()
p.BeginOutputReadLine()
'wait for exit
p.WaitForExit()
End Using
End Sub
Usage:
RunProcessInheritEnvironmentVar("C:\Temp\TestEnvVar.bat", "COM5")
where "COM5" is your port number (replace this with the value from your ComboBox).
It's not clear whether or not you actually need to set the environment variable permanently (ie: in HKEY_CURRENT_USER\Environment). If the environment variable is only being used for the batch script being executed, one may consider one of the alternate methods shown below.
When using System.Diagnostics.Process an environment variable can be set using ProcessStartInfo.EnvironmentVariables which sets the environment variable within the scope of the System.Diagnostics.Process process. The code below doesn't change the value in the registry.
RunProcessSetEnvironmentVar :
Private Sub RunProcessSetEnvironmentVar(filename As String, portName As String)
Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}
'add environment variable
startInfo.Environment.Add("port", portName)
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}
AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("error: " & e.Data)
End If
End Sub
AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("output: " & e.Data)
End If
End Sub
p.Start()
p.BeginErrorReadLine()
p.BeginOutputReadLine()
'wait for exit
p.WaitForExit()
End Using
End Sub
Usage:
RunProcessSetEnvironmentVar("C:\Temp\TestEnvVar.bat", "COM5")
where "COM5" is your port number (replace this with the value from your ComboBox).
Lastly, let's look at how one could specify the port name as an argument because the test batch script allows command-line arguments. The code below doesn't change the value in the registry.
RunProcessWithArgument:
Private Sub RunProcessWithArgument(filename As String, Optional arguments As String = Nothing)
Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}
If Not String.IsNullOrEmpty(arguments) Then
startInfo.Arguments = arguments
End If
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}
AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("error: " & e.Data)
End If
End Sub
AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("output: " & e.Data)
End If
End Sub
p.Start()
p.BeginErrorReadLine()
p.BeginOutputReadLine()
'wait for exit
p.WaitForExit()
End Using
End Sub
Usage
RunProcessWithArgument("C:\Temp\TestEnvVar.bat", "COM5")
where "COM5" is your port number (replace this with the value from your ComboBox).
I've shown a variety of methods that can be used to specify an environment variable that can be used for a batch script depending upon your needs. For a slight variation of System.Diagnostics.Process usage see here.
Update:
When using System.Diagnostics.Process, one can also use StandardInput to provide value(s) for prompt(s).
TestEnvVar.bat:
#echo off
set /p port= "What is your port number: "
setx port "%port%"
echo port: %port%
for /f "delims=" %%a in ('^(reg query "HKCU\Environment" /v "port" ^|find /i "port"^)') do (
echo %%a
)
RunProcessWithStandardInput:
Private Sub RunProcessWithStandardInput(filename As String, portName As String)
If String.IsNullOrEmpty(filename) Then
Throw New Exception("Error: 'filename' is null or empty.")
ElseIf Not System.IO.File.Exists(filename) Then
Throw New Exception($"Error: '{filename}' could not be found.")
End If
If String.IsNullOrEmpty(portName) Then
Throw New Exception("Error: 'portName' is null or empty.")
End If
Dim startInfo As ProcessStartInfo = New ProcessStartInfo(filename) With {.CreateNoWindow = True, .RedirectStandardError = True, .RedirectStandardInput = True, .RedirectStandardOutput = True, .UseShellExecute = False, .WindowStyle = ProcessWindowStyle.Hidden}
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = startInfo}
AddHandler p.ErrorDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("error: " & e.Data)
End If
End Sub
AddHandler p.OutputDataReceived, Sub(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("output: " & e.Data)
End If
End Sub
p.Start()
p.BeginErrorReadLine()
p.BeginOutputReadLine()
Using sw As System.IO.StreamWriter = p.StandardInput
'provide values for each input prompt
'ToDo: add values for each input prompt - changing the for loop as necessary
'Note: Since we only have 1 prompt, using a loop Is unnecessary - a single 'WriteLine' statement would suffice
'if there are additional prompts add them below; else if (i = 1)...
For i As Integer = 0 To 1
If i = 0 Then
'write port name to StandardInput
sw.WriteLine(portName)
End If
Next
End Using
'wait for exit
p.WaitForExit()
End Using
End Sub
Usage:
RunProcessWithStandardInput("C:\Temp\TestEnvVar.bat", "COM5")
where "COM5" is your port number (replace this with the value from your ComboBox).
Additional Resources:
Environment.GetEnvironmentVariable
Environment.SetEnvironmentVariable
RegistryKey Class
Related
It is necessary to execute the batch file command -
qwinsta /server:NamePC
and display the result in a DataGridView.
I'm using the following code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim p As New Process
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = "/c qwinsta /server:" & TextBox1.Text & ""
p.StartInfo.UseShellExecute = False
p.StartInfo.CreateNoWindow = True
p.StartInfo.RedirectStandardOutput = True
p.StartInfo.RedirectStandardError = True
p.StartInfo.StandardOutputEncoding = System.Text.Encoding.GetEncoding(866)
p.StartInfo.StandardErrorEncoding = System.Text.Encoding.GetEncoding(866)
p.Start()
Dim Result As String = p.StandardOutput.ReadToEnd
If Result.Length < 1 Then
MsgBox(p.StandardError.ReadToEnd)
Exit Sub
End If
p.WaitForExit(3000)
SplitOutput(Result)
End Sub
Sub SplitOutput(ByVal text As String)
Try
Dim source() As String = Split(text, Chr(13))
For i = 0 To UBound(source)
Dim r() As String = source(i).Split(New Char() {" "}, StringSplitOptions.RemoveEmptyEntries)
DataGridView1.Rows.Add(r(0), r(1), r(2), r(3), r(4), r(5))
Next
Catch ex As Exception
End Try
End Sub
But the output procedure in the DataGridView does not work correctly. It displays only the title of the result.
Most likely I'm not splitting the line correctly. What could be wrong?
It's not necessary to use cmd.exe. However, on 64-bit Windows, "qwinsta.exe" only exists in %windir%\System32 - not in %windir%\SysWOW64.
If your application is running as 32-bit on 64-bit Windows, it's necessary to use Sysnative in the path:
Dim qwistaPath As String = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "qwinsta.exe")
If your application is running as 64-bit on 64-bit Windows (or 32-bit on 32-bit Windows), use the following:
Dim qwistaPath As String = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "qwinsta.exe")
Try the following:
Private Sub GetQwinstaInfo(Optional arguments As String = Nothing, Optional encoding As System.Text.Encoding = Nothing)
Debug.WriteLine("Is64BitProcess: " & Environment.Is64BitProcess.ToString())
Dim qwistaPath As String
'environment variable windir has the same value as SystemRoot
'use 'Sysnative' to access 64-bit files (in System32) if program is running as 32-bit process
'use 'SysWow64' to access 32-bit files on 64-bit OS
'on 64-bit OS, 'qwinsta.exe' only exists in %windir%\System32 - not in SysWOW64
If Environment.Is64BitOperatingSystem AndAlso Not Environment.Is64BitProcess Then
qwistaPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "Sysnative", "qwinsta.exe")
Else
qwistaPath = System.IO.Path.Combine(Environment.GetEnvironmentVariable("windir"), "System32", "qwinsta.exe")
End If
Dim psInfo As ProcessStartInfo = New ProcessStartInfo(qwistaPath)
psInfo.Arguments = arguments 'arguments
psInfo.CreateNoWindow = True 'don't create a window
If encoding IsNot Nothing Then
psInfo.StandardErrorEncoding = encoding
psInfo.StandardOutputEncoding = encoding
End If
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
'create new instance and set properties
Using p As Process = New Process() With {.EnableRaisingEvents = True, .StartInfo = psInfo}
'subscribe to events (add event handlers)
AddHandler p.ErrorDataReceived, AddressOf P_ErrorDataReceived
AddHandler p.OutputDataReceived, AddressOf P_OutputDataReceived
'start
p.Start()
'begin async reading for both standard error and standard output
p.BeginErrorReadLine()
p.BeginOutputReadLine()
'wait until the process is finished before continuing
p.WaitForExit()
'unsubscribe from events (remove event handlers)
RemoveHandler p.ErrorDataReceived, AddressOf P_ErrorDataReceived
RemoveHandler p.OutputDataReceived, AddressOf P_OutputDataReceived
End Using
'ToDo: add desired code
End Sub
Private Sub P_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("error: " & e.Data)
End If
End Sub
Private Sub P_OutputDataReceived(sender As Object, e As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(e.Data) Then
'ToDo: add desired code
Debug.WriteLine("output: " & e.Data)
End If
End Sub
Usage - Example #1:
GetQwinstaInfo()
Usage - Example #2:
GetQwinstaInfo(arguments:="/server:myServerNameOrPCName")
Usage - Example #3:
GetQwinstaInfo("/server:myServerNameOrPCName", System.Text.Encoding.GetEncoding(866))
I'm using this code:
Shared sb_OutputData As New StringBuilder()
Shared sb_ErrorData As New StringBuilder()
Shared proc As Process = Nothing
Private Sub cmd()
If proc IsNot Nothing Then
Exit Sub
End If
Dim info As New ProcessStartInfo("cmd.exe")
' Redirect the standard output of the process.
info.RedirectStandardOutput = True
info.RedirectStandardInput = True
info.RedirectStandardError = True
' Set UseShellExecute to false for redirection
info.UseShellExecute = False
proc = New Process
proc.StartInfo = info
' Set our event handler to asynchronously read the sort output.
AddHandler proc.OutputDataReceived, AddressOf proc_OutputDataReceived
AddHandler proc.ErrorDataReceived, AddressOf proc_ErrorDataReceived
proc.Start()
' Start the asynchronous read of the sort output stream. Note this line!
proc.BeginOutputReadLine()
proc.BeginErrorReadLine()
End Sub
Private Shared Sub proc_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs)
'Console.WriteLine("Error data: {0}", e.Data)
sb_ErrorData.AppendLine(e.Data)
End Sub
Private Shared Sub proc_OutputDataReceived(sender As Object, e As DataReceivedEventArgs)
' Console.WriteLine("Output data: {0}", e.Data)
sb_OutputData.AppendLine(e.Data)
End Sub
Sub CmdWrite(arguments As String)
Dim writeStream As StreamWriter = proc.StandardInput
writeStream.WriteLine(arguments)
End Sub
It works exactly as I want, be able to retrieve cmd output and error data without closing it (and asynchronously), however, I'm not able to know when the command is finished executing. I'd like to know when it reaches the end of the stream for me to grab all the output and do something with it...
I've been searching for quite long, and can't find an answer to this.
Help please?
The stream is open is long as the command window is open. You can't tell when they stop or start. If the command you're running doesn't indicate its end with a unique/detectable pattern, you're stuck, unless you can edit the script you're running and insert a string - which you can't guarantee won't show in the normal output - in between the command calls.
I need to automate usage of a command line utility in VB.net. here is an example.
From the code, I need to decrypt a file using command line utility. Here is the command line procedure.
You start utility using this line
C:\gnupg>gpg --decrypt c:\temp\File_Encr.xml
Once executed, then it shows this
You need a passphrase to unlock the secret key for
user: "xxxx <abc#def.com>"
1024-bit ELG-E key, ID ABCD, created 2013-10-25 (main key ID DEF)
Enter passphrase:
and when you enter the passphrase, it do the job.
I need start this process from the code (VB.NET) and input passphrase as well so that it doesn't require any user interaction. My code will be used in Windows Services as well as Web application.
Can someone help on this please?
Thank you.
Sameers
Here is a code snippet I use.
All output code is written to the Visual Studio debug window for convenience. All program output all redirected to output handlers. This allows you to inspect the output coming out of the program in "real time". If you needed to watch the output lines and scan for a certain phrase and then perform an action, you could easily do this in Sub OutputReceivedHandler().
I tried to make it generic so you can see how it works:
Imports Microsoft.VisualBasic
Imports System.Diagnostics
Imports System
Public Class ExternalUtilities
Private myprocess As Process
Private SW As System.IO.StreamWriter
Private myprocess_HasError As Boolean = False
Private myprocess_ErrorMsg As String = ""
Private myprocess_Output As String = ""
Public Sub New()
' do init stuff here.
' maybe pass the executable path, or command line args.
End Sub
Public Sub launchUtilityProcess1()
Dim executeableFullPath As String = "C:\Path\To\file.exe"
' define the process
myprocess = New Process
myprocess.StartInfo.FileName = executeableFullPath
myprocess.StartInfo.RedirectStandardInput = True
myprocess.StartInfo.RedirectStandardOutput = True
myprocess.StartInfo.RedirectStandardError = True
myprocess.StartInfo.UseShellExecute = False
myprocess.StartInfo.CreateNoWindow = True
myprocess.StartInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(executeableFullPath)
myprocess.StartInfo.Arguments = "--decrypt c:\temp\File_Encr.xml"
' add handlers to monitor various conditions
myprocess.EnableRaisingEvents = True
AddHandler myprocess.OutputDataReceived, AddressOf OutputReceivedHandler
AddHandler myprocess.ErrorDataReceived, AddressOf ErrorReceivedHandler
AddHandler myprocess.Exited, AddressOf ExitedHandler
' launch
Try
myprocess.Start()
' redirect this processes IO
SW = myprocess.StandardInput
SW.AutoFlush = True
' use asynchronous reading so buffers dont fill up
myprocess.BeginOutputReadLine()
myprocess.BeginErrorReadLine()
' wait for program to end
myprocess.WaitForExit()
myprocess.Close()
SW.Close()
myprocess.Dispose()
' check for errors
If myprocess_ErrorMsg "" Then
' something bad happened, handle it.
End If
Catch ex As Exception
' something bad happened, handle it.
End Try
End Sub
Private Sub OutputReceivedHandler(ByVal sendingProcess As Object, ByVal line As DataReceivedEventArgs)
If Not line.Data Is Nothing Then
If line.Data = "string to search for..." Then
' when this string is detected, send more output
SW.Write("helloworld" & System.Environment.NewLine)
ElseIf line.Data = "something else..." Then
' when this string is detected, send more output
SW.Write("goodbyeworld" & System.Environment.NewLine)
End If
Debug.WriteLine(line.Data)
End If
End Sub
Private Sub ErrorReceivedHandler(ByVal sendingProcess As Object, ByVal line As DataReceivedEventArgs)
Debug.WriteLine(line.Data)
' These executables send newlines on the STDERR path, ignore newlines
If line.Data <> "" Then
myprocess_HasError = True
myprocess_ErrorMsg = line.Data
End If
End Sub
Private Sub ExitedHandler(ByVal sender As Object, ByVal e As System.EventArgs)
Debug.WriteLine("Process Exited")
End Sub
End Class
I run multiple exe with different names by using Process.start() and enables Raisingevents to True. To check the status of Process, I intimated User at Process Exited event and show a message to user.
But the problem is I want to show the particular Exe Name to User at that Exit event. My code for For process Start is:
Private Sub StartExe()
Private psi As ProcessStartInfo
Private cmd As Process
Dim filePath As String = "vision.exe"
psi = New ProcessStartInfo(filePath)
Dim systemencoding As System.Text.Encoding = _
System.Text.Encoding.GetEncoding(Globalization.CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
With psi
.Arguments = "Some Input String"
.UseShellExecute = False
.RedirectStandardError = True
.RedirectStandardOutput = True
.RedirectStandardInput = True
.CreateNoWindow = False
.StandardOutputEncoding = systemencoding
.StandardErrorEncoding = systemencoding
End With
cmd = New Process With {.StartInfo = psi, .EnableRaisingEvents = True}
AddHandler cmd.ErrorDataReceived, AddressOf Async_Data_Received
AddHandler cmd.OutputDataReceived, AddressOf Async_Data_Received
AddHandler cmd.Exited, AddressOf processExited
cmd.Start()
cmd.BeginOutputReadLine()
cmd.BeginErrorReadLine()
End Sub
'For Receiving the Output of Exe, I used a TextBox to view
Private Sub Async_Data_Received(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Me.Invoke(New InvokeWithString(AddressOf Sync_Output1), e.Data)
End Sub
Private Sub Sync_Output1(ByVal text As String)
txtLog.AppendText(text & Environment.NewLine)
End Sub
'At the Exit event, I inform the user that an Exe exited due to some reason etc.
Private Sub processExited(ByVal sender As Object, ByVal e As EventArgs)
Me.BeginInvoke(New Action(Function()
MessageBox.Show("The Stream for " &Particular Exe& "is Exited.", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
End Function))
End Sub
At the Process Exited Event, how can I show name of that particular Exe which fired that event. Like in that particular code, I started an "vision.exe", So I want to inform the user that vision.exe is terminated due to some reason etc.
By the time the Exited event runs, the process is already dead and you cannot retrieve its properties anymore. Since you already use lambda expressions, you can solve this one too by writing one that captures the filePath variable. Like this:
AddHandler cmd.Exited,
Sub(s, e)
Me.BeginInvoke(New Action(
Sub()
MessageBox.Show(filePath + " has ended")
End Sub))
End Sub
Do beware that you'll have to keep your Form object alive until the process terminates or your own program exits. If you don't then the BeginInvoke() call is going to be made on a disposed form, also a problem in your original code. You can avoid this by checking Me.InvokeRequired. If it returns false then don't do anything.
ok, please do no laugh at this :x
i'm trying to create a simple software testing tool in VB.NET
i created a simple C program PROG.EXE which scans a number and prints the OUTPUT, and started building my tester, it should execute PROG.EXE output.txt, so PROG.EXE takes input from input.txt and prints the output to output.txt
but i failed, at first i tried Process.start then shell but nothing worked !
so i did this trick, the VB.NET codes generate a batch file with this codes PROG.EXE output.txt, but again i failed, though the VB.NET created the batch file and executes too, but nothing happened ! but when i manually run the batch file i got success !
i tried executing the batchfile then sendkey the VBCR/LF/CRLF still nothing happens !
whats wrong ?
My VB.NET Code, i am using Visual Studio 2010 Professional
Option Explicit On
Option Strict On
Public Class Form1
Dim strFileName As String
Private Sub btnRun_Click() Handles btnRun.Click
Dim strOutput As String
Using P As New Process()
P.StartInfo.FileName = strFileName
P.StartInfo.Arguments = txtInput.Text
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.UseShellExecute = False
P.StartInfo.WindowStyle = ProcessWindowStyle.Hidden ' will this hide the console ?
P.Start()
Using SR = P.StandardOutput
strOutput = SR.ReadToEnd()
End Using
End Using
txtOutput.Text = strOutput
End Sub
Private Sub btnTarget_Click() Handles btnTarget.Click
dlgFile.ShowDialog()
strFileName = dlgFile.FileName
lblFileName.Text = strFileName
End Sub
End Class
And this is my C code
#include<stdio.h>
#include<conio.h>
void main()
{
int x;
scanf("%d",&x);
printf("%d",(x*x));
}
my program runs perfectly when i run prog.exe <input.txt> output.txt in console
Below is a fully working example. You want to use the Process class as you tried but you need to RedirectStandardOutput on the process's StartInfo. Then you can just read the process's StandardOutput. The sample below is written using VB 2010 but works pretty much the same for older versions.
Option Explicit On
Option Strict On
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//This will hold the entire output of the command that we are running
Dim T As String
''//Create our process object
Using P As New Process()
''//Pass it the EXE that we want to execute
''//NOTE: you might have to use an absolute path here
P.StartInfo.FileName = "ping.exe"
''//Pass it any arguments needed
''//NOTE: if you pass a file name as an argument you might have to use an absolute path
P.StartInfo.Arguments = "127.0.0.1"
''//Tell the process that we want to handle the commands output stream
''//NOTE: Some programs also write to StandardError so you might want to watch that, too
P.StartInfo.RedirectStandardOutput = True
''//This is needed for the previous line to work
P.StartInfo.UseShellExecute = False
''//Start the process
P.Start()
''//Wrap a StreamReader around the standard output
Using SR = P.StandardOutput
''//Read everything from the stream
T = SR.ReadToEnd()
End Using
End Using
''//At this point T will hold whatever the process with the given arguments kicked out
''//Here we are just dumping it to the screen
MessageBox.Show(T)
End Sub
End Class
EDIT
Here is an updated version that reads from both StandardOutput and StandardError. This time it reads asynchronously. The code calls the CHOICE exe and passes an invalid command line switch which will trigger writing to StandardError instead of StandardOutput. For your program you should probably monitor both. Also, if you're passing a file into the program make sure that you are specifying the absolute path to the file and make sure that if you have spaces in the file path that you are wrapping the path in quotes.
Option Explicit On
Option Strict On
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//This will hold the entire output of the command that we are running
Dim T As String
''//Create our process object
Using P As New Process()
''//Pass it the EXE that we want to execute
''//NOTE: you might have to use an absolute path here
P.StartInfo.FileName = "choice"
''//Pass it any arguments needed
''//NOTE: if you pass a file name as an argument you might have to use an absolute path
''//NOTE: I am passing an invalid parameter to show off standard error
P.StartInfo.Arguments = "/G"
''//Tell the process that we want to handle the command output AND error streams
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardError = True
''//This is needed for the previous line to work
P.StartInfo.UseShellExecute = False
''//Add handlers for both of the data received events
AddHandler P.ErrorDataReceived, AddressOf ErrorDataReceived
AddHandler P.OutputDataReceived, AddressOf OutputDataReceived
''//Start the process
P.Start()
''//Start reading from both error and output
P.BeginErrorReadLine()
P.BeginOutputReadLine()
''//Signal that we want to pause until the program is done running
P.WaitForExit()
Me.Close()
End Using
End Sub
Private Sub ErrorDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Trace.WriteLine(String.Format("From Error : {0}", e.Data))
End Sub
Private Sub OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Trace.WriteLine(String.Format("From Output : {0}", e.Data))
End Sub
End Class
Its important that you put your entire file path in quotes if it has spaces in it (in fact, you should always enclose it in quotes just in case.) For instance, this won't work:
P.StartInfo.FileName = "attrib"
P.StartInfo.Arguments = "C:\Program Files\Windows NT\Accessories\wordpad.exe"
But this will:
P.StartInfo.FileName = "attrib"
P.StartInfo.Arguments = """C:\Program Files\Windows NT\Accessories\wordpad.exe"""
EDIT 2
Okay, I'm an idiot. I thought you were just wrapping a filename in angled brackets like <input.txt> or [input.txt], I didn't realize that you were using actual stream redirectors! (A space before and after input.txt would have helped.) Sorry for the confusion.
There are two ways to handle stream redirection with the Process object. The first is to manually read input.txt and write it to StandardInput and then read StandardOutput and write that to output.txt but you don't want to do that. The second way is to use the Windows command interpreter, cmd.exe which has a special argument /C. When passed it executes any string after it for you. All stream redirections work as if you typed them at the command line. Its important that whatever command you pass gets wrapped in quotes so along with the file paths you'll see some double-quoting. So here's a version that does all that:
Option Explicit On
Option Strict On
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//Full path to our various files
Dim FullExePath As String = "C:\PROG.exe"
Dim FullInputPath As String = "C:\input.txt"
Dim FullOutputPath As String = "C:\output.txt"
''//This creates our command using quote-escaped paths, all completely wrapped in an extra set of quotes
''//""C:\PROG.exe" < "C:\input.txt" > "C:\output.txt""
Dim FullCommand = String.Format("""""{0}"" < ""{1}"" > ""{2}""""", FullExePath, FullInputPath, FullOutputPath)
''//Create our process object
Using P As New Process()
''//We are going to use the command shell and tell it to process our command for us
P.StartInfo.FileName = "cmd"
''//The /C (capitalized) means "execute whatever else is passed"
P.StartInfo.Arguments = "/C " & FullCommand
''//Start the process
P.Start()
''//Signal to wait until the process is done running
P.WaitForExit()
End Using
Me.Close()
End Sub
End Class
EDIT 3
The entire command argument that you pass to cmd /C needs to be wrapped in a set of quotes. So if you concat it it would be:
Dim FullCommand as String = """""" & FullExePath & """" & " <""" & FullInputPath & """> " & """" & FullOutputPath & """"""
Here's what the actual command that you pass should look like:
cmd /C ""C:\PROG.exe" < "C:\INPUT.txt" > "C:\output.txt""
Here's a full code block. I've added back the error and output readers just in case you're getting a permission error or something. So look at the Immediate Window to see if any errors are kicked out. If this doesn't work I don't know what to tell you.
Option Explicit On
Option Strict On
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
''//Full path to our various files
Dim FullExePath As String = "C:\PROG.exe"
Dim FullInputPath As String = "C:\INPUT.txt"
Dim FullOutputPath As String = "C:\output.txt"
''//This creates our command using quote-escaped paths, all completely wrapped in an extra set of quotes
Dim FullCommand As String = """""" & FullExePath & """" & " <""" & FullInputPath & """> " & """" & FullOutputPath & """"""
Trace.WriteLine("cmd /C " & FullCommand)
''//Create our process object
Using P As New Process()
''//We are going to use the command shell and tell it to process our command for us
P.StartInfo.FileName = "cmd"
''//Tell the process that we want to handle the command output AND error streams
P.StartInfo.RedirectStandardError = True
P.StartInfo.RedirectStandardOutput = True
''//This is needed for the previous line to work
P.StartInfo.UseShellExecute = False
''//Add handlers for both of the data received events
AddHandler P.ErrorDataReceived, AddressOf ErrorDataReceived
AddHandler P.OutputDataReceived, AddressOf OutputDataReceived
''//The /C (capitalized) means "execute whatever else is passed"
P.StartInfo.Arguments = "/C " & FullCommand
''//Start the process
P.Start()
''//Start reading from both error and output
P.BeginErrorReadLine()
P.BeginOutputReadLine()
''//Signal to wait until the process is done running
P.WaitForExit()
End Using
Me.Close()
End Sub
Private Sub ErrorDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Trace.WriteLine(String.Format("From Error : {0}", e.Data))
End Sub
Private Sub OutputDataReceived(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Trace.WriteLine(String.Format("From Output : {0}", e.Data))
End Sub
End Class
Public Class attributeclass
Public index(7) As ctrarray
End Class
Public Class ctrarray
Public nameclass As String
Public ctrlindex(10) As ctrlindexclass
End Class
Public Class ctrlindexclass
Public number As Integer
Public names(10) As String
Public status(10) As Boolean
Sub New()
number = 0
For i As Integer = 0 To 10
names(i) = "N/A"
status(i) = False
Next
End Sub
End Class
Public attr As New attributeclass
Sub Main()
attr.index(1).nameclass = "adfdsfds"
System.Console.Write(attr.index(1).nameclass)
System.Console.Read()
End Sub