7

WebBrowser コントロールで MSHTML を使用しているのは、テキスト ノードなど、WebBrowser ではアクセスできないものにアクセスできるためです。ReleaseComObjectここや Web で、参照するすべての COM オブジェクトを呼び出さなければならないという投稿をいくつか見ました。だから、私がこれをするとしましょう:

var doc = myBrowser.Document.DomDocument as IHTMLDocument2;

リリースする必要がありdocますか? bodyこのコードでの方法:

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

これらのオブジェクトは、それらへの参照がなくなるとすぐに解放する RCW によってラップされていませんか? そうでない場合は、ガベージコレクターが作動するとすぐにそれらを解放する(Disposeを使用する代わりに)ファイナライザーを使用して、それぞれのラッパーを作成することをお勧めします(手動で心配する必要がないように)それらを処分する)?

問題は、私のアプリケーションにメモリ リークがあり、これに関連していると思われることです。Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbolANTS メモリ プロファイラーによると、第 2 世代でメモリを使用するオブジェクトのトップ リストにある一連のオブジェクトへの参照を保持している関数の 1 つ (MSHTML オブジェクトを使用する他の多くの関数の中でも) は次のとおりです。

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 メモリ プロファイラのスクリーンショット

そのサイトコンテナが何であるかわかりません。

4

2 に答える 2

7

The RCW will release the COM object when the RCW is finalized, so you don't need to create a wrapper that does this. You call ReleaseComObject because you don't want to wait around for the finalization; this is the same rationale for the Dispose pattern. So creating wrappers that can be Disposed isn't a bad idea (and there are examples out there

For var doc = myBrowser.Document.DomDocument ...;, you should also capture .Document in a separate variable and ReleaseComObject it as well. Any time you reference a property of a COM object which produces another object, make sure to release it.

In GetAttribute, you're casting the element to another interface. In COM programming, that adds another reference. You'll need to do something like var htmlElement = (IHTMLElement) element; so you can release that as well.

Edit - this is the pattern to use when working with COM objects:

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

This should really be done for every COM object reference you have.

于 2013-02-09T15:51:20.667 に答える
1

おそらく、この記事はいくつかの光をもたらします:

COM 参照カウントがどのように機能するか、および AddRef と Release をいつ呼び出すかについてのいくつかの基本的なルールに関する MSDN

あなたの場合、 Release は ReleaseComObject です

于 2013-02-09T20:43:06.340 に答える