2

I'm using System.Windows.Forms.WebBrowser in console application to render a web page then invoke scripts on it. To render the web page, I use Application.DoEvents to load and execute scripts.

However sometime the app hangs on calling Application.DoEvents. I found no reason. I don't think I'm creating deadlock on the event loop.

Many people on the web says that one should not use Application.DoEvents at all because it creates more problems than it solves. So I'm thinking there must be an alternative to it. But I've searched a lot and find no alternative to Application.DoEvents.

Does anyone know one? Any information is appreciated. Thank you in advance!


Thanks to Noseratio's help, I finished drafting my code but still there's freezing issue.

Take following code as example(code is too long so I pasted it on pastebin): http://pastebin.com/DkDcrirU

When you run this code, occasionally there will be at least one window fail to close. And if you attach to the frozen process, you will find the code stuck at following line(indicated by ">>>"):

public static bool NavigateLoadAndRender(WebBrowserContext browserContext, string url, TimeSpan loadTimeout, TimeSpan renderTime, out string errMsg)
{
    ForceInitActiveXInstance(browserContext);

    object axi = null;
    Func<Uri> getBrowserUri = null;
    Action<Uri> navigateBrowser = null;
    Func<IHTMLDocument2> getBrowserDoc = null;
    switch (browserContext.WebBrowserType)
    {
        case WebBrowserTypeEnum.Forms:
            {
                var browser = browserContext.GetWebBrowserAsFormsType();
                getBrowserUri = () => browser.Url;
                navigateBrowser = u =>
                {
                    var finished = false;
                    browserContext.SyncContext.Post(state =>
                    {
                        browser.Navigate(u);
                        finished = true;
                    }, null);
                    while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
                };
                getBrowserDoc = () =>
                {
                    IHTMLDocument2 doc = null;
                    bool finished = false;
                    browserContext.SyncContext.Post(state =>
                    {
                        doc = (IHTMLDocument2)browser.Document.DomDocument;
                        finished = true;
                    }, null);
>>>                 while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);

                    return doc;
                };
                axi = GetActiveXInstance(browserContext);
            }
            break;

        case WebBrowserTypeEnum.Wpf:
            {
                var browser = browserContext.GetWebBrowserAsWpfType();
                axi = GetActiveXInstance(browser);
                getBrowserUri = () => browser.Source;
                navigateBrowser = u =>
                {
                    var finished = false;
                    browserContext.SyncContext.Post(state =>
                    {
                        browser.Navigate(u);
                        finished = true;
                    }, null);
                    while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);
                };
                getBrowserDoc = () =>
                {
                    IHTMLDocument2 doc = null;
                    bool finished = false;
                    browserContext.SyncContext.Post(state =>
                    {
                        doc = (IHTMLDocument2)browser.Document;
                        finished = true;
                    }, null);
                    while (!finished) Thread.Sleep(DefaultConfig_SyncContextPostCheckInterval);

                    return doc;
                };
                axi = GetActiveXInstance(browserContext);
            }
            break;

        default: throw new ArgumentException("unknown browser type", browserContext.WebBrowserType.ToString());
    }

    var success = NavigateLoadAndRender(
        axi, url,
        getBrowserUri,
        navigateBrowser,
        //() => DoEvents(browser),
        getBrowserDoc,
        loadTimeout, renderTime, out errMsg);
    return success;
}

Anyone knows what's happening?

  • A proper alternative is to embrace asynchrony. You can use `async`/`await` (but you don't have to). [This example](http://stackoverflow.com/questions/22239357/how-to-cancel-task-await-after-a-timeout-period/22262976#22262976) might be a good starting point. To learn why using `DoEvents` is wrong, check [this blog](http://blogs.msdn.com/b/jfoscoding/archive/2005/08/06/448560.aspx). – noseratio May 09 '15 at 06:24
  • 1
    Thanks Noseratio, I'm inspired by the example. It utilizes TaskScheduler.FromCurrentSynchronizationContext() and Application.Run() to accomplish the task. I think I can also use Application.Run() and SynchronizationContext.Send(...) to do my job. I'm about to use SynchronizationContext.Send(...) because my projects are basing on .NET 3.5. Do you know any .NET 3.5 alternative to TaskScheduler.FromCurrentSynchronizationContext()? – user2710718 May 10 '15 at 10:51
  • For .NET 3.5, check @HansPassant's classic code: http://stackoverflow.com/a/4271581/1768303. Don't use `SynchronizationContext.Send`, it's another source of different kind of problems. Rather, use `SynchronizationContext.Post` or `Control.BeginInvoke`. – noseratio May 10 '15 at 11:35
  • Hi Noseratio, thank you so much. However I still encounters problem, the app still get frozen(the code in SynchronizationContext.Post(...) never get executed). I've updated my question. Do you know what's happening? Or anything I'm doing wrong? Thank you very much! – user2710718 May 13 '15 at 03:50
  • As I said, you cannot use `Thread.Sleep` in a busy-waiting loop. The thread needs to pump Windows messages for `WebBrowser` to work. I have another example that works for .NET 2.0 and up: http://stackoverflow.com/a/22296644/1768303 – noseratio May 13 '15 at 04:03

0 Answers0