0

[Please note: This question was effectively solved in my other question: How to use a cancellationtokensource to cancel background printing]

I have been using the following method (from SO) to run my printers and print previews on a background thread:

 public static Task StartSTATask(Action func)
        {
            var tcs = new TaskCompletionSource<object>();
            var thread = new Thread(() =>
            {
                try
                {
                    func();
                    tcs.SetResult(null);
                }
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });
            thread.SetApartmentState(ApartmentState.STA);

            thread.Priority = ThreadPriority.AboveNormal;
            thread.Start();
            return tcs.Task;
        }

It works perfectly, without error.

I do not get how to cancel this task appropriately. Should I abort the thread completely or pass in a CancellationTokenSource Token? How is the above code changed to allow cancellation (or abort)?

I am very confused. Thanks for any help with this.

(After much googling, the solution here is not at all as trivial as I had hoped!)

After more thought, I am leaning toward passing the CancellationToken to the func() being executed, and force the function to terminate. In this case, that means the PrintDialogue() to close. Is there another way?

I'm using the above code as:

 public override Task PrintPreviewAsync()
    {
            return StartSTATask(() =>
            {
                // Set up the label page
                LabelMaker chartlabels =  ...

                ... create a fixed document...

                // print preview the document.
                chartlabels.PrintPreview(fixeddocument);
            });
        }

// Print Preview
    public static void PrintPreview(FixedDocument fixeddocument)
    {
        MemoryStream ms = new MemoryStream();

        using (Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite))
        {
            Uri u = new Uri("pack://TemporaryPackageUri.xps");
            PackageStore.AddPackage(u, p);
            XpsDocument doc = new XpsDocument(p, CompressionOption.Maximum, u.AbsoluteUri);
                XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);

            writer.Write(fixeddocument.DocumentPaginator);

            /*  A Working Alternative without custom previewer
            //View in the DocViewer
            var previewWindow = new Window();
            var docViewer = new DocumentViewer();  //  the System.Windows.Controls.DocumentViewer class.
            previewWindow.Content = docViewer;

            FixedDocumentSequence fixedDocumentSequence = doc.GetFixedDocumentSequence();
            docViewer.Document = fixedDocumentSequence as IDocumentPaginatorSource;

            // ShowDialog - Opens a window on top and returns only when the newly opened window is closed.
            previewWindow.ShowDialog();
            */

            FixedDocumentSequence fixedDocumentSequence = doc.GetFixedDocumentSequence();

            // Use my custom document viewer (the print button is removed).
            var previewWindow = new PrintPreview(fixedDocumentSequence);
            previewWindow.ShowDialog();
            PackageStore.RemovePackage(u);
            doc.Close();
        }
    }

Hope it helps explains my problem better.

Community
  • 1
  • 1
Alan Wayne
  • 5,122
  • 10
  • 52
  • 95
  • this has nothing to do with "wpf" tag – Daniel Dec 06 '16 at 05:58
  • @Daniel Sorry...will fix. – Alan Wayne Dec 06 '16 at 06:40
  • 1
    What does `func` do? Not functionality-wise, but code-wise. Whether it's safe to abort the thread depends on that. Whether it's useful to pass in a cancellation token depends on that. Whether there are useful alternatives depends on that. –  Dec 06 '16 at 08:08
  • Absolutely agree with @hvd, I deleted my answer because I couldn't come up with any clean code of any value to add. I wouldn't want my engineers to see stuff like this. Not sure why I even posted an answer to this. A thread inside a task ... Thread.Abort() ... brrr, sends me chills. – Max Dec 06 '16 at 08:11
  • 1
    Can you provide short example of the print code you speak of? I think that will answer @hvd's question. – Adrian Sanguineti Dec 06 '16 at 08:25
  • @hvd Only the most trivial code will ever be Thread.Abort safe (even hello world wouldn't be), printing really isn't. We had the same problem at work (at least the APIs we used couldn't handle misconfigured printer drivers and hang in same rare situations) and the only viable solution is to move the call to its own process. Some printer drivers just don't handle cancellation gracefully. – Voo Dec 06 '16 at 10:52
  • @Voo It's possible in theory to put all the individual bits that cannot be safely aborted in `finally` blocks (where the `try` block may be empty). This would prevent `Thread.Abort` from throwing the exception until that block is finished. But if there are external APIs being called that take long to return, you wouldn't want to delay the thread abortion that long, you would need to find another option, and a separate process is good for that. –  Dec 06 '16 at 11:58
  • @hvd Yep you could use CERs to disable Thread.Abort. Which then would also remove the use for Thread.Abort itself. There just isn't a good use-case for Thread.Abort that doesn't indicate a deeper design problem. – Voo Dec 06 '16 at 12:02
  • @Voo If the operation as a whole takes minutes, but each individual call that cannot be safely aborted lasts no more than seconds, preventing thread abortion during each individual call may be worthwhile, and until the OP clarifies what `func` does, I see this as a realistic possibility. –  Dec 06 '16 at 12:14
  • @Adrain Be happy to post the code if it helps. – Alan Wayne Dec 06 '16 at 13:44
  • @Voo Please see additional code. How would this code be moved to its own process??? Thanks. – Alan Wayne Dec 06 '16 at 14:18
  • @hvd Please see additional code that I'm using as the func(). I'm still learning how to correctly use a cancelllation token. Thanks. – Alan Wayne Dec 06 '16 at 14:24
  • @AlanWayne You're doing a lot. Which part are you trying to cancel? The dialog itself, or the preparation steps for creating the dialog? –  Dec 06 '16 at 20:22
  • @hvd Sorry for the time delay. I would like the user to be able to press Cancel and regardless of where the process is, immediately stop it -- so no further paper is printed--and return to the application as if the Print button had never been used. – Alan Wayne Mar 19 '17 at 18:30

0 Answers0