I am unable to run batch file that is located in mapped network drive using System.Diagnostics.Process in VB.Net. I get:
'<batchfilename>.bat' is not recognized as an internal or external command, operable program or batch file.
Press any key to continue...
I get the same error when navigate to the folder (using explorer) that contains this batch file and double click it. I can successfully run the batch file by changing the directory to that folder through command prompt. I can't figure out why this is happening. I think the problem is Start() function of Process works like double click rather than just run the batch file. I have put my code below. Any help regarding this will be much appreciated.
Private WithEvents batchProcObj As New Process
Private Const SLEEP_AMOUNT As Integer = 100
Public Event Exited As EventHandler
Private eventHandled As Boolean
Public Sub startBatchProcess(ByVal batchProcFilePath As String, ByVal batchParams As String, ByVal domainName As String, ByVal loginName As String, ByVal passwd As String)
' Initialise process start info with batch process path and the parameters
Dim startInfoObj As New ProcessStartInfo()
Dim parentDir As New IO.DirectoryInfo(batchProcFilePath)
' Initialise event to false
eventHandled = False
' Get the batch file name without the extension
batchName = batchProcFilePath
Try
' Assign the batch process properties
startInfoObj.FileName = batchProcFilePath
startInfoObj.Arguments = batchParams
startInfoObj.UseShellExecute = False
startInfoObj.WindowStyle = ProcessWindowStyle.Normal
startInfoObj.WorkingDirectory = "C:\Windows\System32\"
startInfoObj.Domain = domainName
startInfoObj.UserName = loginName
startInfoObj.Password = appHandler.getSecureString(passwd)
startInfoObj.LoadUserProfile = True
batchProcObj.StartInfo = startInfoObj
batchProcObj.EnableRaisingEvents = True
' Start the batch file
batchProcObj.Start()
' Get the start time of the batch process
procStartTime = batchProcObj.StartTime
' Insert audit log
appHandler.writeAuditLog("RunBatchProcess", "START - Batch Process [" & batchName & "]")
Catch ex As Exception
appHandler.writeErrorLog("Start Batch Process: " & batchProcFilePath, ex)
End Try
' Continue running the batch process till it exits
Do While Not eventHandled
elapsedTime += SLEEP_AMOUNT
Thread.Sleep(SLEEP_AMOUNT)
Loop
End Sub
Just use FileIO.FileSystem and copy that into temp file.
Then use Backgroundworker to run.
Related
Everything worked great from my Visual Studio on my PC running this from the Start button. When I build the executable and copied the executable to the production box and scheduled the job via SQL Server Agent on the production machine – everything worked fine to create the file, but the encryption bit does not work.
The gpg.exe is here on the production server: \sql2014\c$\Program Files (x86)\GnuPG\bin
The gpg is here on my PC: C:\Program Files (x86)\GnuPG\bin
The filename.csv gets created in the proper location ok - I tested with both these names
Dim Extract_File As String = “\sql2014\e$\Extracts\ProgramName\filename.csv”
‘Dim Extract_File As String = “E:\Extracts\ProgramName\filename.csv” ‘do to this from my PC I had to change the E: to a C:
This line calls the function:
FileEncrypted = Encrypt_File(Extract_File, Batch_Timestamp)
Private Function Encrypt_File(File_To_Encrypt As String, Batch_Timestamp As Date)
On Error GoTo Encrypt_File_Error
Dim Success As Boolean = False
Dim sourceName As String = File_To_Encrypt
Dim gpgProcess = New Process()
‘Test with working directory - no effect
‘gpgProcess.StartInfo.UseShellExecute = False
'gpgProcess.StartInfo.WorkingDirectory = "\\sql2014\c$\Program Files (x86)\GnuPG\bin\"
‘gpgProcess.StartInfo.FileName = "gpg.exe"
gpgProcess.StartInfo.FileName = \\sql2014\c$\Program Files (x86)\GnuPG\bin\gpg.exe ‘This works from my PC
‘gpgProcess.StartInfo.FileName = \\sql2014\c$\Program Files (x86)\GnuPG\bn\gpg.exe ‘If I change this path took the “i” out of bin I get an error: The system cannot find the file specified
gpgProcess.StartInfo.UseShellExecute = False
gpgProcess.StartInfo.CreateNoWindow = True
gpgProcess.StartInfo.Arguments = "--batch --yes --recipient reciptname --encrypt " & sourceName
gpgProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
gpgProcess.Start()
gpgProcess.WaitForExit()
If FileExists(sourceName & ".gpg") Then
Success = True
End If
Encrypt_File_Exit:
On Error Resume Next
‘gpgProcess.WaitForExit() moved this up to
gpgProcess.Close()
Return Success
Exit Function
Encrypt_File_Error:
Error_Handler("SomeModule.vb", "Encrypt_File", Err, System_Output, Batch_Timestamp)
Resume Encrypt_File_Exit
End Function
Any suggestions for how I can resolve this. When it worked on my PC it creates a filename.csv.gpg in the same directory as filename.csv. On the production server it does not create the gpg and it does not give a visible error message either.
This is how I solved this issue. I Installed the OpenPgpLib from the NuGet Package Manager and re-wrote this Function as shown here.
I created the .asc file from the Kleopatra tool and saved it in the location used in the pubkey in the code bit below. The OpenPgp is from the package.
Private Function Encrypt_File(File_To_Encrypt As String, Log_File As String, Batch_Timestamp As Date)
Dim Success As Boolean = False
Dim encryptthis As String = File_To_Encrypt
Dim thisencrypted As String = File_To_Encrypt & ".gpg"
Dim pubkey As String = "\\sql2014\c$\Data_Programs\MyDirectory\<thepublickeyfile>.asc"
Try
OpenPgp.EncryptFile(encryptthis, thisencrypted, pubkey, False, False)
If FileExists(thisencrypted) Then
Success = True
End If
Catch ex As Exception
App_Logger(Log_File, ex.StackTrace.ToString(), System_Output, Batch_Timestamp)
Success = False
End Try
Return Success
End Function
I need to FTP a local file to Mainframe and have written the below script to create a local Batch text file using streamwriter and then use this file with the ftp -s: command to run it.
Here is the code.
Shared Sub TestFTP()
' BP DEFINED INPUTS ANDF OUTPUTS
'Inputs
Dim hostname As String
Dim username As String
Dim password As String
Dim mainfile As String
Dim localfile As String
'Outputs
Dim success As Boolean
Dim message As String
'-------------
'Test DATA
'-------------
hostname = "XXIBM2"
username = "USER1"
password = "XXX1234"
mainfile = "XXTSO.USER1.TEST2"
localfile = "D:\TestFTP.txt"
'=============================BP Code========================
Try
Dim localPath As String = "C:\BPFTP"
Dim isExists As Boolean = System.IO.Directory.Exists(localPath)
If (isExists = False) Then
System.IO.Directory.CreateDirectory(localPath)
End If
' Open StreamWriter And create batch file
Using writer As StreamWriter = New StreamWriter(localPath + "\\FTP.txt")
writer.WriteLine("open " + hostname)
writer.WriteLine(username)
writer.WriteLine(password)
writer.WriteLine("put " + localfile + " '" + mainfile + "'")
writer.WriteLine("bye")
writer.WriteLine("exit")
End Using
' Perform FTP
Interaction.Shell("ftp -n -s:C:\BPFTP\FTP.txt")
' Delete batch file
System.IO.File.Delete("C:\\BPFTP\\FTP.txt")
success = True
Catch e As Exception
success = False
message = e.Message
End Try
End Sub
If I run the code using F5 the file does not appear on the mainframe.
If I set a breakpoing at the Shell command and run the code (f5) to here and then F5 to the end the file does not get FTPd to the mainframe.
HOWEVER.
If I run the code to the breakpoint and then simply 'Step Over' the Shell command line using F10 then the file successfully FTPs to the mainframe.
When you run in debug mode, you are forcing a synchronous operation, you need to tell shell and your ftp application to wait in order to completely send the file.
Interaction.Shell("ftp -n -s:C:\BPFTP\FTP.txt", AppWinStyle.MinimizedFocus, True, 30000)
See here
This will force it to wait 30 seconds before continuing, if you set it to -1 it waits forever which can cause undesirable behavior.
I am trying to get a command line output line by line till the end of the output but I am not able to do so. I am using it in my Form and this code executes on click of a button.
Can you tell me whats wrong with my code?
Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
Dim pr As Process
proc.CreateNoWindow = True
proc.UseShellExecute = False
proc.RedirectStandardInput = True
proc.RedirectStandardOutput = True
pr = Process.Start(proc)
pr.StandardInput.WriteLine("cd C:\sdk\platform-tools\")
pr.StandardInput.WriteLine("adb help")
Dim helpArray(20) as String
For i as Integer 1 To 7
helpArray(i) = pr.StandardOutput.ReadLine()
Next
pr.StandardOutput.Close()
The program stops responding when this code is executed.
I've done some research. adb help writes output into STDERR. So you need something like:
Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
Dim pr As Process
proc.CreateNoWindow = True
proc.UseShellExecute = False
proc.RedirectStandardInput = True
proc.RedirectStandardOutput = True
pr = Process.Start(proc)
pr.StandardInput.WriteLine("C:\sdk\platform-tools")
pr.StandardInput.WriteLine("adb help 2>&1")
pr.StandardInput.Close()
Console.WriteLine(pr.StandardOutput.ReadToEnd())
pr.StandardOutput.Close()
to catch it.
You need no 2>&1 if you call ipconfig, for example.
Do not interate over the output and do not read it! Normally you don't know how long the output (same goes for error output too) would be, so you need to prepare for an unknown length. Since you are telling the Process class, that you want to handle the standard output and the standard error by yourself, you also need to bind to the events, in this case:
OutputDataReceived
ErrorDataReceived
or to block the current process and read the complete output at once like #Dmitry Kurilo does in his answer. I find the first approach better because I do not need to wait for the process to end to see it's output. The MSDN documentation of the ProcessStartInfo.RedirectstandardError property gives a good explanation of the different possibilities with a lot of examples.
If you want to take a specific line, there are a lot of possibilities. One would be to store each output (line) in the delegate and use it later, using a List(Of String) and output the specific line when the process is done (= all output lines are present).
A possible solution could look like this:
' store error output lines
dim lines = new List(of String)
dim executable = "c:\temp\android\sdk\platform-tools\adb.exe"
dim arguments = " help"
dim process = new Process()
process.StartInfo = createStartInfo(executable, arguments)
process.EnableRaisingEvents = true
addhandler process.Exited, Sub (ByVal sender As Object, ByVal e As System.EventArgs)
Console.WriteLine(process.ExitTime)
Console.WriteLine(". Processing done.")
' output line n when output is ready (= all lines are present)
Console.WriteLine(lines(4))
end sub
' catch standard output
addhandler process.OutputDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs)
if (not String.IsNullOrEmpty(e.Data))
Console.WriteLine(String.Format("{0}> {1}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") ,e.Data))
end if
end sub
' catch errors
addhandler process.ErrorDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs)
'Console.WriteLine(String.Format("! {0}", e.Data))
' add every output line to the list of strings
lines.Add(e.Data)
end sub
' start process
dim result = process.Start()
' and wait for output
process.BeginOutputReadLine()
' and wait for errors :-)
process.BeginErrorReadLine()
private function createStartInfo(byval executable as String, byval arguments as String) as ProcessStartInfo
dim processStartInfo = new ProcessStartInfo(executable, arguments)
processStartInfo.WorkingDirectory = Path.GetDirectoryName(executable)
' we want to read standard output
processStartInfo.RedirectStandardOutput = true
' we want to read the standard error
processStartInfo.RedirectStandardError = true
processStartInfo.UseShellExecute = false
processStartInfo.ErrorDialog = false
processStartInfo.CreateNoWindow = true
return processStartInfo
end function
Now even if the adb writes to the error output, you will be able to see it. It will also be complete.
The output in this case looks like this:
14.10.2014 12:49:10
. Processing done.
-e - directs command to the only running emulator.
Another possibility would be to put everything into one string and after the process has finished split the single string on line endings (CRLF \r\n) and you will gain the lines you want to filter.
So I have this in my coding:
vb Code:
file = My.Computer.FileSystem.OpenTextFileWriter("c:\command.bat", False)
file.WriteLine("#echo off")
file.WriteLine("cd " & TextBox2.Text)
file.WriteLine("adb shell dumpsys meminfo " & TextBox1.Text & " > C:\Sample.txt")
file.Close()
Shell("C:\command.bat")
what I want it to do is to run the batch file without it opening if that makes sense. Right now this runs on a loop for 10 minutes and on every 2 second tick it opens and then closes the .bat. Which is really annoying to see a .bat open and close every two seconds. Is there anyway to get this process to run silently in the background so the user doesnt even know that it is running?
Shell("C:\command.bat", AppWinStyle.Hide)
That will run the batch file but the window is hidden.
or use Process.Start as suggested by David. with WindowStyle = ProcessWindowStyle.Hidden
Here is an example on how to use Process.Start with a hidden window
Dim params As String = "C:\command.bat"
Dim myProcess As New ProcessStartInfo
myProcess.FileName = "cmd.exe"
myProcess.Arguments = params
myProcess.WindowStyle = ProcessWindowStyle.Hidden
Process.Start(myProcess)
if you run into the issue of file not found errors with the path you can try to add the following Windows API call and run your file path through this function as well.
'This would be declared at the top of your Form Code/Class Code
Private Declare Auto Function GetShortPathName Lib "kernel32" ( _
ByVal lpszLongPath As String, _
ByVal lpszShortPath As StringBuilder, _
ByVal cchBuffer As Integer) As Integer
And here is the function to return back a ShortPath (win98 style path (ie. c:/progra~1/myfolder/myfile.bat)
Public Function GetShortPath(ByVal longPath As String) As String
Dim requiredSize As Integer = GetShortPathName(longPath, Nothing, 0)
Dim buffer As New StringBuilder(requiredSize)
GetShortPathName(longPath, buffer, buffer.Capacity)
Return buffer.ToString()
End Function
then simply call your path like this in your process.start function
Dim params As String = GetShortPathName("C:\command.bat")
I currently have this piece of code:
Sub Button1Click(sender As Object, e As EventArgs)
If dlgFolder.ShowDialog = Windows.Forms.DialogResult.OK Then
txtPath.Text = dlgFolder.SelectedPath
Try
Dim CopyFile As String = Path.Combine(Directory.GetCurrentDirectory, "pdftk.exe")
Dim CopyLocation As String = Path.Combine(dlgFolder.SelectedPath, "pdftk.exe")
Dim pyScript As String = Path.Combine(Directory.GetCurrentDirectory, "pdfmerge.py")
Dim pyLocation As String = Path.Combine(dlgFolder.SelectedPath, "pdfmerge.py")
System.IO.File.Copy(CopyFile, CopyLocation, True)
System.IO.File.Copy(pyScript, pyLocation, True)
Catch copyError As IOException
Console.WriteLine(copyError.Message)
End Try
End If
End Sub
This copies two files in the current working directory (which will be the default install folder) to the selected path from the Fodler Dialog Browser. This works correctly.
Now what I want to do is too run "pdfmerge.py" into the selected folder path. I tried the below code but the script is still running in the current working directory.
Sub BtnNowClick(sender As Object, e As EventArgs)
Dim myProcess As Process
Dim processFile As String = Path.Combine(dlgFolder.SelectedPath, "pdfmerge.py")
myProcess.Start(processFile, dlgFolder.SelectedPath)
End Sub
You can set the process's working directory.
Dim p As New ProcessStartInfo
p.FileName = Path.Combine(dlgFolder.SelectedPath, "pdfmerge.py")
p.WorkingDirectory = dlgFolder.SelectedPath
Process.Start(p)
One question: are you ensuring the dlgFolder.SelectedPath is correct? Without knowing the inner workings of your program, it appears possible to press BtnNow before Button1, meaning dlgFolder.SelectedPath won't have been set by the user.
Try using the overload of Process.Start() that takes 5 arguments.
Start ( _
fileName As String, _
arguments As String, _
userName As String, _
password As SecureString, _
domain As String _
)
You may be able to pass in null for userName and password, but if your directory is outside of the standard ones that you have permission for, you may need to put your username and password in. domain would be the working directory, I think.