Why does Internet Explorer not call DLL_PROCESS_DETACH on my DLL when I call ExitProcess?


A customer asked a question, but as is often the case, the question was much more telling than the answer.

We have an Internet Explorer plug-in which calls Exit­Process to force Internet Explorer to exit. We found that when we do this, our plug-in does not receive a DLL_PROCESS_DETACH notification. What could be preventing our plug-in from receiving the DLL_PROCESS_DETACH notification?

As we saw some time ago when we looked at the way processes shut down (plus an important follow-up or two), all a process has to do to thwart proper delivery of DLL_PROCESS_DETACH notifications is to do something untoward during shutdown, at which point the kernel just gives up and calls Terminate­Process.

But like I said, the answer is much less interesting than the question. What if the user had an unsaved email message at the time you decided to exit Internet Explorer? Recall that plug-ins are a guest in the host process; don't go changing the carpet. When we asked the customer why they were exiting Internet Explorer from their plug-in, we received the explanation, "The reason I am calling Exit­Process is that I do not know another good way to exit Internet Explorer from a plug-in."

In this case, the guest is doing far more than just changing the carpet. The guest called in a demolition company!

"Why did you call the demolition company to destroy my house?"
"I couldn't think of a good way to destroy your house."

The point isn't that it's bad to use a telephone call to hire a demolition company to destroy somebody's house and that you should use some other method to contact them (like, say, a text message). The point is that it's bad to destroy somebody else's house in the first place.

Upon further investigation, the customer was writing a test for their plug-in. They open Internet Explorer and navigate to a page that uses the plug-in. When they are satisfied that the plug-in operated correctly, they want to exit the copy of Internet Explorer in order to conclude the test.

If you want to destroy a house, then destroy your own house. Call Co­Create­Instance(CLSID_Internet­Explorer) to build a house, navigate to your test page with IWeb­Browser2::Navigate, and when you're done, you can destroy the house with IWeb­Browser2::Quit(). There is sample code to do exactly this in the documentation for the IWeb­Browser2 interface.

Bonus chatter: The IWeb­Browser2 interface is scriptable.

var ie = new ActiveXObject("InternetExplorer.Application");
ie.Visible = true;
ie.Navigate("http://www.microsoft.com/");
WScript.Sleep(5000); // five seconds, say
ie.Quit();
Comments (11)
  1. I dont wanna give my name says:

    I don't know why, but the demolition analogy made me crack up.  Perhaps because it's so fitting.

  2. Joshua says:

    I've a habit of writing my code TerminateProcess() safe, so I'd be rather unphased by somebody doing this to my code.

  3. Timothy says:

    Joshua – would you please elaborate or provide a link on being TerminateProcess() safe?

  4. InYourSpaceShipStealingYourGravity says:

    @Timothy: I really doubt that Joshua is writing code that is TerminateProcess() safe, although I'm sure he thinks he does, I'd wager that his customers could prove him wrong with enough time with his application.

  5. Nick says:

    Ahh, IWeb­Browser2.  Such an amazingly full and complete interface around a complex object.  Very useful in so many instances.  But so full of edge cases (depending in part on the version of IE installed) without viable workarounds or solutions that it's almost infeasible for real production applications.

    Granted, it's much, much better now than it was about 4-5 years ago.

  6. Anonymous Coward says:

    @Timothy: Easy. You paste over the first few bytes of TerminateProcess with a jmp to a stub function that creates a new event and waits for it.*

    Your plugin and the hosted webpage can communicate back to the host, so you don't necessarily have to wait an arbitrary five seconds. You can also do your tests and signal the host that you want to quit. (I'm sure Raymond knows this, but I thought it was worth pointing out.)

    * That was tongue in cheek. The only reasonable way I know to guard against foreign code calling TerminateProcess is to make sure it runs in a separate process.

  7. TC says:

    @Nick: it's like any interface: if it has behaviour you don't like, you have to identify and code around that. I've written a wrapper later accordingly. Now I can use that layer to automate IE from script, with few if any nasty surprises!

  8. I was instantly reminded of the incident where a demolition crane operator was asked "why did you just demolish #20?" "Because I have a work order here to demolish … uh … #21… whoops."

    Then there was the computer equivalent, in which our servers+storage team tried to improve lousy mail server performance by installing a new mail server … which reformatted the chunk of SAN which held the entire Medical School's existing mail server. Apparently "it's OK, we can reinstall from the tape backup from 3 days ago sometime tomorrow" wasn't enough to get them off the hook, either.

    The house analogy also reminded me of a phone call on TV from someone's houseguest, with running water in the background: "uhh, where's the water shutoff valve in your house?" In the vein of some of the 'local problem, global solution' tales Raymond has shared, I can imagine a lot of these people using the main water shutoff to get their kids/siblings/guests out of the bathroom faster…

  9. IWeb­Browser2 != IE says:

    If you test with IWeb­Browser2 you doesn't test with IE, you test with IWeb­Browser2.

  10. Joshua says:

    @Timothy: Transactional storage. If the filesystem APIs behave as advertised I'm good.

  11. TC says:

    If you test with IWebBrowser2 you don't test with IE, you test with IWebBrowser2.

    set ie = CreateObject ("Internetexplorer.Application")

    msgbox typename (ie)  ' "IWebBrowser2"

Comments are closed.