我有一组打印不同类型文档的线程类。这些类使用继承来共享公共代码。类构造函数需要文件名和打印机名称参数。 Print()
方法创建一个新的工作线程,等待工作线程使用Thread.Join(timeout)
完成,如果Thread.Abort()
超时,则在工作线程上调用Join
。工作线程启动一个可以打开指定文件的应用程序,使文件同步发送到打印机(通常使用应用程序的Print方法)并退出。工作线程的代码包含在try{} ... catch{}
块中,以处理外部应用程序的任何无法预料的崩溃。 catch块包含最少的清理和日志记录。
internal static FilePackage TryPrintDocumentToPdf(string Filename)
{
.....
Logging.Log("Printing this file using PowerPoint.", Logging.LogLevel.Debug);
printableFormat = true;
fc = new FileCollector(Email2Pdf.Settings.Printer.PdfAttachmentCollectDirectoryObj, FileCollector.CollectMethods.FileCount | FileCollector.CollectMethods.FilesNotInUse | FileCollector.CollectMethods.ProcessExit);
fc.FileCount = 1;
fc.ProcessNames = new string[] { OfficePowerPointExe, Email2Pdf.Settings.Printer.PrinterExe };
fc.Prepare();
using (PowerPointPrinter printer = new PowerPointPrinter(Filename, Email2Pdf.Settings.Printer.PdfAttachmentPrinter))
{
printer.KillApplicationOnClose = true;
printer.Print();
printOk = printer.PrintOk;
}
.....
}
internal abstract class ApplicationPrinter : IDisposable
{
protected abstract string applicationName { get; }
protected string filename;
protected string printer;
protected bool workerPrintOk;
protected bool printOk;
public bool PrintOk { get { return printOk; } }
public bool KillApplicationOnClose { get; set; }
public void Print()
{
System.Threading.Thread worker = new System.Threading.Thread(printWorker);
DateTime time = DateTime.Now;
worker.Start();
if (worker.Join(new TimeSpan(0, Email2Pdf.Settings.Printer.FileGenerateTimeOutMins, 0)))
{
printOk = workerPrintOk;
}
else
{
worker.Abort();
printOk = false;
Logging.Log("Timed out waiting for " + applicationName + " file " + filename + " to print.", Logging.LogLevel.Error);
}
}
protected abstract void Close();
protected abstract void printWorker();
public virtual void Dispose() { Close(); }
}
internal class PowerPointPrinter : ApplicationPrinter
{
private const string appName = "PowerPoint";
protected override string applicationName { get { return appName; } }
private Microsoft.Office.Interop.PowerPoint.Application officePowerPoint = null;
public PowerPointPrinter(string Filename, string Printer)
{
filename = Filename;
printer = Printer;
this.Dispose();
}
protected override void printWorker()
{
try
{
officePowerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
officePowerPoint.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
Microsoft.Office.Interop.PowerPoint.Presentation doc = null;
doc = officePowerPoint.Presentations.Open(
filename,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
doc.PrintOptions.ActivePrinter = printer;
doc.PrintOptions.PrintInBackground = Microsoft.Office.Core.MsoTriState.msoFalse;
doc.PrintOptions.OutputType = Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides;
doc.PrintOut();
System.Threading.Thread.Sleep(500);
doc.Close();
//Marshal.FinalReleaseComObject(doc);
doc = null;
workerPrintOk = true;
}
catch (System.Exception ex)
{
Logging.Log("Unable to print PowerPoint file " + filename + ". Exception: " + ex.Message, Logging.LogLevel.Error);
Close();
workerPrintOk = false;
}
}
protected override void Close()
{
try
{
if (officePowerPoint != null)
officePowerPoint.Quit();
Marshal.FinalReleaseComObject(officePowerPoint);
officePowerPoint = null;
if (KillApplicationOnClose)
Utility.KillProcessesByName(OfficePowerPointExe);
}
catch { }
}
}
我发现我的应用程序没有响应,主线程在Thread.Abort()行的Sleep / Wait / Join中。我不记得工作线程的状态,但是应该在catch{}
块中执行的日志记录没有发生。 (我发现它没有响应后,我
我参考Thread.Abort Method中的以下注意:
调用Abort的线程可能会阻塞正在进行的线程 中止是在受保护的代码区域中,例如catch块, 最后阻塞或约束执行区域。如果线程那个 调用Abort持有一个被中止线程需要的锁,一个死锁 可能会发生。
我认为我有一个死锁问题,因为(1)它并不总是发生,(2)因为MSDN上的注意(上图)。
try{} ... catch{}
编辑,则在线程内使用Abort()
NEVER 是安全的。这是真的吗?Abort()
。使用Thread.Interrupt()
代替会有什么不同吗? BackgroundWorker对我不起作用,因为我不需要进度报告,更重要的是,我的工作线程可能会在执行第三方应用程序时无限期地阻塞。出于同样的原因,我不能要求我的线程终止,但只有一个选项 - 无情地Abort()
工作线程。
答案 0 :(得分:3)
使用Thread.Abort()
的机制不是很好。实际上,应该避免调用Thread.Abort()
。
调用Abort的线程可能会阻塞正在进行的线程 中止是在受保护的代码区域中,例如catch块, 最后阻塞或约束执行区域。如果线程那个 调用Abort持有一个被中止线程需要的锁,一个死锁 可以发生。 Ref
相反,使用BackgroundWorker支持取消,进度报告(以及在已完成事件中自动编组到UI线程)。
答案 1 :(得分:0)
在我看来,您基本上是远程控制PowerPoint应用程序以打印PowerPoint文档。因此,您可能会受到应用程序在屏幕上显示(或试图提出)的任何对话框的影响。如果整个事情在后台运行(例如在服务器上),则可能没有用户解雇任何此类对话框,因此可以解释部分问题。我的建议是调查第三方库,这些库允许您加载PPT文件并将其打印(或将其转换为PDF并打印),而不必依赖PowerPoint应用程序。然后你不必等待你控制之外的外部应用程序,你就不必采取强制中止线程。
答案 2 :(得分:0)
我认为通过进行以下更改找到了解决方案:
Thread.Abort()
阻止(请参阅下面的catch{}
),请不要致电protected volatile bool isPrinting
。使用单独的帖子来呼叫Thread.Abort()
,并鼓励使用Sleep(0)
进行上下文切换(请参阅下面的private void AbortPrintWorker()
)。
internal abstract class ApplicationPrinter : IDisposable
{
protected abstract string applicationName { get; }
protected string filename;
protected string printer;
protected bool workerPrintOk;
protected bool printOk;
public bool PrintOk { get { return printOk; } }
public bool KillApplicationOnClose { get; set; }
protected System.Threading.Thread worker;
protected volatile bool isPrinting;
public void Print()
{
worker = new System.Threading.Thread(printWorker);
DateTime time = DateTime.Now;
worker.Start();
if (worker.Join(new TimeSpan(0, Email2Pdf.Settings.Printer.FileGenerateTimeOutMins, 0)))
{
printOk = workerPrintOk;
}
else
{
AbortPrintWorker();
printOk = false;
Logging.Log("Timed out waiting for " + applicationName + " file " + filename + " to print.", Logging.LogLevel.Error);
}
}
protected abstract void printWorker();
public abstract void Dispose();
private void AbortPrintWorker()
{
System.Threading.Thread abortThread = new System.Threading.Thread(abortWorker);
if (isPrinting)
{
abortThread.Start();
System.Threading.Thread.Sleep(0);
abortThread.Join();
}
else
{
worker.Join();
}
}
private void abortWorker()
{
worker.Abort();
worker.Join();
}
}
internal class PowerPointPrinter : ApplicationPrinter
{
private const string appName = "PowerPoint";
protected override string applicationName { get { return appName; } }
private Microsoft.Office.Interop.PowerPoint.Application officePowerPoint = null;
public PowerPointPrinter(string Filename, string Printer)
{
filename = Filename;
printer = Printer;
this.Dispose();
}
protected override void printWorker()
{
try
{
isPrinting = true;
officePowerPoint = new Microsoft.Office.Interop.PowerPoint.Application();
officePowerPoint.DisplayAlerts = Microsoft.Office.Interop.PowerPoint.PpAlertLevel.ppAlertsNone;
Microsoft.Office.Interop.PowerPoint.Presentation doc = null;
doc = officePowerPoint.Presentations.Open(
filename,
Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoFalse,
Microsoft.Office.Core.MsoTriState.msoFalse);
doc.PrintOptions.ActivePrinter = printer;
doc.PrintOptions.PrintInBackground = Microsoft.Office.Core.MsoTriState.msoFalse;
doc.PrintOptions.OutputType = Microsoft.Office.Interop.PowerPoint.PpPrintOutputType.ppPrintOutputSlides;
doc.PrintOut();
System.Threading.Thread.Sleep(500);
doc.Close();
Marshal.FinalReleaseComObject(doc);
doc = null;
workerPrintOk = true;
isPrinting = true;
}
catch (System.Exception ex)
{
isPrinting = false;
Logging.Log("Unable to print PowerPoint file " + filename + ". Exception: " + ex.Message, Logging.LogLevel.Error);
workerPrintOk = false;
}
}
public override void Dispose()
{
try
{
if (officePowerPoint != null)
officePowerPoint.Quit();
Marshal.FinalReleaseComObject(officePowerPoint);
officePowerPoint = null;
if (KillApplicationOnClose)
Utility.KillProcessesByName(OfficePowerPointExe);
}
catch { }
}
}
AbortPrintWorker()
创建一个单独的线程来调用工作线程上的Abort()
。我相信这会处理Abort()
上注意中突出显示的问题:
调用Abort的线程可能会阻塞正在进行的线程 中止是在受保护的代码区域中,例如catch块, 最后阻塞或约束执行区域。如果线程那个 调用Abort持有一个被中止线程需要的锁,一个死锁 可能会发生。
这是对的吗?