How IE7 eats desktop heap like nothin’ and seemingly can’t be stopped doing it

Internet Explorer has by no means ever been a frugal application, at least not since it’s 3rd or 4th incarnation.

But, with IE7, resource usage is on a spree. Even idling with a page loaded from internal resources (e.g. navcancel), almost a hundred K of desktop heap are constantly allocated.

Don’t believe me? Check the desktop heap usage yourself.

With standard settings on the default desktop of the interactive window station, this limits the number of open Internet Explorer instances to 60-70.

Now, why on earth would someone try to work with that many Internet Explorers at the same time?

Think Terminal Server.

Think Automation, anybody that comes up with the idea that it’s the easiest way to generate and print a report by coughing up some HTML and use IWebBrowser2 and CLSID_InternetExplorer to do the hard work.

Now, look at the sample code from Knowledge Base article 323568.

It should not be used without uncommenting the call to Release. But then, the expectation is that it keeps running until the end of times.

It certainly does with IE6. But not so with IE7.

Not only is IE7 using much more desktop heap upfront. This code also causes it to hang around with a "Navigation Canceled" page and wait for god-knows-who to close the window. So, roughly 128K of desktop heap stay allocated.

And the next call opens a new page, and slowly but steadily adds to that desktop heap usage.

And when we’re a service under a certain user account, the out-of-process Internet Explorer get’s it’s own non-interactive desktop, which is limited by default to 512K, so things go south sooner or later.

Basically, if the iexplore process doesn’t terminate after the call to Release, we can print reports for a day or two and then we have to reboot the machine.

Is that a bug in IE7? Probably not. It’s probably a bug in the IWebBrowser2 interface specification.

Look at this:

    1 #include "stdafx.h"

    2 

    3 #import <shdocvw.dll> named_guids, no_namespace

    4 

    5 

    6 

    7 int _tmain(int argc, _TCHAR* argv[])

    8 {

    9    HRESULT hr;

   10    IWebBrowser2 * curBrowser;

   11 

   12     CoInitialize(NULL);

   13 

   14     while ( !FAILED(hr = CoCreateInstance(CLSID_InternetExplorer,

   15 

   16       NULL, CLSCTX_SERVER, IID_IWebBrowser2,

   17 

   18         (LPVOID*)&(curBrowser)))) {

   19 

   20             Sleep(1000);

   21 

   22             curBrowser->Stop();

   23             curBrowser->Quit();

   24 

   25             curBrowser->Release();

   26 

   27     }

   28 

   29     CoUninitialize();

   30     return 0;

   31 }

 I just added line 23, a call to the IWebBrowser2::Quit() method. Now, the iexplore.exe process is gone.

Immediately. Before the call to Release. Ouch.

That’s not playing by the COM rules, which IMHO state the server should stick around while references are held. An we hold a reference to a proxy to CSLID_InternetExplorer’s IWebBrowser2 implementation and this should cause iexplore.exe to not terminate. Then, however, it should cease quickly after the reference count of the proxy hits the floor.

Another example of "wrong by default", the most undervalued anti-pattern in the software industry.

Update: IE7’s process does not exit when the page has been printed, and Stop(), Quit() and Release() methods have been called on the IWebBrowser2 interface. Now, that is a bug in IE7. But at least, the desktop heap usage always returns to an acceptable level.

Update 2: It’s the Phishing Filter, dude! Disable it manually or, better, for non-interactive service accounts using IEAK7 and Group Policy.

Disclaimer: The code sample has been modified from it’s original version. All information and source code is provided "AS IS", for demonstrational purposes only, without warranty of any kind, neither express nor implied, including but not limited to the warranties of merchantability, fitness for a particular purpose or non-infringement. In no event shall the blog author be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this code sample, even if advised of the possibility of such damage.

Advertisements
This entry was posted in Coding Horror. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s