views:

905

answers:

3

I'm running into a perplexing problem with an ActiveX control I'm writing - sometimes, Internet Explorer appears to fail to properly unload the control on process shutdown. This results in the control instance's destructor not being called.

The control is written in C++, uses ATL and it's compiled using Visual Studio 2005. The control instance's destructor is always called when the user browses away from the page the control is embedded in - the problem only occurs when the browser is closed.

When I run IE under a debugger, I don't see anything unusual - the debugger doesn't catch any exceptions, access violations or assertion failures, but the problem is still there - I can set a breakpoint in the control's destructor and it's never hit when I close the broswer.

In addition, when I load a simple HTML page that embeds multiple instances of the control I don't see the problem. The problem only appears to happen when the control is instantiated from our web application, which inserts tags dynamically into the web page - of course, not knowing what causes this problem, I don't know whether this bit of information is relevant or not, but it does seem to indicate that this might be an IE problem, since it's data dependent.

When I run the simple test case under the debugger, I can set a breakpoint in the control's destructor and it's hit every time. I believe this rules out a problem with the control itself (say, an error that would prevent the destructor from ever being called, like an interface leak.)

I do most of my testing with IE 6, but I've seen the problem occur on IE 7, as well. I haven't tested IE 8.

My working hypothesis right now is that there's something in the dynamic HTML code that causes the browser to leak an interface on the ActiveX control. So far, I haven't been able to produce a good test case that reproduces this outside of the application, and the application is a bit too large to make a good test case.

I was hoping that someone might be able to provide insight into possible IE bugs that are known to cause this kind of behavior. The answer provided below, by the way, is too general - I'm looking for a specific set of circumstances that is known to cause this. Surely someone out there has seen this before.

+2  A: 

To debug a problem in COM with C++ where an object's (C++) destructor is not being called, the best approach is to focus on how the COM object's refcounts are being incremented or decremented. What is probably happening is that somebody is incrementing the refcount one too many times, and then not decrementing it the same number of times. This leads to the object not being freed.

It is possible that your dynamic HTML is simply showing up a bug in IE, which doesn't happen if you use a static page.

If there is a bug in IE, the trick would be to figure out what causes the bug to appear, and what you can do to trick IE into releasing your COM object properly (like, making the HTML go away).

Thanks for the suggestions. The dynamic HTML code is sufficiently hairy that nobody's quite sure how it works, and my attempts to unravel it just enough to create a test case that reproduces the problem have failed so far.
Ori Pessach
A: 

Another approach - add cleanup code to your DllMain function (adding that function if it doesn't already exist). Then regardless of reference counts (and reference count errors), when your DLL is unloaded you can clean yourself up:

BOOL WINAPI DllMain(HINSTANCE, DWORD dwReason, LPVOID) {
    if (dwReason == DLL_PROCESS_DETACH) {
        CleanUpAnyObjectsStillAlive();
    }
}

Oh, and a word of warning - don't take too long doing your cleanup - if you do, I can't promise the process shutdown won't kill you anyway.

Bruce
You do realize that in a control written in MFC can't do that, right?
Ori Pessach
Why? I'm not an MFC expert, but I don't see how a library like MFC could *prevent* you from providing your own DllMain if you wanted to include one. Do you mean that MFC controls don't provide a way to clean them up? Suprising if the case.
Bruce
Or perhaps you mean MFC provides its own implementation of DllMain, and doesn't allow you to hook into it? That would be a problem.
Bruce
That's part of the problem. The other part is that the exact cleanup sequence of MFC controls is controlled by MFC, and you'd better stay out of it - especially in a scenario where cleanup does happen, but not always. ATL is similar, but I'm not as familiar with the details as I am with MFC.
Ori Pessach
Ok, then my MFC ignorance is showing. If MFC has special cleanup requirements, I don't know enough about it to help.
Bruce
MFC uses a hash table to route WM_* messages to window class instances it manages. If DllMain is called before the interfaces are freed, MFC nukes the hash tables, which causes it to assert later when WM_DESTROY is delivered to the control's window. It's really messy.
Ori Pessach
A: 

I have the same problem, but only on a specific computer. This computer also has a problem with the Flash ActiveX, that remains alive after closing the tab. My guess is that the problem is not with your code. Do you have that problem on other computers?

I've seen this on other machines.
Ori Pessach