Why a RaceOnRCWCleanup error when closing a form with WebBrowser control on it?

ChrisA picture ChrisA · Dec 9, 2009 · Viewed 25.3k times · Source

VS2008, .NET 2, VB.NET, XP ...

I have a Windows form, with a WebBrowser control and a Close button, which just does a Me.Close. The form's cancel button is set to the Close button, so that I can hit ESC to close the form.

I set the DocumentText property of the WebBrowser control in the load event, and the HTML displays.

Running the application from Visual Studio, if I click the Close button, the form closes with no error.

If I hit the ESC button I get

RaceOnRCWCleanup was detected Message: An attempt has been made to free an RCW that is in use. The RCW is in use on the active thread or another thread. Attempting to free an in-use RCW can cause corruption or data loss.

If I run the app outside VS, I get no error.

Any ideas a) why the error, and b) how to prevent or suppress it?

Many thanks in advance.

Answer

Hans Passant picture Hans Passant · Dec 10, 2009

It is not an error, it is a warning. Produced by a Managed Debugging Assistant (MDA), an extension to the debugger for managed code, that thinks it is seeing something going wrong in your code. The shoe fits. You are using an RCW, WebBrowser is an COM control. You are killing off the RCW, you are closing your form. The MDA steps in because it thinks it is seeing the web browser in use and it getting killed before the request is completed. That would normally only make sense if you are using a thread in your code.

Are you? If not, don't lose any sleep over it. COM uses reference counting, notorious for not being able to resolve circular references.


Okay, I got a repro for this, enabled by the comments. Yes, this is triggered by the form's CancelButton property or the button's DialogResult property. This happens when the WB has the focus, it sees the Escape key press. Part of the ActiveX plumbing is to tell the container about it so it can respond to keystrokes that should have a side-effect. Shortcut keystrokes, Tab, Enter. And Escape. If the button then closes the form, the debugger sees the WB getting disposed while there are active stack frames from the RCW code on the stack. The danger is that this might cause a crash when the called code returns since the COM component got released, it isn't uncommon.

Seeing this crash is pretty unlikely, but I can imagine that this could bomb when the finalizer thread runs just before the button's Click event returns. The workaround for the MDA and the potential crash is to delay closing the form until after the ActiveX code stops running. Elegantly done with Control.BeginInvoke(). Like this:

    private void CancelButton_Click(object sender, EventArgs e) {
        this.BeginInvoke((MethodInvoker)delegate { this.Close(); });
    }