C# 我真的需要在每个MSHTML对象上调用ReleaseComObject吗?
我将MSHTML与WebBrowser控件一起使用,因为它使我能够访问WebBrowser没有的内容,例如文本节点。我在这里和网上看到过一些帖子,人们说你必须为你引用的每个COM对象调用C# 我真的需要在每个MSHTML对象上调用ReleaseComObject吗?,c#,com,mshtml,C#,Com,Mshtml,我将MSHTML与WebBrowser控件一起使用,因为它使我能够访问WebBrowser没有的内容,例如文本节点。我在这里和网上看到过一些帖子,人们说你必须为你引用的每个COM对象调用ReleaseComObject。所以,假设我这样做: var doc=myBrowser.Document.DomDocument作为IHTMLDocument2 我是否需要发布doc?如何在本代码中显示body: var body=(myBrowser.Document.DomDocument作为IHTMLD
ReleaseComObject
。所以,假设我这样做:
var doc=myBrowser.Document.DomDocument作为IHTMLDocument2代码>
我是否需要发布doc
?如何在本代码中显示body
:
var body=(myBrowser.Document.DomDocument作为IHTMLDocument2.body)代码>
这些对象不是由RCW包装的吗?RCW会在不再引用它们时立即释放它们吗?如果不是,那么使用终结器(而不是使用Dispose)为它们中的每一个创建一个包装器是一个好主意吗?该终结器将在垃圾收集器启动时立即释放它们(这样我就不需要担心手动处理它们)
问题是,我的应用程序内存泄漏,我相信这与此有关。根据ANTS memory profiler的说法,在第二代内存使用对象列表的顶部,有一个函数(在许多其他碰巧使用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 "";
}
不确定这里出了什么问题,因为属性
只是一个字符串
下面是ANTS profiler的实例保留图上显示的另一个函数(我添加了一堆FinalEleaseComObject
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.ReleaseComObject(doc3);
}
catch (Exception ex)
{
MessageBox.Show("Couldn't release!");
throw ex;
}
}
MessageBox.Show(“无法释放!”)代码>行从未命中,所以我假设所有内容都已正确发布。以下是ANTS展示的内容:
我不知道站点容器是什么。当RCW最终确定时,RCW将释放COM对象,因此您不需要创建完成此操作的包装器。您调用ReleaseComObject
,因为您不想等待完成;这与Dispose模式的原理相同。因此,创建可以Dispose
d的包装器并不是一个坏主意(这里有一些例子)
对于var doc=myBrowser.Document.DomDocument…;
,您还应该在单独的变量中捕获.Document
,并ReleaseComObject
它。任何时候引用生成另一个对象的COM对象的属性时,请确保释放它
在GetAttribute
中,您正在将元素强制转换到另一个接口。在COM编程中,您需要执行类似var htmlElement=(IHTMLElement)元素;
的操作,以便也可以释放该元素
编辑-这是处理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;
}
}
IHTMLElement元素=null;
尝试
{
元素=;
//用元素做些什么
}
catch(Exception ex)//尽管异常类型应该尽可能具体
{
//日志,随便什么
throw;//不是“throw ex;”-这使得调用堆栈认为异常就起源于这里
}
最后
{
if(元素!=null)
{
Marshal.ReleaseComObject(元素);
元素=空;
}
}
对于您拥有的每个COM对象引用,都应该这样做。本文可能会带来一些启示:
在您的情况下,Release是ReleaseComObject这是否意味着我可以偶尔执行GC.Collect()
,而RCW包装器将处理每个不再具有引用的COM对象(这似乎比在我使用它们的任何地方手动释放它们都要容易,就像在一千个地方一样)?我尝试了关于我的GetAttribute
方法的建议,得到了一个与其基础RCW分离的COM对象,无法使用。
错误。这样做似乎会释放整个节点,而不仅仅是ihtmlement
接口。您绝对不应该调用GC.Collect()
,几乎永远都不会。当你调用COM对象时,它甚至不会释放COM对象,只会释放那些已经被标记为要完成的对象。这讨论了一种在使用COM对象时释放COM对象的更自动化的方法。不过,底线是,当你使用非常不同的内存管理方法组合两个系统时这肯定会有痛苦。我看到您经常使用FinalReleaseComObject
。使用ReleaseComObject
。调用直到引用计数为零-因此,如果您调用它,例如,在已强制转换为ihtmlement
的元素上调用它,那么,当然,原始对象将被释放。这就是whatFinalReleaseComObject
是为-说你真的,终于完成了这个对象。myBrowser.Document
是一个System.Windows.Forms.HtmlDocument
不是COM对象,IIRC。所以不能为它调用ReleaseComObject
。如果你在一个方法中有COM对象,总是在抛出异常之前清理它们,因为在抛出异常后无法清理它们…绝对是。COM对象的“引用”与.NET对象的“引用”非常不同,理解两者的区别至关重要。谢谢!
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;
}
}