如何解决.NET Webbrowser控件中的内存泄漏问题?
这是.NET Web浏览器控件的一个广为人知的旧问题。
简介:拥有.NET webbrowser控件导航到页面会增加从不释放的内存使用量。
重现内存泄漏:将WebBrowser控件添加到窗体。 使用它来导航到任何你想要的页面。 关于:空白作品,在Google图片上滚动直到您的使用率为100MB +,然后在其他地方浏览以注意几乎没有任何内存被释放是一个更具戏剧性的演示。
我目前对应用程序的要求包括长时间运行,显示有限的IE7浏览器窗口。 运行IE7本身有一些混乱的钩子设置,BHO和组策略也不是所希望的,尽管目前这看起来像是后备。 将浏览器嵌入到Windows窗体应用程序中。 使用不同的浏览器基础不适合我。 IE7是必需的。
与此已知内存泄漏相关的以前的主题和文章:
通常建议的修补程序不起作用:
当整个应用程序关闭并重新启动时,内存被清除。
如果这是对问题的肯定修复,我愿意直接在COM或Windows API中编写自己的浏览器控件。 当然,我宁愿不要那么复杂的修复; 我宁愿避免低级别做事情,因为我不想从浏览器支持的功能方面重新发明轮子。 Letalone在自己的样式浏览器中复制IE7功能和非标准行为。
帮帮我?
这个泄漏似乎是非托管内存中的泄漏,所以在你的进程中你没有做任何事情来回收这些内存。 从你的帖子中,我可以看到你已经尽力避免泄漏,并且没有成功。
如果可行,我会建议采用不同的方法。 创建一个单独的应用程序,使用Web浏览器控制并从应用程序启动它。 使用此处描述的方法将新创建的应用程序嵌入到您自己的现有应用程序中。 使用WCF或.NET远程处理与该应用程序通信。 不时重新启动子进程,以防止进入太多内存。
这当然是相当复杂的解决方案,重启过程可能看起来很丑。 每次用户导航到另一个页面时,您可能会诉诸重新启动整个浏览器应用程序。
我拿了udione的代码(它对我有效,谢谢!),并改变了两件小事:
IKeyboardInputSite是一个公共接口,并且具有方法Unregister() ,因此我们在收到对* _keyboardInputSinkChildren *集合的引用后不需要使用反射。
由于视图并不总是直接引用其窗口类(特别是在MVVM中),所以我添加了一个方法GetWindowElement(DependencyObject元素) ,它通过遍历可视树来返回所需的引用。
谢谢,udione
public void Dispose()
{
_browser.Dispose();
var window = GetWindowElement(_browser);
if (window == null)
return;
var field = typeof(Window).GetField("_swh", BindingFlags.NonPublic | BindingFlags.Instance);
var valueSwh = field.GetValue(window);
var valueSourceWindow = valueSwh.GetType().GetField("_sourceWindow", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(valueSwh);
var valuekeyboardInput = valueSourceWindow.GetType().GetField("_keyboardInputSinkChildren", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(valueSourceWindow);
var inputSites = valuekeyboardInput as IEnumerable<IKeyboardInputSite>;
if (inputSites == null)
return;
var currentSite = inputSites.FirstOrDefault(s => ReferenceEquals(s.Sink, _browser));
if (currentSite != null)
currentSite.Unregister();
}
private static Window GetWindowElement(DependencyObject element)
{
while (element != null && !(element is Window))
{
element = VisualTreeHelper.GetParent(element);
}
return element as Window;
}
谢谢你们!
有一种方法可以通过使用反射并从mainForm上的私有字段中删除引用来清除内存泄漏。 这不是一个好的解决方案,但对于绝望的人来说,这里是代码:
//dispose to clear most of the references
this.webbrowser.Dispose();
BindingOperations.ClearAllBindings(this.webbrowser);
//using reflection to remove one reference that was not removed with the dispose
var field = typeof(System.Windows.Window).GetField("_swh", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var valueSwh = field.GetValue(mainwindow);
var valueSourceWindow = valueSwh.GetType().GetField("_sourceWindow", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(valueSwh);
var valuekeyboardInput = valueSourceWindow.GetType().GetField("_keyboardInputSinkChildren", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(valueSourceWindow);
System.Collections.IList ilist = valuekeyboardInput as System.Collections.IList;
lock(ilist)
{
for (int i = ilist.Count-1; i >= 0; i--)
{
var entry = ilist[i];
var sinkObject = entry.GetType().GetField("_sink", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (object.ReferenceEquals(sinkObject.GetValue(entry), this.webbrowser.webBrowser))
{
ilist.Remove(entry);
}
}
}
链接地址: http://www.djcxy.com/p/41831.html
上一篇: How to get around the memory leak in the .NET Webbrowser control?