Apr 042013
 

Recently I developed an HTML+Javascript based application that embedded the Internet Explorer browser control on the Windows platform.

MFC (in my case VC++ 2005) makes this task quite easy – so it seems. Just create a dialog-based application using the wizard, add the web browser ActiveX control to your dialog, call Navigate() on your browser object and you are done. This is described in many tutorials on the web, so I won’t go further into this.

At first everything looked fine, but when I opened the HTML in standalone IE, my Javascript (jQuery) menu was animated much more fluid compared to the embedded IE.

The symptom

To get a better idea of the performance I used the FishBowl test from the Internet Explorer Test Drive suite with “fish count” set to “auto”. So it draws the maximum number of fishs possible at 60 fps.

This is what I got:

The result was far worse than expected. Only a single fish was drawn in embedded IE, which didn’t even reach 60 fps!

Nearing the solution

As it turns out, the embedded browser uses the IE7 rendering engine by default, even if a newer version of IE is installed. Also, GPU rendering is switched off, so the browser uses software rendering only.

This was fixed by setting the following feature control registry keys:

  • FEATURE_BROWSER_EMULATION – set to the desired version of the IE rendering engine
  • FEATURE_GPU_RENDERING – set to 1 to enable GPU rendering

These keys can be set under HKEY_LOCAL_MACHINE or HKEY_CURRENT_USER for a specific program (executable name), which uses the embedded browser. HKCU is preferred since the program won’t need administrator privileges to write to HKCU.

So when I set FEATURE_BROWSER_EMULATION to 10000 (for IE10) and set FEATURE_GPU_RENDERING to 1, the performance improved to ~850 fishies @ 60 fps. Still not as good as standalone IE with its 1000+ fishies, but quite an improvement!

Digging deeper

Of course 85% performance of the embedded browser was still unacceptable. I also noticed that the animation still looked sloppy compared to standalone IE. It was also noticable in my real application, though not as bad as in the FishBowl test.

When dragging the window of my application around, the performance suddenly improved to standalone IE level. The same thing happened when I created a modal dialog by calling AfxMessageBox(). So I guessed it could have something to do with MFC’s message loop implementation, which is a little bit more involved than the standard GetMessage() -> TranslateMessage() -> DispatchMessage() loop.

By copying the source of CWnd::RunModalLoop() into a method of my own dialog and stripping it down to the bare minimum I found that the PeekMessage( …, PM_NOREMOVE ) calls were the evildoers. By simply replacing them with GetQueueStatus() the standalone IE performance level was finally reached.

I’m not quite sure why the original MFC code creates a performance issue but I think it has something todo with how PeekMessage() operates. It always processes incoming sent messages, despite the PM_NOREMOVE flag. If you look closely at the documentation, you’ll see that PM_NOREMOVE only specifies that queued messages shall not be removed. SendMessage() calls for instance are not put in the queue. So I guess this somehow interferes with the operation of the browser control, by processing the messages out of order.

Problem solved? Not quite. The animation was still slightly sloppy until I set another feature control registry key named FEATURE_ALIGNED_TIMERS. This one is undocumented, it was found at this japanese site.

Even then I notice a minor sloppiness in the FishBowl test, if the fish moves fast. I tried FEATURE_ALLOW_HIGHFREQ_TIMER and timeBeginPeriod( 1 ) with no noticable improvement. Anyway, it is not noticable in my real world application, so case closed.

Summary and step by step

To fix the embedded IE performance issues, I had to set various feature control registry keys and use a modified version of the MFC modal message loop.

Steps:

  1. On startup of application, before instantiating the browser control, set the following feature control registry keys under HKCU:
      • FEATURE_BROWSER_EMULATION – set to the desired version of the IE rendering engine
      • FEATURE_GPU_RENDERING – set to 1
      • FEATURE_ALIGNED_TIMERS – set to 1

    For instance:

  2. Copy the source of CWnd::RunModalLoop() from “wincore.cpp” into a method of your dialog that hosts the browser control, for instance CMyDialog::RunMyModalLoop().
    Find the two occurences of:

    Replace with:

  3. Replace the call to DoModal() with the following code:

TODO:
I currently only have VC++ 2005 available. I will check if the message loop issue was fixed in newer MFC versions.

Update:I just checked the MFC source of VS 2008 and VS 2012, CWnd::RunModalLoop() still calls PeekMessage( …, PM_NOREMOVE ). Although I didn’t actually check the performance of the browser control with VS 2008/2012, I assume the issue still persists. My project now switched to Chromium Embedded Framework, so I won’t check further for now.

Conclusion

Embedding the IE browser control is a valid option for many use cases, even more so with recent IE versions, which are quite performant and good in support of web standards. Although there are other options like the Chromium Embedded Framework, embedding the IE control has some advantages, for instance, the small binary size of the executable.

It is relatively easy to embed the browser control.  If you know the quirks, which I described in this article, you can get good performance which is comparable to other modern browsers.

Disclaimer

The solution described here uses undocumented registry keys which might not be available in future versions of IE (> IE10). It also depends on MFC internals which might not be 100% reliable. The solution is not broadly tested yet. It happens to work on my system but might not work on yours or might even make things worse for you. So be warned and use the solution at your own risk!

 

  3 Responses to “Performance issue of embedded IE browser control and how to fix it”

  1. This is indeed helpful .. Thanks a lot for this research . . ! 😀

  2. Many thanks for the great article!

    I have issue with the following code:

    dlg.Create( CMyDialog::IDD, parent );

    I’ve tried using:

    dlg.Create(IDD_DIALOG, this);

    but compiler reports:

    error C2664: ‘BOOL CDialog::Create(UINT,CWnd *)’ : cannot convert argument 1 from ‘int’ to ‘LPCTSTR’

    Any ideas?

  3. Many thanks! That helped to solve my problem

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

(required)

(required)