[.NET,iTextSharp]用程式呼叫cmd(command line)執行exe(以開啟adobe reader列印pdf為例)

  • 4489
  • 0
  • 2016-03-24

[.NET,iTextSharp]用程式呼叫cmd(command line)執行exe(以開啟adobe reader列印pdf為例)

要透過程式碼呼叫adobe reader來列印pdf的話,比較簡單明瞭的方式就是透過command line來呼叫,呼叫command line列印的方式有兩種,一種是呼叫之後,command line會自動關閉,另外一種則是呼叫之後,command line還是開啟著。
本篇的adobe reader版本是11.0,作業系統環境是windows 8 64bit,如果是用其他版本的adobe reader或是其他版本的windows,請自行修改路徑喔!
1.執行之後會跳出cmd視窗,但是穩定性高,幾乎可以執行所有種類的外部.exe,透過Process.StandardInput.WriteLine()直接下你要執行的.exe + filename即可。
如果要解決跳出cmd視窗的問題的話,建議可以用console執行這段程式碼然後透過排程管理員執行console就OK了,就不會跳出cmd視窗。
想要透過設定Process的參數,讓cmd視窗不會跳出來的話,試了很久都沒成功,目前推測是不可能的,推測可能是StandardInput.WriteLine()導致強制跳出cmd視窗。
首先要先取得adobe reader的安裝路徑,主要是透過.net的Registry物件,細節請看下面程式碼片段:

var adobe = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Classes").OpenSubKey("acrobat").OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command");
string adobeExeFileName = adobe.GetValue("").ToString();
adobeExeFileName = adobeExeFileName.Substring(1, adobeExeFileName.IndexOf(".exe") + 3);

然後用Process.StandardInput.WriteLine()直接執行:

var processStartInfo = new ProcessStartInfo();            
processStartInfo.WorkingDirectory = Path.GetDirectoryName(adobeExeFileName);
processStartInfo.FileName = "cmd.exe";
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;

// set additional properties     
Process proc = Process.Start(processStartInfo);                       
proc.StandardInput.WriteLine(Path.GetFileName(adobeExeFileName) + " /p " + pdfFileName);
proc.Close();

會自動跳出列印pdf的視窗:

參考的完整程式碼:

private void PrintToPrinterBtn_Click(object sender, EventArgs e)
{
   
	Document doc = new Document(PageSize.A8);
	string pdfFileName = Application.StartupPath + "\\test.pdf";
	PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(pdfFileName, FileMode.Create));
	doc.Open();
	doc.Add(new Paragraph("when open this pdf, the printer will start to print!!!"));
	doc.Close();

	//這是會跳出commandline視窗的方式,但是非常穩定(因為是StandardInput.WriteLine的關係,但也一定會跳出cmd視窗),就算phantomjs也能透過這種指令執行
	//如果要解決跳出cmd視窗的問題的話,建議可以用console執行這段程式碼然後透過排程管理員執行就OK了,就不會跳出cmd視窗
	var adobe = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Classes").OpenSubKey("acrobat").OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command");
	string adobeExeFileName = adobe.GetValue("").ToString();
	adobeExeFileName = adobeExeFileName.Substring(1, adobeExeFileName.IndexOf(".exe") + 3);

	//設定cmd參數
	var processStartInfo = new ProcessStartInfo();            
	processStartInfo.WorkingDirectory = Path.GetDirectoryName(adobeExeFileName);
	processStartInfo.FileName = "cmd.exe";
	processStartInfo.RedirectStandardOutput = true;
	processStartInfo.RedirectStandardInput = true;
	processStartInfo.UseShellExecute = false;

	//開始執行
	Process proc = Process.Start(processStartInfo);                       
	proc.StandardInput.WriteLine(Path.GetFileName(adobeExeFileName) + " /p " + pdfFileName);
	proc.Close();
			  
	MessageBox.Show("已完成");
}

2.列印時不會跳出cmd視窗寫法:
一樣是透過Process物件執行cmd指令,差別就在於會設定Process.StartInfo.WindowStyle的屬性為隱藏cmd視窗的彈出。相信在絕大多數的情況下,大部分人會選擇這個隱藏跳出cmd視窗的作法拉,但是實際測試真的有碰到不支援的.exe的情況(ex:PhantomJS),所以姑且叫這個作法的穩定性為80%吧!

Process pdfProcess = new Process();
pdfProcess.StartInfo = newProcess;
//在此隱藏執行視窗
pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
pdfProcess.Start();
pdfProcess.Close();

執行結果跟上面相同,就不重複貼了。

完整程式碼:

private void PrintToPrinterHideBtn_Click(object sender, EventArgs e)
{       
	Document doc = new Document(PageSize.A8);
	string pdfFileName = Application.StartupPath + "\\test.pdf";
	PdfWriter writer = PdfWriter.GetInstance(doc, new FileStream(pdfFileName, FileMode.Create));
	doc.Open();
	doc.Add(new Paragraph("when open this pdf, the printer will start to print!!!"));
	doc.Close();

	//這是隱藏command line視窗的方式,曾經碰過.exe無法這樣執行的,ex:Phantomjs
	//透過Registry取得adobe reader的安裝路徑
	var adobe = Registry.LocalMachine.OpenSubKey("Software").OpenSubKey("Classes").OpenSubKey("acrobat").OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command");
	string adobeExeFileName = adobe.GetValue("").ToString();
	adobeExeFileName = adobeExeFileName.Substring(1, adobeExeFileName.IndexOf(".exe") + 3);

	ProcessStartInfo newProcess = new ProcessStartInfo("\"" + adobeExeFileName + "\"", " /p \"" + pdfFileName + "\"");
	newProcess.CreateNoWindow = true;
	newProcess.RedirectStandardOutput = true;
	newProcess.UseShellExecute = false;

	Process pdfProcess = new Process();
	pdfProcess.StartInfo = newProcess;
	//在此隱藏執行視窗
	pdfProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
	pdfProcess.Start();
	pdfProcess.Close();

	MessageBox.Show("已完成");
}

要注意的地方就是,不論使用上列哪一種寫法,最後釋放資源的時候最好使用Process.Close(),而不要用Process.WaitForExit(),如果印表機剛好當掉的話,就會把原本執行的應用程式(webform or winform)給block住,然後你的應用程式就死當不能動囉~
美中不足的地方就是,還是會產出pdf檔案而且不能當下執行完立刻刪除,以及列印完畢之後,adobe reader仍然是開啟著的(但是剛才列印的檔案已關閉)。

這篇大概是這樣。。。

參考資料:

Can you print a pdf file using ITextSharp (c#) ? If yes how?

Adobe Reader Command Line Reference

Printing PDFs from Windows Command Line