2013-02-09 22 views
7

Ich verwende MSHTML mit einem WebBrowser-Steuerelement, weil es mir Zugriff auf Dinge gibt, die der WebBrowser nicht tut, wie Textknoten. Ich habe mehrere Posts hier und im Internet gesehen, wo Leute sagen, dass Sie ReleaseComObject für jedes COM-Objekt aufrufen müssen, auf das Sie verweisen. Also, sage ich dies tun:Muss ich ReleaseComObject für jedes MSHTML-Objekt unbedingt aufrufen?

var doc = myBrowser.Document.DomDocument as IHTMLDocument2;

Muss ich doc veröffentlichen? Wie body in diesem Code:

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

Sind nicht diese Aufgaben durch ein RCW gewickelt, dass sie so bald freigeben würde, da es keine weiteren Artikeln zu ihnen? Wenn nicht, wäre es eine gute Idee, einen Wrapper für jeden von ihnen mit einem Finalizer zu erstellen (anstatt Dispose zu verwenden), der sie freigibt, sobald der Garbage Collector startet (so dass ich mir keine Gedanken machen muss) entsorgt sie)?

Die Sache ist, meine Anwendung hat ein Speicherleck und ich glaube, dass damit verbunden ist. Laut ANTS-Speicherprofiler ist eine der Funktionen (neben vielen anderen, die MSHTML-Objekte verwenden), die einen Verweis auf eine Reihe von Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol Objekten enthält, die sich in der obersten Liste von Objekten befinden, die Speicher in Generation 2 verwenden:

Nicht sicher, was hier falsch ist, da attribute nur eine Zeichenfolge ist.

Hier ist eine weitere Funktion, die Instanz Retention des Diagramms auf der ANTS Profiler angezeigt wird (Ich habe eine Reihe von FinalReleaseComObject s ist aber nach wie vor angezeigt):

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

ich hinzugefügt, die ReleaseComObject aber die Funktion scheint immer noch zu einen Verweis auf etwas halten. Hier ist, wie meine Funktion wie jetzt aussieht:

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

Die MessageBox.Show("Couldn't release!"); Linie wird nie getroffen, so gehe ich davon aus, alles richtig freigegeben worden ist. Hier ist, was ANTS zeigt:

ANTS memory profiler screenshot

Ich habe keine Ahnung, was das Standortcontainer Sache ist.

+0

Wenn Sie COM-Objekte in einem Verfahren haben, so dass sie immer vor dem Wurf Ausnahmen aufzuräumen, da sie kann nicht bereinigt werden, nachdem die Ausnahme ausgelöst wurde ... – prprcupofcoffee

Antwort

7

Die RCW wird das COM-Objekt freigeben, wenn die RCW abgeschlossen ist, so dass Sie keinen Wrapper erstellen müssen, der dies tut. Du rufst ReleaseComObject an, weil du nicht auf die Finalisierung warten willst; Das ist das gleiche Argument für das Dispose-Muster. So Wrapper erstellen, die Dispose d sein kann, ist keine schlechte Idee (und es gibt Beispiele gibt

Für var doc = myBrowser.Document.DomDocument ...;, sollten Sie auch .Document in einer separaten Variable erfassen und ReleaseComObject es auch. Jedes Mal, verweisen Sie auf eine Eigenschaft ein COM-Objekt, das ein anderes Objekt, vergewissern Sie sich, es zu veröffentlichen.

In GetAttribute, sind Gießen Sie das Element an eine andere Schnittstelle. In der COM-Programmierung, that adds another reference. Sie brauchen so etwas wie var htmlElement = (IHTMLElement) element; zu tun, so können Sie loslassen das auch.

bearbeiten - das ist das Muster zu verwenden, wenn mit COM-Objekten arbeiten:

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

Das ist wirklich für jeden COM-Objekt Referenz getan werden sollte, die Sie haben.

+0

Bedeutet das, anstatt sie loszulassen, kann ich einfach 'GC.Collect()' ab und zu machen und die RCW-Wrapper werden sich um jedes COM-Objekt kümmern hat keine Referenz mehr (das scheint viel einfacher als die manuelle Freigabe) Wo verwende ich sie, das ist wie tausend Orte)? – Juan

+0

Ich habe versucht, was Sie in Bezug auf meine 'GetAttribute' Methode vorgeschlagen, und ich habe ein' COM-Objekt, das von seinem zugrunde liegenden RCW getrennt wurde, kann nicht verwendet werden. Fehler. Scheint, dass dadurch der gesamte Knoten freigegeben wird, nicht nur die 'IHTMLElement'-Schnittstelle. – Juan

+1

Sie sollten 'GC.Collect()' definitiv nie nennen. Es wird nicht einmal COM-Objekte freigeben, wenn Sie es aufrufen, notwendigerweise - nur die, die bereits für die Finalisierung markiert wurden. [Hier ist ein CodeProject-Artikel] (http://www.codeproject.com/Articles/10888/SafeCOMWrapper-Managed-Disposable-Strongly-Typed-s), der eine automatisierte Art der Freigabe von COM-Objekten behandelt, wenn Sie fertig sind Sie. Die Quintessenz ist jedoch, dass, wenn Sie zwei Systeme mit sehr unterschiedlichen Methoden der Speicherverwaltung kombinieren, es zwangsläufig Schmerz geben wird. – prprcupofcoffee

Verwandte Themen