Domanda

Ho una serie di filettati classi di stampa diversi tipi di documenti.Le classi utilizzare l'ereditarietà per condividere un codice comune.Il costruttore della classe richiede il nome del file e il nome della stampante argomenti.Un Print() il metodo crea un nuovo thread, attende che il thread di lavoro a termine utilizzando Thread.Join(timeout) e chiamate Thread.Abort() sul thread di lavoro se il Join timeout.Il thread inizia un'applicazione in grado di aprire il file specificato, fa sì che il file da inviare alla stampante in modo sincrono (in genere mediante applicazione del metodo di Stampa) e le uscite.Il thread di lavoro codice è avvolto in un try{} ... catch{} blocco per affrontare eventuali imprevisti crash dell'applicazione esterna.Il blocco catch contiene minimo di pulizia e di registrazione.

    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 { }
        }
    }

Ho trovato la mia applicazione che non risponde, con il thread principale in un Sonno/Attendere/Partecipare al Thread.Abort() linea.Non ricordo lo stato di questo thread, ma la registrazione che doveva essere eseguita in catch{} il blocco non ha avuto luogo.(Mi In allegato per il mio processo con VS2010 dopo che ho trovato non risponde).

Mi riferisco alle seguenti Nota dal Thread.Metodo Abort:

Il thread che chiama Interruzione potrebbe bloccare se il thread che è in fase di interrotto è in un'area protetta, del codice, come ad esempio un blocco catch, infine, il blocco, o vincolata di esecuzione di regione.Se il thread chiamate Abort contiene un blocco che il filo interrotto richiede un deadlock può verificarsi.

Credo di essere un morto di bloccaggio problema, perché (1) non succede sempre, e (2) a causa della Nota su MSDN (di cui sopra).

  1. La Nota sembra suggerire che l'utilizzo di try{} ... catch{} è MAI cassetta di sicurezza all'interno di un thread, se il thread può essere Abort()'ed.È vero questo?
  2. Non vedo come posso evitare di usare Abort() nel mio scenario.Utilizzando Thread.Interrupt() invece di fare alcuna differenza?
  3. Come posso risolvere il morto di blocco che problema ho?

BackgroundWorker non funziona per me, perché non ho bisogno di rendicontazione intermedia e, cosa più importante, è possibile che il mio thread di lavoro blocco a tempo indeterminato quando si esegue applicazioni di terze parti.Per lo stesso motivo, non posso chiedi il mio thread per terminare, ma si dispone di un'opzione solo per spietatamente Abort() il thread di lavoro.

È stato utile?

Soluzione 3

Penso di aver trovato una soluzione, apportare le seguenti modifiche:

  1. Non chiamare Thread.Abort() se ci sapere che il thread è in esecuzione un catch{} blocco (vedere protected volatile bool isPrinting qui di seguito).
  2. Usare un thread separato per chiamare Thread.Abort() e favorire un cambio di contesto con Sleep(0) (vedere private void AbortPrintWorker() qui di seguito).

    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() crea un thread separato per chiamare Abort() sul thread di lavoro.Credo che questo ha a che fare con il problema evidenziato nel Nota su Abort():

Il thread che chiama Interruzione potrebbe bloccare se il thread che è in fase di interrotto è in un'area protetta, del codice, come ad esempio un blocco catch, infine, il blocco, o vincolata di esecuzione di regione.Se il thread chiamate Abort contiene un blocco che il filo interrotto richiede un deadlock può verificarsi.

Questo è corretto?

Altri suggerimenti

Il tuo meccanismo usando Thread.Abort() non è buono. In effetti, chiamare Thread.Abort() dovrebbe essere evitato.

Il thread che chiama Abort potrebbe bloccare se il thread che viene interrotto si trova in una regione di codice protetta, come un blocco di cattura, infine blocco o una regione di esecuzione vincolata. Se il thread che chiama abort ha un blocco richiesto dal thread abortito, può verificarsi un punto morto. Rif.

Invece, usa un file Lavoro di sfondo che supporta la cancellazione, i rapporti sullo stato di avanzamento (e il marshalling automatico sul thread dell'interfaccia utente nell'evento completato).

Mi sembra che tu stia fondamentalmente controllando l'applicazione PowerPoint per stampare un documento PowerPoint. Pertanto, potresti essere soggetto a qualsiasi finestra di dialogo che l'applicazione ha pubblicato (o ha provato a mettere) sullo schermo. Se tutta questa cosa viene eseguita in background (ad esempio su un server), potrebbe non esserci un utente per respingere tali dialoghi, in modo che possa spiegare una parte del problema. La mia raccomandazione sarebbe quella di esaminare le librerie di terze parti che ti consentirebbero di caricare un file PPT e stamparlo (o convertirlo in PDF e stamparlo) senza dover fare affidamento sull'applicazione PowerPoint. Quindi non dovresti attendere un'app esterna al di fuori del tuo controllo e non dovresti ricorrere a thread di abortire con forza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top