I'm using MSHTML with a WebBrowser control because it gives me access to things the WebBrowser doesn't such as text nodes. I've seen several posts here and on the web where people say you must call ReleaseComObject
for every COM object you reference. So, say I do this:
var doc = myBrowser.Document.DomDocument as IHTMLDocument2;
Do I need to release doc
? How body
in this code:
var body = (myBrowser.Document.DomDocument as IHTMLDocument2).body;
Aren't these objects wrapped by a RCW that would release them as soon as there are no more references to them? If not, would it be a good idea to create a wrapper for each of them with a finalizer (instead of using Dispose) that would release them as soon as the garbage collector kicks in (such that I don't need to worry about manually disposing them)?
The thing is, my application has a memory leak and I believe is related to this. According to ANTS memory profiler, one of the functions (among many others that happen to use MSHTML objects) that is holding a reference to a bunch of Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol
objects which are on the top list of objects using memory in Generation 2 is this one:
internal static string GetAttribute(this IHTMLDOMNode element, string name)
{
var attribute = element.IsHTMLElement() ? ((IHTMLElement)element).getAttribute(name) : null;
if (attribute != null) return attribute.ToString();
return "";
}
Not sure what's wrong here since attribute
is just a string.
Here is another function that is shown on the ANTS profiler's Instance Retention Graph (I added a bunch of FinalReleaseComObject
s but is still shown):
private void InjectFunction(IHTMLDocument2 document)
{
if (null == Document) throw new Exception("Cannot access current document's HTML or document is not an HTML.");
try
{
IHTMLDocument3 doc3 = document as IHTMLDocument3;
IHTMLElementCollection collection = doc3.getElementsByTagName("head");
IHTMLDOMNode head = collection.item(0);
IHTMLElement scriptElement = document.createElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)scriptElement;
IHTMLDOMNode scriptNode = (IHTMLDOMNode)scriptElement;
script.text = CurrentFuncs;
head.AppendChild(scriptNode);
if (Document.InvokeScript(CurrentTestFuncName) == null) throw new Exception("Cannot inject Javascript code right now.");
Marshal.FinalReleaseComObject(scriptNode);
Marshal.FinalReleaseComObject(script);
Marshal.FinalReleaseComObject(scriptElement);
Marshal.FinalReleaseComObject(head);
Marshal.FinalReleaseComObject(collection);
//Marshal.FinalReleaseComObject(doc3);
}
catch (Exception ex)
{
throw ex;
}
}
I added the ReleaseComObject
but the function seems to still be holding a reference to something. Here is how my function looks like now:
private void InjectFunction(IHTMLDocument2 document)
{
if (null == Document) throw new Exception("Cannot access current document's HTML or document is not an HTML.");
try
{
IHTMLDocument3 doc3 = document as IHTMLDocument3;
IHTMLElementCollection collection = doc3.getElementsByTagName("head");
IHTMLDOMNode head = collection.item(0);
IHTMLElement scriptElement = document.createElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)scriptElement;
IHTMLDOMNode scriptNode = (IHTMLDOMNode)scriptElement;
script.text = CurrentFuncs;
head.AppendChild(scriptNode);
if (Document.InvokeScript(CurrentTestFuncName) == null) throw new Exception("Cannot inject Javascript code right now.");
Marshal.FinalReleaseComObject(scriptNode);
Marshal.FinalReleaseComObject(script);
Marshal.FinalReleaseComObject(scriptElement);
Marshal.FinalReleaseComObject(head);
Marshal.FinalReleaseComObject(collection);
Marshal.ReleaseComObject(doc3);
}
catch (Exception ex)
{
MessageBox.Show("Couldn't release!");
throw ex;
}
}
The MessageBox.Show("Couldn't release!");
line is never hit so I assume everything is been released properly. Here is what ANTS shows:
I have no idea what that site container thing is.