Print Multiple PDF file in one click - c#

I want to print existing .pdf files in one click.
It works fine when i only print one file at a time, but when choose to print multiple files at once it only prints one file.
Here's my code:
List<string> ListFilePath; //Assume i have a collection of filepath stored here
foreach(string FilePath in ListFilePath)
{
Process proc = new Process();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.Verb = "print";
proc.StartInfo.FileName = FilePath;
proc.StartInfo.Arguments = String.Format(#"/p /h {0}", FilePath);
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (proc.HasExited == false)
{
proc.WaitForExit(10000);
}
proc.EnableRaisingEvents = true;
proc.Close();
}
Where's my mistake?

Your code is launching a process and creating a printing job on the server. Are you sure that's right, and it's not supposed to print on the client instead?
One thing you might try is to merge all pdfs into one (PDFSharp does that nicely) and then print just the merged pdf.

Can You assign to the event handler, and stop the process then ?
Not inside this method.

This works great for me
foreach (string pdf in ListFilePath)
{
string filename = System.IO.Path.GetFileName(pdf);
string fullPath = (#"U:\Define\Full\Path" + filename);
Print(fullPath, 'PrinterNameHere');
}
public static bool Print(string file, string printer)
{
try
{
Process.Start(Registry.LocalMachine.OpenSubKey(
#"SOFTWARE\Microsoft\Windows\CurrentVersion" +
#"\App Paths\AcroRd32.exe").GetValue("").ToString(),
string.Format("/h /t \"{0}\" \"{1}\"", file, printer));
return true;
}
catch
{ return false; }
}

Related

I was asked to create a windows service that prints pdf files , the code below works when i run it on a console application but not on the service

here is my printing code :
//Print & Move the files after printing
DirectoryInfo sourceinfo = new DirectoryInfo(#"C:\fold");
DirectoryInfo target = new DirectoryInfo(#"C:\fold1");
if (!Directory.Exists(target.FullName))
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in sourceinfo.GetFiles())
{
if (fi.Length != 0)
Pdf.PrintPDFs(fi.FullName);
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
fi.Delete();
}
and here is my pdf class (it uses adobe acrobat reader) :
class Pdf
{
public static Boolean PrintPDFs(string pdfFileName)
{
try
{
Process proc = new Process();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.Verb = "print";
//Define location of adobe reader/command line
//switches to launch adobe in "print" mode
proc.StartInfo.FileName =
#"C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe";
proc.StartInfo.Arguments = String.Format(#"/p /h {0}", pdfFileName);
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (proc.HasExited == false)
{
proc.WaitForExit(10000);
}
proc.EnableRaisingEvents = true;
proc.Close();
KillAdobe("AcroRd32");
return true;
}
catch
{
return false;
}
}
//For whatever reason, sometimes adobe likes to be a stage 5 clinger.
//So here we kill it with fire.
private static bool KillAdobe(string name)
{
foreach (Process clsProcess in Process.GetProcesses().Where(
clsProcess => clsProcess.ProcessName.StartsWith(name)))
{
clsProcess.Kill();
return true;
}
return false;
}
}
I think the windows service has a problem accessing the adobe acrobat reader because the code needs to open it to print files.

ffmpeg from a C# app using Process class - user prompt not shown in standardError

I am writing an app to run ffmpeg using c#. My program redirects the standardError output to a stream so it can be parsed for progress information.
During testing I have found a problem:
If the output is shown in a command window rather than being redirected ffmpeg will display it's normal headers followed by "file c:\temp\testfile.mpg already exists. overwrite [y]". If I click on the command window and press y the program continues to encode the file.
If the StandardError is redirected to my handler and then printed to the console, I see the same header information that was displayed in the command window now printed to the console. except for the file...already exists prompt. If I click in the command window and press y the program continues to process the file.
Is there a stream other than standardOutput or standardError that is used when the operator is prompted for information, or am I missing something else?
public void EncodeVideoWithProgress(string filename, string arguments, BackgroundWorker worker, DoWorkEventArgs e)
{
Process proc = new Process();
proc.StartInfo.FileName = "ffmpeg";
proc.StartInfo.Arguments = "-i " + " \"" + filename + "\" " + arguments;
proc.StartInfo.UseShellExecute = false;
proc.EnableRaisingEvents = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = false;
proc.StartInfo.CreateNoWindow = false; //set to true for testing
proc.ErrorDataReceived += new DataReceivedEventHandler(NetErrorDataHandler);
proc.Start();
proc.BeginErrorReadLine();
StreamReader reader = proc.StandardOutput;
string line;
while ((line = reader.ReadLine()) != null)
{ Console.WriteLine(line); }
proc.WaitForExit();
}
private static void NetErrorDataHandler(object sendingProcess,
DataReceivedEventArgs errLine)
{
if (!String.IsNullOrEmpty(errLine.Data))
{
Console.WriteLine(errLine.Data);
}
}
Rather than going through all this stuff, use the "-y" command line option when you start the process, which will force ffmpeg to overwrite existing files.

Executing a process with Process.Start() using the path of the process

I'm doing this:
public static void ExecProcess(String path, string filename)
{
Process proc = new Process();
proc.StartInfo.FileName = path + "nst.exe";
proc.StartInfo.Arguments = filename;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
proc.WaitForExit();
var stringa = proc.StandardOutput.ReadToEnd();
proc.Close();
}
The problem is my process is using the path of my C# application and not its path. So nst.exe is in C:\Desktop but when I call it with the code above the execution path became C:\\Documents\VisualStudio\MyProject\Debug\.
How can I execute the process in his path?
[EDIT]
This is how I call the method:
public void EseguiOttimizzatore()
{
OttimizzatoreService.ExecProcess(#"C:\Users\Developer\Desktop\", _idPlanning.ToString() + ".dat");
}
Set the WorkingDirectory property of StartInfo:
proc.StartInfo.WorkingDirectory = #"C:\Users\Developer\Desktop\";

How to run a batch file within a C# GUI form

How would you execute a batch script within a GUI form in C#
Could anyone provide a sample please?
System.Diagnotics.Process.Start("yourbatch.bat"); ought to do it.
Another thread covering the same issue.
This example assumes a Windows Forms application with two text boxes (RunResults and Errors).
// Remember to also add a using System.Diagnostics at the top of the class
private void RunIt_Click(object sender, EventArgs e)
{
using (Process p = new Process())
{
p.StartInfo.WorkingDirectory = "<path to batch file folder>";
p.StartInfo.FileName = "<path to batch file itself>";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
p.WaitForExit();
// Capture output from batch file written to stdout and put in the
// RunResults textbox
string output = p.StandardOutput.ReadToEnd();
if (!String.IsNullOrEmpty(output) && output.Trim() != "")
{
this.RunResults.Text = output;
}
// Capture any errors written to stderr and put in the errors textbox.
string errors = p.StandardError.ReadToEnd();
if (!String.IsNullOrEmpty(errors) & errors.Trim() != ""))
{
this.Errors.Text = errors;
}
}
}
Updated:
The sample above is a button click event for a button called RunIt. There's a couple of text boxes on the form, RunResults and Errors where we write the results of stdout and stderr to.
I deduce that by executing within a GUI form you mean showing execution results within some UI-Control.
Maybe something like this:
private void runSyncAndGetResults_Click(object sender, System.EventArgs e)
{
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo(#"C:\batch.bat");
psi.RedirectStandardOutput = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
System.Diagnostics.Process batchProcess;
batchProcess = System.Diagnostics.Process.Start(psi);
System.IO.StreamReader myOutput = batchProcess.StandardOutput;
batchProcess.WaitForExit(2000);
if (batchProcess.HasExited)
{
string output = myOutput.ReadToEnd();
// Print 'output' string to UI-control
}
}
Example taken from here.

Obtain Exitcode from CMD using c#

I am using following code to write the PATH, EXECUTABLE NAME and ARGUMENTS to a batch file and execute it through CMD using c#. The problem is sometimes the application dosent starts up after executing the batch file. And the c# code dosent give me exception or any notification.
For which i want to get the Exitcode from CMD to determine if the commands executed properly.
How can i determine Exit code ?
public void Execute()
{
try
{
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());
}
Log.Flow_writeToLogFile("Executed .Bat file : " + LatestFileName);
process.WaitForExit(1000);
if (process.ExitCode != 0)
{
int iExitCode = process.ExitCode;
}
stream.Close();
process.Close();
srinput.Close();
sroutput.Close();
}
else
{
ExceptionHandler.writeToLogFile("File not found");
}
}
catch (Exception ex)
{
ExceptionHandler.writeToLogFile(System.Environment.NewLine + "Target : " + ex.TargetSite.ToString() + System.Environment.NewLine + "Message : " + ex.Message.ToString() + System.Environment.NewLine + "Stack : " + ex.StackTrace.ToString());
}
}
_________________Update___________________
script inside Batchfile : [Note that Notepads.exe is wrong to get the error ]
START
Notepads.EXE
"if "%ERRORLEVEL%" == "1" exit /B 1"
It is much easier to run the process directly instead of using creating a batch file that you later execute since you lose some control since you are using a batch script layer.
Use this code instead:
/// <summary>
/// Execute external process.
/// Block until process has terminated.
/// Capture output.
/// </summary>
/// <param name="binaryFilename"></param>
/// <param name="arguments"></param>
/// <param name="currentDirectory"></param>
/// <param name="priorityClass">Priority of started process.</param>
/// <returns>stdout output.</returns>
public static string ExecuteProcess(string binaryFilename, string arguments, string currentDirectory, ProcessPriorityClass priorityClass)
{
if (String.IsNullOrEmpty(binaryFilename))
{
return "no command given.";
}
Process p = new Process();
p.StartInfo.FileName = binaryFilename;
p.StartInfo.Arguments = arguments;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
if (!String.IsNullOrEmpty(currentDirectory))
p.StartInfo.WorkingDirectory = currentDirectory;
p.StartInfo.CreateNoWindow = false;
p.Start();
// Cannot set priority process is started.
p.PriorityClass = priorityClass;
// Must have the readToEnd BEFORE the WaitForExit(), to avoid a deadlock condition
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
if (p.ExitCode != 0)
{
throw new Exception(String.Format("Process '{0} {1}' ExitCode was {2}",
binaryFilename,
arguments,
p.ExitCode));
}
//string standardError = p.StandardError.ReadToEnd();
//if (!String.IsNullOrEmpty(standardError))
//{
// throw new Exception(String.Format("Process '{0} {1}' StandardError was {2}",
// binaryFilename,
// arguments,
// standardError));
//}
return output;
}
I use it in a number of projects and it works like a charm.
If you HAVE to go the batch script route, make sure that the batch script set exitcode properly.
Does the Process.ExitCode property not give you what you want? Obviously you'd need to make sure that the batch file itself exits with the right exit code, mirroring the application it's running.
By the way, you should use using statements to make sure that all the relevant streams are closed in the face of exceptions - and I would suggest the asynchronous ways of reacting to data from the application, rather than synchronous IO. If you do stick to synchronous IO, you should have another thread reading from standard error - otherwise if the process writes a lot of data to standard error, it will block waiting for you to clear the buffer.
Use ExitCode like follows:
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = #"usbformat.bat";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true; // change to false to show the cmd window
proc.Start();
proc.WaitForExit();
if (proc.ExitCode != 0)
{
return false;
}
else
{
return true;
}
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);
}
you can get it from procinfo.ExitCode

Categories

Resources