我是否绝对需要在每个MSHTML对象上调用ReleaseComObject?

Jua*_*uan 7 c# com mshtml

我正在使用带有WebBrowser控件的MSHTML,因为它让我可以访问WebBrowser没有的东西,例如文本节点.我在这里和网上看过几篇帖子,人们说你必须调用ReleaseComObject你引用的每个COM对象.所以,说我这样做:

var doc = myBrowser.Document.DomDocument as IHTMLDocument2;

我需要发布doc吗?如何body在此代码中:

var body = (myBrowser.Document.DomDocument as IHTMLDocument2).body;

RCW包装的这些对象是否会在没有更多引用的情况下立即释放它们?如果没有,最好使用终结器(而不是使用Dispose)为每个人创建一个包装器,一旦垃圾收集器启动就会释放它们(这样我就不用担心了处理他们)?

问题是,我的应用程序有内存泄漏,我相信这与此有关.根据ANTS内存分析器,其中一个函数(在许多其他恰好使用MSHTML对象的函数中)持有对Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol第2代中使用内存的对象顶部对象的一组对象的引用,这个是:

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 "";
}
Run Code Online (Sandbox Code Playgroud)

不知道这里有什么问题,因为attribute它只是一个字符串.

这是ANTS分析器的实例保留图上显示的另一个函数(我添加了一堆FinalReleaseComObjects但仍然显示):

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我添加了ReleaseComObject但功能似乎仍然是对某事的引用.以下是我的函数现在的样子:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

MessageBox.Show("Couldn't release!");条线从未被击中,因此我认为一切都已正确发布.这是ANTS显示的内容:

ANTS内存分析器截图

我不知道那个站点容器是什么东西.

prp*_*fee 7

当RCW完成时,RCW将释放COM对象,因此您不需要创建执行此操作的包装器.你打电话ReleaseComObject是因为你不想等待最终确定; 这与Dispose模式的原理相同.所以创建可以是Disposed的包装器并不是一个坏主意(并且有一些例子

因为var doc = myBrowser.Document.DomDocument ...;,你也应该.Document在一个单独的变量中捕获ReleaseComObject它.每次引用生成另一个对象的COM对象的属性时,请确保释放它.

GetAttribute,您将元素转换为另一个接口.在COM编程中,这增加了另一个参考.你需要做类似的事情var htmlElement = (IHTMLElement) element;,你也可以释放它.

编辑 - 这是使用COM对象时使用的模式:

IHTMLElement element = null;
try
{
    element = <some method or property returning a COM object>;
    // do something with element
}
catch (Exception ex) // although the exception type should be as specific as possible
{
    // log, whatever

    throw; // not "throw ex;" - that makes the call stack think the exception originated right here
}
finally
{
    if (element != null)
    {
        Marshal.ReleaseComObject(element);
        element = null;
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该对您拥有的每个COM对象引用都要完成.