views:

2460

answers:

3

Is it possible to load a webpage in a TWebBrowser in a background thread?

When the application loads, I want to download a page from the web into a TWebBrowser, but I don't want to block the gui-thread.

Any suggestions?

Some clarifications:
The webbrowser-component is living on a form, and i want to display a page from the web. But I want to do all the downloading of that page in a background thread, so that loading a heavy page won't block the gui-thread.

I think I'm capable of writing threads in general.

A: 

I have to say that I have never tried something similar, but I'd assume that you will run into problems.

You can have several threads in one process, all implementing message loops and handling the messages for the windows of that thread, but all windows are tied to the thread that created them. Windows messages will always be handled in the thread that the window was created in. So you might be able to call a method from the embedded TWebBrowser control from another thread, but as this will involve sending a message to the web browser window they will still be handled in your GUI thread.

For more information check out the Wikipedia article about Threading in COM and the blog postings by Raymond Chen about STA, like this one.

mghie
+9  A: 

TWebBrowser already downloads stuff in a separate thread. Your program should already remain responsive while it's downloading. You can see this because a frequent pattern is to set the URL and then wait for the download to complete:

WebBrowser1.Navigate(...);
while WebBrowser1.Busy do
  Application.ProcessMessages;

That uses ProcessMessages, so I cannot in good faith recommend it. To be notified when the download is complete, instead of polling like that code does, handle the control's OnNavigateComplete2 event. Beware that the event may be fired for frames as well as the main page.

If you want to display the page, then your TWebBrowser control should not be in a separate thread anyway because it falls under the same rules as any other control used with the VCL. If you're just using the control to download a page, then TWebBrowser might be overkill. You could use Indy, or the operating system's built-in file-downloading functions. Those can be used in separate threads easily.

Rob Kennedy
This makes sense. My application is stopping up for some seconds, but I guess I have to find another reason for it, then.
Vegar
The general idea is good. You just need to use ReadyState intead of Busy. See my comment for more information..
Wouter van Nifterick
+2  A: 

I needed the same (to make automated screenshots of pages), and I started out with Rob's code. It didn't work for the pages that I was loading.. half of the time the busy flag was not set while clearly the page was not done yet.

After some research, this seemed to work:

while (webBrowser1.ReadyState <>  READYSTATE_COMPLETE) do
  Application.ProcessMessages;

where readystate is one of the following:

const READYSTATE_UNINITIALIZED = 0; // Default initialisation state.
const READYSTATE_LOADING = 1; // Object is currently loading data.
const READYSTATE_LOADED = 2; // Object has been initialised.
const READYSTATE_INTERACTIVE = 3; // User can interact with the object but loading has not yet finished.
const READYSTATE_COMPLETE = 4; // All of the object's data has been loaded.


The idea to use readystate came from here: http://gaskell.org/making-the-webbrowser-control-synchronous/

The readystate delphi constants were taken from here: http://www.cryer.co.uk/brian/delphi/twebbrowser/twebbrowser_properties.htm

Wouter van Nifterick