how to pass html as a string using wkhtmltopdf? - c#

how to pass html as a string instead of url in wkhtmltopdf using asp.net, c#?

STDIn and STDOut have been redirected in this example, so you shouldn't need files at all.
public static class Printer
{
public const string HtmlToPdfExePath = "wkhtmltopdf.exe";
public static bool GeneratePdf(string commandLocation, StreamReader html, Stream pdf, Size pageSize)
{
Process p;
StreamWriter stdin;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = Path.Combine(commandLocation, HtmlToPdfExePath);
psi.WorkingDirectory = Path.GetDirectoryName(psi.FileName);
// run the conversion utility
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
// note: that we tell wkhtmltopdf to be quiet and not run scripts
psi.Arguments = "-q -n --disable-smart-shrinking " + (pageSize.IsEmpty? "" : "--page-width " + pageSize.Width + "mm --page-height " + pageSize.Height + "mm") + " - -";
p = Process.Start(psi);
try {
stdin = p.StandardInput;
stdin.AutoFlush = true;
stdin.Write(html.ReadToEnd());
stdin.Dispose();
CopyStream(p.StandardOutput.BaseStream, pdf);
p.StandardOutput.Close();
pdf.Position = 0;
p.WaitForExit(10000);
return true;
} catch {
return false;
} finally {
p.Dispose();
}
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
int read;
while ((read = input.Read(buffer, 0, buffer.Length)) > 0) {
output.Write(buffer, 0, read);
}
}
}

Redirecting STDIN is probably the easiest way to accomplish what you're trying to do.
One method of redirecting STDIN with wkhtmltopdf (in ASP.Net) is as follows:
private void WritePDF(string HTML)
{
string inFileName,
outFileName,
tempPath;
Process p;
System.IO.StreamWriter stdin;
ProcessStartInfo psi = new ProcessStartInfo();
tempPath = Request.PhysicalApplicationPath + "temp\\";
inFileName = Session.SessionID + ".htm";
outFileName = Session.SessionID + ".pdf";
// run the conversion utility
psi.UseShellExecute = false;
psi.FileName = "c:\\Program Files (x86)\\wkhtmltopdf\\wkhtmltopdf.exe";
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
// note that we tell wkhtmltopdf to be quiet and not run scripts
// NOTE: I couldn't figure out a way to get both stdin and stdout redirected so we have to write to a file and then clean up afterwards
psi.Arguments = "-q -n - " + tempPath + outFileName;
p = Process.Start(psi);
try
{
stdin = p.StandardInput;
stdin.AutoFlush = true;
stdin.Write(HTML);
stdin.Close();
if (p.WaitForExit(15000))
{
// NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works
Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName));
//Response.WriteFile(tempPath + outFileName);
}
}
finally
{
p.Close();
p.Dispose();
}
// delete the pdf
System.IO.File.Delete(tempPath + outFileName);
}
Note that the code above assumes that there's a temp directory available in your application directory. Also, you must explicitly enable write access to that directory for the user account used when running the IIS process.

I know this is an older post, but I want future developers to have this option. I had the same need, and the idea of having to start a background process just to get a PDF inside of a web app is terrible.
Here's another option: https://github.com/TimothyKhouri/WkHtmlToXDotNet
It's a .NET native wrapper around wkhtmltopdf.
Sample code here:
var pdfData = HtmlToXConverter.ConvertToPdf("<h1>COOOL!</h1>");
Note, it's not thread-safe as of right now - I'm working on that. So just use a monitor or something or a lock.

Related

Starting an ImageMagick Exe Process and resizing the image

I am using ImageMagick convert.exe(to re-size a image) in command line. It works great. But if I do the same in C# then It doesn't work. It does not show any error and all the lines run just fine. The StanderdErrorOutput is also empty. Any idea? Here is my code.
var myProcess = new Process();
myProcess.StartInfo.FileName = #"C:\Users\user\Desktop\ImageMagick-6.8.6-Q16\convert.exe";
myProcess.StartInfo.Arguments = #"icon.png -resize 64x64 icon1.png";
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
myProcess.WaitForExit();
Console.Read();
This is what I use to run processes, it's basically the same as what you have there except for the line that calls StandardError.ReadToEnd()
// create process start info
ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments);
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
// run the process
using (Process proc = System.Diagnostics.Process.Start(startInfo))
{
// This needs to be before WaitForExit() to prevent deadlocks, for details:
// http://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput%28v=VS.80%29.aspx
proc.StandardError.ReadToEnd();
// Wait for exit
proc.WaitForExit();
}
I found the solution by creating a temporary batch file,
static void Main(string[] args)
{
var guid = Guid.NewGuid().ToString();
var root = AppDomain.CurrentDomain.BaseDirectory;
var batchFilePath = root + guid + ".bat";
var cmd = #"cd C:\Users\user\Desktop\ImageMagick-6.8.6-Q16" + Environment.NewLine
+ "convert icon.png -resize 64x64 icon1.png";
CreateBatchFile(cmd, batchFilePath);// Temporary Batch file
RunBatchFile(batchFilePath);
DeleteBatchFile(batchFilePath);
}
private static void RunBatchFile(string batFilePath)
{
var myProcess = new Process();
myProcess.StartInfo.FileName = batFilePath;
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
myProcess.StartInfo.CreateNoWindow = true;
myProcess.Start();
myProcess.WaitForExit();
}
private static void DeleteBatchFile(string file)
{
File.Delete(file);
}
private static void CreateBatchFile(string input, string filePath)
{
FileStream fs = new FileStream(filePath, FileMode.Create);
StreamWriter writer = new StreamWriter(fs);
writer.WriteLine(input);
writer.Close();
fs.Close();
}
I was having a similar problem on Mac when trying to run Convert, even with the ReadToEnd() suggested by the other poster.
I found that by adding
-debug 'All'
option caused it to work.
I have not ideas why!

Xcopy in C# - how to call it?

Here I should copy file from nearby local ip address to my local system i have used the following code for copying using the Xcopy command and then launching the process but copying through Argumentsetting in code mentioned if I execute in command prompt it is copying but through code is not copying please tell what is the issue. Any ideas? what through code not copying.
string Porocess = String.Format("\"{0}\\xcopy.exe\"", Environment.SystemDirectory.ToString());
string SolutionSettings = string.Format("\"\\\\{0}\\C$\\Documents and Settings\\All Users\\Application Data\\Symantec\\Common Client\\settings.bak\"", IPaddress);
string TargetSettings = string.Format("\"C:\\Documents and Settings\\All Users\\Application Data\\Symantec\\settings.bak\"");
string Argumentsetting = /*"\"" +*/ SolutionSettings + " " + TargetSettings + " /Y";// parameters to launch process
int iret1 = LauncProcess(Porocess, Argumentsetting, Environment.SystemDirectory.ToString());
public static int LauncProcess(string sProcess, string sParams, string sWorkingDir)
{
int iRet = 0;
Process process = null;
try
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = sProcess;
startInfo.Arguments = sParams;
startInfo.WorkingDirectory = sWorkingDir;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
process = Process.Start(startInfo);
process.WaitForExit();
Console.WriteLine("Copy has finished.");
if (process.HasExited)
{
Console.WriteLine("Process has exited.");
if (process.ExitCode != 0)
{
iRet = 1;
}
else
{
iRet = 0;
}
}
else
{
iRet = 1;
}
}
catch (Exception ex)
{
iRet = 1;
}
finally
{
process.Dispose();
}
return iRet;
}
check whether source address and destination address are correctly given
Process process = new Process();
process.StartInfo.Arguments= #"D:\sourcePath F:\DestinationPath /e /y /I";
process.Start();

How to make sure a batch file is executed properly through a CMD from a c# applicaiton?

I have a c# applications which writes to a batch file and executes it. The application to be started and the path of application will be written in batch file and executed. which is working fine.
How can i make sure that the application launched successfully via my batch file run in command prompt ?
Is there any value that cmd returns after executing a batch file ? or any other ideas please...
Code i am using now :
public void Execute()
{
string LatestFileName = GetLastWrittenBatchFile();
if (System.IO.File.Exists(BatchPath + LatestFileName))
{
System.Diagnostics.ProcessStartInfo procinfo = new System.Diagnostics.ProcessStartInfo("cmd.exe");
procinfo.UseShellExecute = false;
procinfo.RedirectStandardError = true;
procinfo.RedirectStandardInput = true;
procinfo.RedirectStandardOutput = true;
System.Diagnostics.Process process = System.Diagnostics.Process.Start(procinfo);
System.IO.StreamReader stream = System.IO.File.OpenText(BatchPath + LatestFileName);
System.IO.StreamReader sroutput = process.StandardOutput;
System.IO.StreamWriter srinput = process.StandardInput;
while (stream.Peek() != -1)
{
srinput.WriteLine(stream.ReadLine());
}
stream.Close();
process.Close();
srinput.Close();
sroutput.Close();
}
else
{
ExceptionHandler.writeToLogFile("File not found");
}
}
I'm not familiar with batch files, but if there is possibility to return exit code from it, you can check it with System.Diagnostics.Process.ExitCode
Process process = Process.Start(new ProcessStartInfo{
FileName = "cmd.exe",
Arguments = "/C myfile.bat",
UseShellExecute = false,
});
process.WaitForExit();
Console.WriteLine("returned {0}", process.ExitCode);
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo(filename);
psi.RedirectStandardOutput = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
System.Diagnostics.Process listFiles;
listFiles = System.Diagnostics.Process.Start(psi);
System.IO.StreamReader myOutput = listFiles.StandardOutput;
listFiles.WaitForExit(2000);
if (listFiles.HasExited)
{
string output = myOutput.ReadToEnd();
MessageBox.Show(output);
}

Execute multiple command lines with the same process using .NET

I'm trying to execute multiple commands without create a new process each time. Basically, I want to start the DOS command shell, switch to the MySQL command shell, and execute a command. Here's how I am calling the procedure (also below). Also, how do I handle the "\"'s in the command?
ExecuteCommand("mysql --user=root --password=sa casemanager", 100, false);
ExecuteCommand(#"\. " + Environment.CurrentDirectory + #"\MySQL\CaseManager.sql", 100, true);
private void ExecuteCommand(string Command, int Timeout, Boolean closeProcess)
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/C " + Command);
ProcessInfo.CreateNoWindow = false;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
Process.WaitForExit(Timeout);
if (closeProcess == true) { Process.Close(); }
}
You can redirect standard input and use a StreamWriter to write to it:
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("mysql -u root -p");
sw.WriteLine("mypassword");
sw.WriteLine("use mydb;");
}
}
const string strCmdText = "/C command1&command2";
Process.Start("CMD.exe", strCmdText);
Couldn't you just write all the commands into a .cmd file in the temp folder and then execute that file?
As another answer alludes to under newer versions of Windows it seems to be necessary to read the standard output and/or standard error streams otherwise it will stall between commands. A neater way to do that instead of using delays is to use an async callback to consume output from the stream:
static void RunCommands(List<string> cmds, string workingDirectory = "")
{
var process = new Process();
var psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
psi.WorkingDirectory = workingDirectory;
process.StartInfo = psi;
process.Start();
process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
process.BeginOutputReadLine();
process.BeginErrorReadLine();
using (StreamWriter sw = process.StandardInput)
{
foreach (var cmd in cmds)
{
sw.WriteLine (cmd);
}
}
process.WaitForExit();
}
I prefer to do it by using a BAT file.
With BAT file you have more control and can do whatever you want.
string batFileName = path + #"\" + Guid.NewGuid() + ".bat";
using (StreamWriter batFile = new StreamWriter(batFileName))
{
batFile.WriteLine($"YOUR COMMAND");
batFile.WriteLine($"YOUR COMMAND");
batFile.WriteLine($"YOUR COMMAND");
}
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd.exe", "/c " + batFileName);
processStartInfo.UseShellExecute = true;
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Normal;
Process p = new Process();
p.StartInfo = processStartInfo;
p.Start();
p.WaitForExit();
File.Delete(batFileName);
ProcessStartInfo pStartInfo = new ProcessStartInfo();
pStartInfo.FileName = "CMD";
pStartInfo.Arguments = #"/C mysql --user=root --password=sa casemanager && \. " + Environment.CurrentDirectory + #"\MySQL\CaseManager.sql"
pStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(pStartInfo);
The && is the way to tell the command shell that there is another command to execute.
A command-line process such cmd.exe or mysql.exe will usually read (and execute) whatever you (the user) type in (at the keyboard).
To mimic that, I think you want to use the RedirectStandardInput property: http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardinput.aspx
You could also tell MySQL to execute the commands in the given file, like so:
mysql --user=root --password=sa casemanager < CaseManager.sql
You need to READ ALL data from input, before send another command!
And you can't ask to READ if no data is avaliable... little bit suck isn't?
My solutions... when ask to read... ask to read a big buffer... like 1 MEGA...
And you will need wait a min 100 milliseconds... sample code...
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim oProcess As New Process()
Dim oStartInfo As New ProcessStartInfo("cmd.exe", "")
oStartInfo.UseShellExecute = False
oStartInfo.RedirectStandardOutput = True
oStartInfo.RedirectStandardInput = True
oStartInfo.CreateNoWindow = True
oProcess.StartInfo = oStartInfo
oProcess.Start()
Dim Response As String = String.Empty
Dim BuffSize As Integer = 1024 * 1024
Dim x As Char() = New Char(BuffSize - 1) {}
Dim bytesRead As Integer = 0
oProcess.StandardInput.WriteLine("dir")
Threading.Thread.Sleep(100)
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
MsgBox(Response)
Response = String.Empty
oProcess.StandardInput.WriteLine("dir c:\")
Threading.Thread.Sleep(100)
bytesRead = 0
bytesRead = oProcess.StandardOutput.Read(x, 0, BuffSize)
Response = String.Concat(Response, String.Join("", x).Substring(0, bytesRead))
MsgBox(Response)
End Sub
End Class
I'm using these methods:
public static Process StartCommand(params string[] commands) => StartCommand(commands, false);
public static Process StartCommand(IEnumerable<string> commands, bool inBackground, bool runAsAdministrator = true)
{
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
if(commands.Any()) p.StartInfo.Arguments = #"/C " + string.Join("&&", commands);
if (runAsAdministrator)
p.StartInfo.Verb = "runas";
if (inBackground)
{
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
p.Start();
return p;
}
Enjoy...

How To: Execute command line in C#, get STD OUT results

How do I execute a command-line program from C# and get back the STD OUT results? Specifically, I want to execute DIFF on two files that are programmatically selected and write the results to a text box.
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "YOURBATCHFILE.bat";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Code is from MSDN.
Here's a quick sample:
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = strCommand;
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Optional
pProcess.StartInfo.WorkingDirectory = strWorkingDirectory;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
There one other parameter I found useful, which I use to eliminate the process window
pProcess.StartInfo.CreateNoWindow = true;
this helps to hide the black console window from user completely, if that is what you desire.
// usage
const string ToolFileName = "example.exe";
string output = RunExternalExe(ToolFileName);
public string RunExternalExe(string filename, string arguments = null)
{
var process = new Process();
process.StartInfo.FileName = filename;
if (!string.IsNullOrEmpty(arguments))
{
process.StartInfo.Arguments = arguments;
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
var stdOutput = new StringBuilder();
process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data); // Use AppendLine rather than Append since args.Data is one line of output, not including the newline character.
string stdError = null;
try
{
process.Start();
process.BeginOutputReadLine();
stdError = process.StandardError.ReadToEnd();
process.WaitForExit();
}
catch (Exception e)
{
throw new Exception("OS error while executing " + Format(filename, arguments)+ ": " + e.Message, e);
}
if (process.ExitCode == 0)
{
return stdOutput.ToString();
}
else
{
var message = new StringBuilder();
if (!string.IsNullOrEmpty(stdError))
{
message.AppendLine(stdError);
}
if (stdOutput.Length != 0)
{
message.AppendLine("Std output:");
message.AppendLine(stdOutput.ToString());
}
throw new Exception(Format(filename, arguments) + " finished with exit code = " + process.ExitCode + ": " + message);
}
}
private string Format(string filename, string arguments)
{
return "'" + filename +
((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) +
"'";
}
The accepted answer on this page has a weakness that is troublesome in rare situations. There are two file handles which programs write to by convention, stdout, and stderr.
If you just read a single file handle such as the answer from Ray, and the program you are starting writes enough output to stderr, it will fill up the output stderr buffer and block. Then your two processes are deadlocked. The buffer size may be 4K.
This is extremely rare on short-lived programs, but if you have a long running program which repeatedly outputs to stderr, it will happen eventually. This is tricky to debug and track down.
There are a couple good ways to deal with this.
One way is to execute cmd.exe instead of your program and use the /c argument to cmd.exe to invoke your program along with the "2>&1" argument to cmd.exe to tell it to merge stdout and stderr.
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
Another way is to use a programming model which reads both handles at the same time.
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = #"/c dir \windows";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = false;
p.OutputDataReceived += (a, b) => Console.WriteLine(b.Data);
p.ErrorDataReceived += (a, b) => Console.WriteLine(b.Data);
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.WaitForExit();
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo(#"program_to_call.exe");
psi.RedirectStandardOutput = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi); ////
System.IO.StreamReader myOutput = proc.StandardOutput;
proc.WaitForExit(2000);
if (proc.HasExited)
{
string output = myOutput.ReadToEnd();
}
You will need to use ProcessStartInfo with RedirectStandardOutput enabled - then you can read the output stream. You might find it easier to use ">" to redirect the output to a file (via the OS), and then simply read the file.
[edit: like what Ray did: +1]
One-liner run command:
new Process() { StartInfo = new ProcessStartInfo("echo", "Hello, World") }.Start();
Read output of command in shortest amount of reable code:
var cliProcess = new Process() {
StartInfo = new ProcessStartInfo("echo", "Hello, World") {
UseShellExecute = false,
RedirectStandardOutput = true
}
};
cliProcess.Start();
string cliOut = cliProcess.StandardOutput.ReadToEnd();
cliProcess.WaitForExit();
cliProcess.Close();
In case you also need to execute some command in the cmd.exe, you can do the following:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C vol";
p.Start();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
This returns just the output of the command itself:
You can also use StandardInput instead of StartInfo.Arguments:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.Start();
// Read the output stream first and then wait.
p.StandardInput.WriteLine("vol");
p.StandardInput.WriteLine("exit");
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
The result looks like this:
Since the most answers here dont implement the using statemant for IDisposable and some other stuff wich I think could be nessecary I will add this answer.
For C# 8.0
// Start a process with the filename or path with filename e.g. "cmd". Please note the
//using statemant
using myProcess.StartInfo.FileName = "cmd";
// add the arguments - Note add "/c" if you want to carry out tge argument in cmd and
// terminate
myProcess.StartInfo.Arguments = "/c dir";
// Allows to raise events
myProcess.EnableRaisingEvents = true;
//hosted by the application itself to not open a black cmd window
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.CreateNoWindow = true;
// Eventhander for data
myProcess.Exited += OnOutputDataRecived;
// Eventhandler for error
myProcess.ErrorDataReceived += OnErrorDataReceived;
// Eventhandler wich fires when exited
myProcess.Exited += OnExited;
// Starts the process
myProcess.Start();
//read the output before you wait for exit
myProcess.BeginOutputReadLine();
// wait for the finish - this will block (leave this out if you dont want to wait for
// it, so it runs without blocking)
process.WaitForExit();
// Handle the dataevent
private void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
{
//do something with your data
Trace.WriteLine(e.Data);
}
//Handle the error
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
//do something with your exception
throw new Exception();
}
// Handle Exited event and display process information.
private void OnExited(object sender, System.EventArgs e)
{
Trace.WriteLine("Process exited");
}
Here is small example:
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
var p = Process.Start(
new ProcessStartInfo("git", "branch --show-current")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
WorkingDirectory = Environment.CurrentDirectory
}
);
p.WaitForExit();
string branchName =p.StandardOutput.ReadToEnd().TrimEnd();
string errorInfoIfAny =p.StandardError.ReadToEnd().TrimEnd();
if (errorInfoIfAny.Length != 0)
{
Console.WriteLine($"error: {errorInfoIfAny}");
}
else {
Console.WriteLine($"branch: {branchName}");
}
}
}
I believe this is shortest form.
Please notice that most of command line tools easily confuse standard output and standard error, sometimes it makes sense just to clue those together into single string.
Also p.ExitCode might be sometimes useful.
Example above serves for purpose of writing command line utility like tools if you want to do it by yourself. Please note that for cli automation it's also possible to use Cake Frosten and Cake Git extension.
You can launch any command line program using the Process class, and set the StandardOutput property of the Process instance with a stream reader you create (either based on a string or a memory location). After the process completes, you can then do whatever diff you need to on that stream.
This might be useful for someone if your attempting to query the local ARP cache on a PC/Server.
List<string[]> results = new List<string[]>();
using (Process p = new Process())
{
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.Arguments = "/c arp -a";
p.StartInfo.FileName = #"C:\Windows\System32\cmd.exe";
p.Start();
string line;
while ((line = p.StandardOutput.ReadLine()) != null)
{
if (line != "" && !line.Contains("Interface") && !line.Contains("Physical Address"))
{
var lineArr = line.Trim().Split(' ').Select(n => n).Where(n => !string.IsNullOrEmpty(n)).ToArray();
var arrResult = new string[]
{
lineArr[0],
lineArr[1],
lineArr[2]
};
results.Add(arrResult);
}
}
p.WaitForExit();
}
This may not be the best/easiest way, but may be an option:
When you execute from your code, add " > output.txt" and then read in the output.txt file.
There is a ProcessHelper Class in PublicDomain open source code which might interest you.
Julian's solution is tested working with some minor corrections. The following is an example that also used https://sourceforge.net/projects/bat-to-exe/ GenericConsole.cs and https://www.codeproject.com/Articles/19225/Bat-file-compiler program.txt for args part:
using System;
using System.Text; //StringBuilder
using System.Diagnostics;
using System.IO;
class Program
{
private static bool redirectStandardOutput = true;
private static string buildargument(string[] args)
{
StringBuilder arg = new StringBuilder();
for (int i = 0; i < args.Length; i++)
{
arg.Append("\"" + args[i] + "\" ");
}
return arg.ToString();
}
static void Main(string[] args)
{
Process prc = new Process();
prc.StartInfo = //new ProcessStartInfo("cmd.exe", String.Format("/c \"\"{0}\" {1}", Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args)));
//new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args));
new ProcessStartInfo("mapTargetIDToTargetNameA3.bat");
prc.StartInfo.Arguments = buildargument(args);
prc.EnableRaisingEvents = true;
if (redirectStandardOutput == true)
{
prc.StartInfo.UseShellExecute = false;
}
else
{
prc.StartInfo.UseShellExecute = true;
}
prc.StartInfo.CreateNoWindow = true;
prc.OutputDataReceived += OnOutputDataRecived;
prc.ErrorDataReceived += OnErrorDataReceived;
//prc.Exited += OnExited;
prc.StartInfo.RedirectStandardOutput = redirectStandardOutput;
prc.StartInfo.RedirectStandardError = redirectStandardOutput;
try
{
prc.Start();
prc.BeginOutputReadLine();
prc.BeginErrorReadLine();
prc.WaitForExit();
}
catch (Exception e)
{
Console.WriteLine("OS error: " + e.Message);
}
prc.Close();
}
// Handle the dataevent
private static void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
{
//do something with your data
Console.WriteLine(e.Data);
}
//Handle the error
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
// Handle Exited event and display process information.
//private static void OnExited(object sender, System.EventArgs e)
//{
// var process = sender as Process;
// if (process != null)
// {
// Console.WriteLine("ExitCode: " + process.ExitCode);
// }
// else
// {
// Console.WriteLine("Process exited");
// }
//}
}
The code need to compile inside VS2007, using commandline csc.exe generated executable will not show console output correctly, or even crash with CLR20r3 error. Comment out the OnExited event process, the console output of the bat to exe will be more like the original bat console output.
Just for fun, here's my completed solution for getting PYTHON output - under a button click - with error reporting. Just add a button called "butPython" and a label called "llHello"...
private void butPython(object sender, EventArgs e)
{
llHello.Text = "Calling Python...";
this.Refresh();
Tuple<String,String> python = GoPython(#"C:\Users\BLAH\Desktop\Code\Python\BLAH.py");
llHello.Text = python.Item1; // Show result.
if (python.Item2.Length > 0) MessageBox.Show("Sorry, there was an error:" + Environment.NewLine + python.Item2);
}
public Tuple<String,String> GoPython(string pythonFile, string moreArgs = "")
{
ProcessStartInfo PSI = new ProcessStartInfo();
PSI.FileName = "py.exe";
PSI.Arguments = string.Format("\"{0}\" {1}", pythonFile, moreArgs);
PSI.CreateNoWindow = true;
PSI.UseShellExecute = false;
PSI.RedirectStandardError = true;
PSI.RedirectStandardOutput = true;
using (Process process = Process.Start(PSI))
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Error(s)!!
string result = reader.ReadToEnd(); // What we want.
return new Tuple<String,String> (result,stderr);
}
}

Categories