簡體   English   中英

我是否絕對需要在每個MSHTML對象上調用ReleaseComObject?

[英]Do I absolutely need to call ReleaseComObject on every MSHTML object?

我正在使用帶有WebBrowser控件的MSHTML,因為它讓我可以訪問WebBrowser沒有的東西,例如文本節點。 我在這里和網上看過幾篇帖子,人們說你必須為你引用的每個COM對象調用ReleaseComObject 所以,說我這樣做:

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對象的引用在第二代是這一個:

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 "";
}

不知道這里有什么問題,因為attribute只是一個字符串。

這是另一個在ANTS探查器的實例保留圖上顯示的函數(我添加了一堆FinalReleaseComObject但仍然顯示):

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;
    }
}

我添加了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;
    }
}

MessageBox.Show("Couldn't release!"); 線從未被擊中所以我認為一切都已正確發布。 這是ANTS顯示的內容:

ANTS內存分析器截圖

我不知道那個站點容器是什么東西。

當RCW完成時,RCW將釋放COM對象,因此您不需要創建執行此操作的包裝器。 您調用ReleaseComObject是因為您不想等待最終確定; 這與Dispose模式的原理相同。 所以創建可以Dispose d的包裝器並不是一個壞主意(並且有一些例子

對於var doc = myBrowser.Document.DomDocument ...; ,你還應該在單獨的變量和ReleaseComObject捕獲.Document 每次引用生成另一個對象的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;
    }
}

這應該對您擁有的每個COM對象引用都要完成。

可能這篇文章帶來了一些啟示:

關於COM引用計數如何工作的MSDN以及何時調用AddRef和Release的一些基本規則

在您的情況下,Release是ReleaseComObject

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM