Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我真的需要在每个MSHTML对象上调用ReleaseComObject吗?_C#_Com_Mshtml - Fatal编程技术网

C# 我真的需要在每个MSHTML对象上调用ReleaseComObject吗?

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

我将MSHTML与WebBrowser控件一起使用,因为它使我能够访问WebBrowser没有的内容,例如文本节点。我在这里和网上看到过一些帖子,人们说你必须为你引用的每个COM对象调用
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
元素上调用它,那么,当然,原始对象将被释放。这就是what
FinalReleaseComObject
是为-说你真的,终于完成了这个对象。
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;
    }
}