Parsing arp.exe - WaitForExit(60000) times out - vb.net

I'm trying to parse arp.exe's output to get the MAC address for a computer. I'm able to parse ping.exe and obtain the IP address but for some reason arp.exe appears to never exit. If I remove the arguments I it exits almost instantly. If I run the command myself in command prompt it outputs the result in less than one second. I'm not sure why it's not working.
Public Function DNSLookup(ByVal dnsName As String)
#If Not Debug Then
Try
#End If
Dim p As New Process
With p.StartInfo
.FileName = "ping.exe"
.Arguments = "-n 1 -4 " + dnsName 'Send 1 echo/packet (-n 1) and force IPv4 (-4)
.CreateNoWindow = True
.RedirectStandardOutput = True
.RedirectStandardError = True
.UseShellExecute = False
End With
p.Start()
If p.WaitForExit(5000) Then
'Find and parse "Reply from xxx.xxx.xxx.xxx: bytes=xx time=xms TTL=xxx"
Dim result As String = p.StandardOutput.ReadToEnd 'Read the result from the command line
Dim i As Integer = result.IndexOf("Reply from ") + 11 '11 is the length of "Reply from "
result = result.Substring(i, result.IndexOf(": bytes=") - i) 'Get the IP from the command line output
Return result
Else
Throw New System.Exception("DNS lookup failed. Timeout exceeded")
End If
#If Not Debug Then
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
#End If
End Function
Public Function GetMACAddress(ByVal dnsName As String)
Dim ip As String = DNSLookup(dnsName)
#If Not Debug Then
Try
#End If
Dim arp As New Process
With arp.StartInfo
.FileName = "arp.exe"
.Arguments = "-a" ' | find " + Chr(34) + ip + Chr(34) 'Example: arp -a | find "192.168.0.1"
.CreateNoWindow = True
.RedirectStandardOutput = True
.RedirectStandardError = True
.RedirectStandardInput = False
.UseShellExecute = False
End With
arp.Start()
If arp.WaitForExit(60000) Then
MsgBox(arp.StandardOutput.ReadToEnd)
Else
Throw New System.Exception("ARP lookup failed. Timeout exceeded")
End If
Return 1 '### TO DO: Return parsed MAC address
#If Not Debug Then
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
#End If
End Function

I'm not sure why this wasn't working with the arguments just set to "-a" last night as when I ran it this morning that worked. However I realised that the reason my full argument wasn't working is because "| find" isn't an argument recognised by arp.exe. To solve it I've used cmd.exe /c arp. Updated code below works:
Public Function GetMACAddress(ByVal dnsName As String)
Dim ip As String = DNSLookup(dnsName)
#If Not Debug Then
Try
#End If
Dim arp As New Process
With arp.StartInfo
.FileName = "cmd.exe"
.Arguments = "/c arp -a | find " + Chr(34) + ip + Chr(34) 'Example: arp -a | find "192.168.0.1"
.CreateNoWindow = False
.RedirectStandardOutput = True
.RedirectStandardError = True
.RedirectStandardInput = False
.UseShellExecute = False
End With
arp.Start()
If arp.WaitForExit(5000) Then
MsgBox(arp.StandardOutput.ReadToEnd)
Else
Throw New System.Exception("ARP lookup failed. Timeout exceeded")
End If
Return 1 '### TO DO: Return parsed IP address
#If Not Debug Then
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
#End If
End Function

Related

DotRAS.dll not working on Windows 10 Update 1803

I have a small VB.NET program, which creates a VPN connection.
This is working until (and including) Windows 10 Update 1709.
But since the Update 1803 I always get an exception:
DotRas.RasException: An incorrect structure size was detected.
at DotRas.Internal.ThrowHelper.ThrowRasException(Int32 errorCode)
at DotRas.Internal.RasHelper.GetEntryProperties(RasPhoneBook phoneBook, String entryName)
at DotRas.RasEntryCollection.Load()
at DotRas.RasPhoneBook.Open(String phoneBookPath)
Following my working Code Sub (until 1709)
Private Sub CreateVPN(VpnName As String, Destination As String, bAllUsers As Boolean)
Dim PresharedKey As String = "XXXXXXXXXXX"
Try
Dim PhoneBook As New RasPhoneBook
Dim sUsedPhoneBook As String = sPhoneBook
If bAllUsers Then sUsedPhoneBook = sAllUserPhoneBook
PhoneBook.Open(sUsedPhoneBook)
'First remove existing VPN with same name, if exist!
Try
PhoneBook.Entries.Remove(VpnName)
Catch ex As Exception
End Try
Dim VpnEntry As RasEntry = RasEntry.CreateVpnEntry(VpnName, Destination, DotRas.RasVpnStrategy.L2tpOnly, DotRas.RasDevice.Create(VpnName, DotRas.RasDeviceType.Vpn))
VpnEntry.Options.UsePreSharedKey = True
VpnEntry.Options.IPHeaderCompression = True
VpnEntry.Options.SoftwareCompression = True
VpnEntry.Options.NetworkLogOn = True
VpnEntry.NetworkProtocols.IPv6 = False
VpnEntry.Options.RequireEap = False
VpnEntry.Options.RequirePap = True
VpnEntry.Options.RequireChap = True
VpnEntry.Options.RequireMSChap2 = True
VpnEntry.Options.RequireEncryptedPassword = False
VpnEntry.Options.UseLogOnCredentials = False
VpnEntry.Options.CacheCredentials = False
VpnEntry.DnsSuffix = "YYYYYYY.local"
VpnEntry.Options.UseDnsSuffixForRegistration = True
' *********** THE FOLLOWING LINE WILL CRASH ON WINDOWS 10 1803 **********
PhoneBook.Entries.Add(VpnEntry)
VpnEntry.UpdateCredentials(RasPreSharedKey.Client, PresharedKey)
If txt_Username.Text <> "" AndAlso txt_Password.Text <> "" Then
VpnEntry.UpdateCredentials(New Net.NetworkCredential(txt_Username.Text, txt_Password.Text), False)
VpnEntry.Options.CacheCredentials = True
VpnEntry.Update()
End If
PhoneBook.Dispose()
txt_Status.Text = VpnName & " successful added."
txt_Status.ForeColor = Color.DarkGreen
Catch ex As Exception
Console.WriteLine("Error while adding " & VpnName & "." & Environment.NewLine & ex.ToString)
Finally
End Try
End Sub
Can somebody help me?
I didn't found an updated DotRAS.dll.
Best would be a working example without external DLL.
Thank you in advance.

VB won't show PDF Reader

I use the following Code to Open a PDF File:
Public Sub Execute_Doc(afilename As String, Optional style As ProcessWindowStyle = ProcessWindowStyle.Minimized)
Dim myProcess As New Process
Const ERROR_FILE_NOT_FOUND As Integer = 2
Const ERROR_ACCESS_DENIED As Integer = 5
Try
myProcess.StartInfo.FileName = afilename
myProcess.StartInfo.WindowStyle = style
myProcess.Start()
Catch e As System.ComponentModel.Win32Exception
If e.NativeErrorCode = ERROR_FILE_NOT_FOUND Then
Console.WriteLine(e.Message + ". Check the path.")
MsgBox("File<" + afilename + "> not found!")
Else
If e.NativeErrorCode = ERROR_ACCESS_DENIED Then
Console.WriteLine(e.Message + ". You do not have permission to print this file.")
MsgBox("File <" + afilename + "> couldn't be opened!")
End If
End If
MsgBox(e.ToString())
Catch ex As Exception
MsgBox(e.ToString())
Finally
myProcess.Kill()
myProcess.Dispose()
End Try
End Sub
I call Execute_Doc("C:\ProgrammName\Test.pdf", ProcessWindowStyle.Normal) but the Adobe Reader won't show up. I can see it in the Task Manager.
Well it works if I start Adobe Reader first without any files by clicking the default Icon on my Desktop. It also works with the integrated PDF Reader from Windows 8.1. I can't debug this isse on my Windows 7 / VS 2013 Computer. The problem only exists on ONE! client computer.
Any tipps how to solve this?
Public Sub Execute_Doc(afilename As String, Optional style As ProcessWindowStyle = ProcessWindowStyle.Minimized)
Dim myProcess As New Process
Const ERROR_FILE_NOT_FOUND As Integer = 2
Const ERROR_ACCESS_DENIED As Integer = 5
Try
myProcess.StartInfo.FileName = "AcroRd32.exe " & afilename
myProcess.StartInfo.WindowStyle = style
myProcess.Start()
Catch e As System.ComponentModel.Win32Exception
If e.NativeErrorCode = ERROR_FILE_NOT_FOUND Then
Console.WriteLine(e.Message + ". Check the path.")
MsgBox("File<" + afilename + "> not found!")
Else
If e.NativeErrorCode = ERROR_ACCESS_DENIED Then
Console.WriteLine(e.Message + ". You do not have permission to print this file.")
MsgBox("File <" + afilename + "> couldn't be opened!")
End If
End If
MsgBox(e.ToString())
Catch ex As Exception
MsgBox(e.ToString())
Finally
myProcess.Kill()
myProcess.Dispose()
End Try
End Sub
Just use Shell Execute it is provided by the Win API so you do not have to worry about what program is installed to handle the extension. Windows does the work for you.
Private Function ShellExecute(ByVal File As String) As Boolean
Dim myProcess As New Process
myProcess.StartInfo.FileName = File
myProcess.StartInfo.UseShellExecute = True
myProcess.StartInfo.RedirectStandardOutput = False
myProcess.Start()
myProcess.Dispose()
End Function

Download Direct links

My program has been using:
Dim DLLink1 As String
DLLink1 = Trim(TextBox2.Text)
Dim DownloadDirectory1 As String
DownloadDirectory1 = Trim(TextBox4.Text)
Try
Button3.Enabled = False
' My.Computer.Network.DownloadFile(DLLink1, (DownloadDirectory1 + "/UpdatedClient.zip"))
Dim HttpReq As HttpWebRequest = DirectCast(WebRequest.Create(DLLink1), HttpWebRequest)
Using HttpResponse As HttpWebResponse = DirectCast(HttpReq.GetResponse(), HttpWebResponse)
Using Reader As New BinaryReader(HttpResponse.GetResponseStream())
Dim RdByte As Byte() = Reader.ReadBytes(1 * 1024 * 1024 * 10)
Using FStream As New FileStream(DownloadDirectory1 + "/UpdatedClient.zip", FileMode.Create)
FStream.Write(RdByte, 0, RdByte.Length)
End Using
End Using
End Using
Finally
MsgBox("Finished Download.")
Button3.Enabled = True
Label4.Visible = True
I tried this previously, and it didn't work at all:
My.Computer.Network.DownloadFile(DLLink1, (DownloadDirectory1 + "/UpdatedClient.zip"))
The website requires you to be logged in, so I made a spare account for the program:
WebBrowser1.Navigate("http://www.mpgh.net/forum/admincp/")
Timer1.Start()
Button2.Enabled = False
Then
WebBrowser1.Document.GetElementById("vb_login_username").SetAttribute("value", "AutoUpdaterAccount")
WebBrowser1.Document.GetElementById("vb_login_password").SetAttribute("value", "password")
Dim allelements As HtmlElementCollection = WebBrowser1.Document.All
For Each webpageelement As HtmlElement In allelements
If webpageelement.GetAttribute("type") = "submit" Then
webpageelement.InvokeMember("click")
Timer1.Stop()
Label5.Text = "Authorized."
Button2.Enabled = True
So now you're logged into the account, on the website, but when the code above to download runs, it downloads a zip, but it's corrupted. So I opened it with notepad++ and this is what I get (Does this mean it didn't login for the download, and it only logged in with the webbrowser and they aren't linked? Or something? Like My firefox logins aren't linked with chrome?:
The code is huge, it's like a HTML coding. Here is the link to a online notepad I put it on:
http://shrib.com/nCOucdfL
Another thing, a webbrowser can't be showing on the program, it can be on the outside not showing, like I did with the login. They also can't click the save button like on a normal web browser when a window pops up, I want it to download automatically to where they set it using a button which sets the directory as DownloadDirectory1
It must be your lucky day because today I woke up and decided that I would like to help you with your cause. I first tried to get the download to work with the web browser control but unfortunately I am sure this is not possible without extending the web browser control and we don't want to do that today.
As I mentioned in the comments, the only way I really know that this is possible (without user interaction) is to log in via the HttpWebRequest method. It's pretty tricky stuff. Definitely not for beginners.
Now I must admit that this isn't the cleanest, most "proper" and user-friendly code around, so if anyone wants to suggest a better way to do things, I am all ears.
I suggest you test this first before you incorporate it into your existing app. Just create a new vb.net app and replace all of the code in Form1 with the code below. You will have to update the usernamehere and passwordhere strings with your real username and password. Also, the file is saving to C:\file.rar by default so you can change this path if you want. This code completely removes the need for the web browser control (unless you are using it for something else) so most likely you can remove that from your real application once you incorporate this properly:
Imports System.Net
Imports System.IO
Imports System.Text
Public Class Form1
Private Const gsUserAgent As String = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0"
Const sUsername As String = "usernamehere"
Const sPassword As String = "passwordhere"
Const sMainURL As String = "http://www.mpgh.net/"
Const sCheckLoginURL As String = "http://www.mpgh.net/forum/login.php?do=login"
Const sDownloadURL As String = "http://www.mpgh.net/forum/attachment.php?attachmentid=266579&d=1417312178"
Const sCookieLoggedInMessage As String = "mpgh_imloggedin=yes"
Dim oCookieCollection As CookieCollection = Nothing
Dim sSaveFile As String = "c:\file.rar"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
StartScrape()
End Sub
Private Sub StartScrape()
Try
Dim bContinue As Boolean = True
Dim sPostData(15) As String
sPostData(0) = UrlEncode("vb_login_username")
sPostData(1) = UrlEncode(sUsername)
sPostData(2) = UrlEncode("vb_login_password")
sPostData(3) = UrlEncode(sPassword)
sPostData(4) = UrlEncode("vb_login_password_hint")
sPostData(5) = UrlEncode("Password")
sPostData(6) = UrlEncode("s")
sPostData(7) = UrlEncode("")
sPostData(8) = UrlEncode("securitytoken")
sPostData(9) = UrlEncode("guest")
sPostData(10) = UrlEncode("do")
sPostData(11) = UrlEncode("login")
sPostData(12) = UrlEncode("vb_login_md5password")
sPostData(13) = UrlEncode("")
sPostData(14) = UrlEncode("vb_login_md5password_utf")
sPostData(15) = UrlEncode("")
If GetMethod(sMainURL) = True Then
If SetMethod(sCheckLoginURL, sPostData, sMainURL) = True Then
' Login successful
If DownloadMethod(sDownloadURL, sMainURL) = True Then
MessageBox.Show("File downloaded successfully")
Else
MessageBox.Show("Error downloading file")
End If
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Function GetMethod(ByVal sPage As String) As Boolean
Dim req As HttpWebRequest
Dim resp As HttpWebResponse
Dim stw As StreamReader
Dim bReturn As Boolean = True
Try
req = HttpWebRequest.Create(sPage)
req.Method = "GET"
req.AllowAutoRedirect = False
req.UserAgent = gsUserAgent
req.Accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
req.Headers.Add("Accept-Language", "en-us,en;q=0.5")
req.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7")
req.Headers.Add("Keep-Alive", "300")
req.KeepAlive = True
resp = req.GetResponse ' Get the response from the server
If req.HaveResponse Then
' Save the cookie info if applicable
SaveCookies(resp.Headers("Set-Cookie"))
resp = req.GetResponse ' Get the response from the server
stw = New StreamReader(resp.GetResponseStream)
stw.ReadToEnd() ' Read the response from the server, but we do not save it
Else
MessageBox.Show("No response received from host " & sPage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
bReturn = False
End If
Catch exc As WebException
MessageBox.Show("Network Error: " & exc.Message.ToString & " Status Code: " & exc.Status.ToString & " from " & sPage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
bReturn = False
End Try
Return bReturn
End Function
Private Function SetMethod(ByVal sPage As String, ByVal sPostData() As String, sReferer As String) As Boolean
Dim bReturn As Boolean = False
Dim req As HttpWebRequest
Dim resp As HttpWebResponse
Dim str As StreamWriter
Dim sPostDataValue As String = ""
Try
req = HttpWebRequest.Create(sPage)
req.Method = "POST"
req.UserAgent = gsUserAgent
req.Accept = "application/x-shockwave-flash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
req.Headers.Add("Accept-Language", "en-us,en;q=0.5")
req.Headers.Add("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7")
req.Referer = sReferer
req.ContentType = "application/x-www-form-urlencoded"
req.Headers.Add("Pragma", "no-cache")
req.Headers.Add("Keep-Alive", "300")
If oCookieCollection IsNot Nothing Then
' Pass cookie info from the login page
req.CookieContainer = SetCookieContainer(sPage)
End If
str = New StreamWriter(req.GetRequestStream)
If sPostData.Count Mod 2 = 0 Then
' There is an even number of post names and values
For i As Int32 = 0 To sPostData.Count - 1 Step 2
' Put the post data together into one string
sPostDataValue &= sPostData(i) & "=" & sPostData(i + 1) & "&"
Next i
sPostDataValue = sPostDataValue.Substring(0, sPostDataValue.Length - 1) ' This will remove the extra "&" at the end that was added from the for loop above
' Post the data to the server
str.Write(sPostDataValue)
str.Close()
' Get the response
resp = req.GetResponse
If req.HaveResponse Then
If resp.Headers("Set-Cookie").IndexOf(sCookieLoggedInMessage) > -1 Then
' Save the cookie info
SaveCookies(resp.Headers("Set-Cookie"))
bReturn = True
Else
MessageBox.Show("The email or password you entered are incorrect." & vbCrLf & vbCrLf & "Please try again.", "Unable to log in", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
bReturn = False
End If
Else
' This should probably never happen.. but if it does, we give a message
MessageBox.Show("The email or password you entered are incorrect." & vbCrLf & vbCrLf & "Please try again.", "Unable to log in", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
bReturn = False
End If
Else
' Did not specify the correct amount of parameters so we cannot continue
MessageBox.Show("POST error. Did not supply the correct amount of post data for " & sPage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
bReturn = False
End If
Catch ex As Exception
MessageBox.Show("POST error. " & ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
bReturn = False
End Try
Return bReturn
End Function
Private Function DownloadMethod(ByVal sPage As String, sReferer As String) As Boolean
Dim req As HttpWebRequest
Dim bReturn As Boolean = False
Try
req = HttpWebRequest.Create(sPage)
req.Method = "GET"
req.AllowAutoRedirect = False
req.UserAgent = gsUserAgent
req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
req.Headers.Add("Accept-Language", "en-US,en;q=0.5")
req.Headers.Add("Accept-Encoding", "gzip, deflate")
req.Headers.Add("Keep-Alive", "300")
req.KeepAlive = True
If oCookieCollection IsNot Nothing Then
' Set cookie info so that we continue to be logged in
req.CookieContainer = SetCookieContainer(sPage)
End If
' Save file to disk
Using oResponse As System.Net.WebResponse = CType(req.GetResponse, System.Net.WebResponse)
Using responseStream As IO.Stream = oResponse.GetResponseStream
Using fs As New IO.FileStream(sSaveFile, FileMode.Create, FileAccess.Write)
Dim buffer(2047) As Byte
Dim read As Integer
Do
read = responseStream.Read(buffer, 0, buffer.Length)
fs.Write(buffer, 0, read)
Loop Until read = 0
responseStream.Close()
fs.Flush()
fs.Close()
End Using
responseStream.Close()
End Using
oResponse.Close()
End Using
bReturn = True
Catch exc As WebException
MessageBox.Show("Network Error: " & exc.Message.ToString & " Status Code: " & exc.Status.ToString & " from " & sPage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
bReturn = False
End Try
Return bReturn
End Function
Private Function SetCookieContainer(sPage As String) As System.Net.CookieContainer
Dim oCookieContainerObject As New System.Net.CookieContainer
Dim oCookie As System.Net.Cookie
For c As Int32 = 0 To oCookieCollection.Count - 1
If IsDate(oCookieCollection(c).Value) = True Then
' Fix dates as they seem to cause errors/problems
oCookieCollection(c).Value = Format(CDate(oCookieCollection(c).Value), "dd-MMM-yyyy hh:mm:ss")
End If
oCookie = New System.Net.Cookie
oCookie.Name = oCookieCollection(c).Name
oCookie.Value = oCookieCollection(c).Value
oCookie.Domain = New Uri(sPage).Host
oCookie.Secure = False
oCookieContainerObject.Add(oCookie)
Next
Return oCookieContainerObject
End Function
Private Sub SaveCookies(sCookieString As String)
Dim sCookieStrings() As String = sCookieString.Trim.Replace(" HttpOnly,", "").Replace(" HttpOnly", "").Replace(" domain=.mpgh.net,", "").Split(";".ToCharArray())
oCookieCollection = New CookieCollection
For Each sCookie As String In sCookieStrings
If sCookie.Trim <> "" Then
Dim sName As String = sCookie.Trim().Split("=".ToCharArray())(0)
Dim sValue As String = sCookie.Trim().Split("=".ToCharArray())(1)
oCookieCollection.Add(New Cookie(sName, sValue))
End If
Next
End Sub
Private Function UrlEncode(ByRef URLText As String) As String
Dim AscCode As Integer
Dim EncText As String = ""
Dim bStr() As Byte = Encoding.ASCII.GetBytes(URLText)
Try
For i As Long = 0 To UBound(bStr)
AscCode = bStr(i)
Select Case AscCode
Case 48 To 57, 65 To 90, 97 To 122, 46, 95
EncText = EncText & Chr(AscCode)
Case 32
EncText = EncText & "+"
Case Else
If AscCode < 16 Then
EncText = EncText & "%0" & Hex(AscCode)
Else
EncText = EncText & "%" & Hex(AscCode)
End If
End Select
Next i
Erase bStr
Catch ex As WebException
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Return EncText
End Function
End Class

How to start a visible process

I have the following code to start the program R (even though I think that the program is not relevent for the problem here) and run a script:
Public Shared Sub RunRScript(rCodeFilePath As String, rScriptExecutablePath As String, args As String)
Dim file As String = rCodeFilePath
Dim result As String = String.Empty
Try
Dim info = New ProcessStartInfo()
info.FileName = rScriptExecutablePath
info.WorkingDirectory = Path.GetDirectoryName(rScriptExecutablePath)
info.Arguments = rCodeFilePath & " " & args
info.RedirectStandardInput = False
info.RedirectStandardOutput = True
info.UseShellExecute = False
info.CreateNoWindow = True
Using proc = New Process()
proc.StartInfo = info
proc.Start()
result = proc.StandardOutput.ReadToEnd()
proc.Close()
End Using
Catch ex As Exception
Throw New Exception("R Script failed: " & result, ex)
End Try
End Sub
Problem is, if there is an error in the script I run within R I dont get an error message because the instance is invisible. I tried to make it visible with
.WindowStyle = ProcessWindowStyle.Normal
in all combinations of .UseShellExcecute and .CreateNoWindow but this is not working. Could anyone help me to can make my process visible?
Since you are redirecting StandardInput and StandardOutput, you should now redirect StandardError to trap the errors also.
More info available on MSDN

Realtime console redirection issue

First of all, I did spend quite some time trying to find an answer to my issue but so far, nothing.
Here is the issue.
I have to run a cli command with arguments and redirect the output (REALTIME) to a textbox.
The command will look like this:
smcli - n name -f "script.scr"
Since smcli is not in the system PATH variable, I first need to go to the right Dir
So, I end up running the full command that looks like this:
cmd /c cd "c:\Program Files(x86)\bla\" && smcli - n name -f "script.scr"
I'll put the code in one sec but basically, the thing is working just fine, I get to the correct directory, run the command, it runs the script, all good.
I tried to redirect the output to a different form but then it doen't work anymore at all; anyway, that would be the bonus question.
The thing is though, when the script runs, it does say step by step what it is doing and sends that info to the output (i.e. restoring the name, restoring the disk config, ...)
As I said, I do get all those outputs....once EVERYTHING is completed (i.e. after 5 minutes of apparent freeze).
NOT redirecting the output to the textbox (i.e. leaving the console window open), I get the output real time.
So here is the code now (there is a bit of it, sorry):
Private Sub ReadMDcfgToolStripButton_Click(sender As Object, e As EventArgs) Handles ReadMDcfgToolStripButton.Click
Dim exitcode As Int32
smcli = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'SMcli.exe"
Dim gotosmclidir As String = "cd """ & smcli & """"
Dim smclicommand As String
smclicommand = "smcli -n " & mdname & " -c ""save storagearray configuration file=\""" & saveFileDialog2.FileName & "\"" allconfig"";"
cmd = gotosmclidir & " && " & smclicommand
exitcode = RunCommandCom(cmd, "", False)
End Sub
Private Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Int32
' Usage:
'RunCommandCom("DIR", "/W", true)
'For the multiple command on one line the key are the & | && and || command connectors
'•A & B -> execute command A, then execute command B.
'•A | B -> execute command A, and redirect all it's output into the input of command B.
'•A && B -> execute command A, evaluate the errorlevel after running Command A, and if the exit code (errorlevel) is 0, only then execute command B.
'•A || B -> execute Command A, evalutate the exit code of this command and if it's anything but 0, only then execute command B.
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
Dim pi As ProcessStartInfo = New ProcessStartInfo()
pi.FileName = "cmd.exe"
pi.Arguments = " " + If(permanent = True, "/K", "/C") + " " + command + " " + arguments
MsgBox(pi.Arguments)
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
pi.CreateNoWindow = True
p.StartInfo = pi
AddHandler p.OutputDataReceived, AddressOf GotData
p.Start()
p.BeginOutputReadLine()
Do Until p.HasExited
Loop
exitcodelbl.Text = p.ExitCode
Select Case p.ExitCode
Case 0
exitcodelbl.Text += " - Command completed successfully."
Case 1
exitcodelbl.Text += " - Could not communicate with the Array."
Case 9
exitcodelbl.Text += " - Could not communicate with the Array."
Case 14
exitcodelbl.Text += " - Could not communicate with the Array."
Case Else
exitcodelbl.Text += " - Unknown code."
End Select
Return p.ExitCode
End Function
Sub GotData(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
UpdateTextBox(e.Data)
End Sub
Private Delegate Sub UpdateTextBoxDelegate(ByVal Text As String)
Private Sub UpdateTextBox(ByVal Tex As String)
If Me.InvokeRequired Then
Dim del As New UpdateTextBoxDelegate(AddressOf UpdateTextBox)
Dim args As Object() = {Tex}
Me.Invoke(del, args)
Else
TextBox1.Text &= Tex & Environment.NewLine
End If
End Sub
Apart from the command itself, everything else is comming from researches and they are just put together, the way to run the command on one hand and the redirection on the other hand.
I believe the issue is with my command (looks like that part is VERY sensible).
Either I can have it done a different way (again, it works but not real time) or I missed something trivial...?
Thanks for your help.
Following tinstaafl here is the updated code:
Private Sub MainForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
copyrightlbl.Focus()
wait(1000)
Await RunCommandCom("", "192.168.219.152", False) '172.16.1.55
CmdCloseBtn.Enabled = True
End Sub
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
With p.StartInfo
.WorkingDirectory = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'Directory for SMcli.exe"
.FileName = "ping.exe"
.Arguments = command + " " + arguments
.CreateNoWindow = True
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardError = True
End With
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
exitcodelbl.Text = p.ExitCode
Return p.ExitCode
End Function
Instead of using a delegate try reading the output directly to the textbox. The StandardOutput property of the Process is a stream that you can use the ReadLineAsync method to get the output. Something like this should work:
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
p.StartInfo = pi
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
Intellisense will prompt you to make the sub routine Async, but with that you should get the output you want.
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
Await RunCommandCom("", "192.168.219.152", False)