2013-02-09 1 views
7

私はWebBrowserがテキストノードのようなものにアクセスできないので、WebBrowserコントロールでMSHTMLを使用しています。ここではいくつかの記事を見てきましたが、Web上では参照するすべてのCOMオブジェクトに対してReleaseComObjectと呼ぶ必要があります。だから、私はこれを行うと言う:すべてのMSHTMLオブジェクトでReleaseComObjectを必ず呼び出す必要はありますか?

var doc = myBrowser.Document.DomDocument as IHTMLDocument2;

私はdocを解放する必要がありますか?どのようにこのコードでbody

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

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

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

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 Sの束を追加しましたが、まだ示されている)である:私は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.FinalReleaseComObject(doc3); 
    } 
    catch (Exception ex) 
    { 
     throw ex; 
    } 
} 

何かへの参照を保持しています。ここに私の関数は、今どのように見えるかです:

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 memory profiler screenshot

私はそのサイトのコンテナの事が何であるか見当がつかない。

+0

です例外がスローされた後にクリーンアップすることはできません... – prprcupofcoffee

答えて

7

RCWはRCWがファイナライズされたときにCOMオブジェクトを解放するので、これを行うラッパーを作成する必要はありません。ファイナライズを待つことを望まないので、ReleaseComObjectに電話してください。これはDisposeパターンの根拠と同じです。だから、Dispose dは悪い考えではない可能ラッパーを作成(および実施例はそこ

var doc = myBrowser.Document.DomDocument ...;のためにありますが、あなたはまた、別の変数に.Documentを捕捉しなければならないとReleaseComObjectは、それは同様に。いつでもあなたはのプロパティを参照します別のオブジェクトを作成するCOMオブジェクト、それを解放することを確認してください。GetAttribute

、あなたは別のインターフェイスに要素をキャストしている。COMプログラミング、that adds another referenceで。あなたはとてもあなたが解放することができ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オブジェクト参照のために行われるべきです。

+0

これを解放するのではなく、単にGC.Collect()を毎回実行し、RCWラッパーがそのすべてのCOMオブジェクトを処理しますもう参照がありません(これはマニュアルを手動でリリースするよりも簡単です私は何千もの場所のようなものを使っていますか?) – Juan

+0

私は 'GetAttribute'メソッドに関してあなたが提案したものを試しましたが、その根底にあるRCWから分離されたCOMオブジェクトは使用できません。 'IHTMLElement'インターフェースだけでなく、ノード全体を解放すると思われます。 – Juan

+1

あなたは間違いなく 'GC.Collect()'を呼び出すべきではありません。 COMオブジェクトを呼び出すときにCOMオブジェクトを解放することはありません - 必ずファイナライズのためのマークが付けられているものだけです。 [ここでは、CodeProjectの記事](http://www.codeproject.com/Articles/10888/SafeCOMWrapper-Managed-Disposable-Strongly-Typed-s)で、COMオブジェクトを終了するときにCOMオブジェクトを解放する自動化された方法について説明しています。それら。しかし、要するに、2つのシステムを非常に異なるメモリ管理方法で結合すると、苦痛がかかることになります。 – prprcupofcoffee

1

おそらく、この記事では、いくつかの光をもたらします:あなたのケースでは

MSDN on how COM refcounting works and some basic rules when to call AddRef and Release

、リリースはあなたの方法でCOMオブジェクトを使用している場合は、常に以来、例外をスローする前にそれらをクリーンアップからReleaseComObject

+0

絶対に。 "参照"は、COMオブジェクトとは.NETオブジェクトとは非常に異なるものであり、その違いを理解することは非常に重要です。ありがとう! – prprcupofcoffee

関連する問題