Threads for WebBrowser.Print()
browser, c#, multithreading, printing
Solution
Main problem with your code is `WebBrowser` wrong using.
`WebBrowser` supposed to be used for interactive web-browsing, during it user do some things in the internet. But in your case you are using WebBrowser just for the printing after downloading the html. This is wrong by two reasons:
- Your code creates whole Windows Forms Control and not using even half of its functionality.
- Your code tries to use the WinForms Control in the background thread, which leads to the unexpected behaviour.
`BackgroundWorker` class supposed to be used for
execute a time-consuming operation (like downloads and database transactions) in the background.
Much more:
You must be careful not to manipulate any user-interface objects in your `DoWork` event handler. Instead, communicate to the user interface through the `ProgressChanged` and `RunWorkerCompleted` events.
Your code will fail in the background thread, because WinForms control is a `user-interface object`.
Just for the record, WebBrowser.Print method invokes native windows API, so you have no chance that this will work in background. From the disassembly code:
this.AxIWebBrowser2.ExecWB(NativeMethods.OLECMDID.OLECMDID_PRINT,
NativeMethods.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
ref obj,
IntPtr.Zero);
So, my suggestion for your code is:
- Remove usage of the `WebBrowser` class in the background. Use `HttpWebRequest` instead for downloading the web content.
- Choose other way for the printing your downloaded content. Options are:
- `PrintDocument` implementation (example for it is here).
- Use MS Office classes for opening and printing your html-files (first parameter for the Print method is a `Boolean Background`, I think this can help you). As far as I know, even in 2010 this approach works well.
- Check other questions about printing (here is a discussion of printing the images, but this is the same thing, I think).
PS: in the comments you've said that you may need the `PDF` from your html. I did this by C# by two ways:
- Batching using the wkhtmltopdf
- Using Microsoft Office Add-in: Microsoft Save as PDF or XPS. This Add-in should be installed on the server, after that you can easily use MS Office classes for saving the output in the PDF format.
Some update here:
As we have an `async/await` and TPL options for the time-consuming operations, I don't recommend you to use the `BackgroundWorker` class anymore.
Problem
I'm trying to create GUI program that generates HTML invoices, and sends them for printing. I have this working. However, now I want to introduce threading. I have a form with a `BackgroundWorker`. The Background worker runs this code: ``` #region BackGroundWorker private void bg_htmlGeneration_DoWork(object sender, DoWorkEventArgs e) { //SOME MORE CODE.. foreach (XElement ele in Lib.GetInvoiceElement(inv, ico.Supplier)) { PrintDocument(Lib.CreateHTMLFile()); } } #endregion public void PrintDocument(string fileName) { var th = new Thread(() => { WebBrowser webBrowserForPrinting = new WebBrowser(); webBrowserForPrinting.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(PrintDocumentHandler); webBrowserForPrinting.Url = new Uri(fileName); Application.Run(); }); th.SetApartmentState(ApartmentState.STA); th.Start(); } public void PrintDocumentHandler(object sender, WebBrowserDocumentCompletedEventArgs e) { ((WebBrowser)sender).Print(); ((WebBrowser)sender).Dispose(); Application.ExitThread(); } ``` Everything runs through fine. However, the `WebBrowser` object refuses to print. There are no errors (that are obvious), the program finishes off with nothing sent to the printer. When I take away the threading, the program works again. My knowledge of threading is weak, and I'm pretty much teaching myself - so presumably I'm misunderstanding how threading priority is set. Here's How it should work: - User selects Invoice(s) on Main Form, chooses to print. - Background thread goes away and prints them while user continues on the program. Any ideas would be appreciated.