How to get around the memory leak in the .NET Webbrowser control?
This is a widely-known, old issue with the .NET Webbrowser control.
Summary: Having the .NET webbrowser control Navigate to a page increases memory usage that is never freed.
Reproduce the memory leak: Add a WebBrowser control to a form. Use it to Navigate to whatever pages you'd like. about:blank works, scrolling down on Google Images until your usage is 100MB+ and then browsing elsewhere to notice barely any of that memory is freed is a more dramatic demonstration.
My current requirements for an application include running it for long periods of time, displaying a limited IE7 browser window. Running IE7 itself with some bastard setup of hooks, BHOs and group policies isn't desired either, although that's looking like the fallback at this time. Embedding a browser into a Windows Forms application is. Using a different browser base is not an available option for me. IE7 is required.
Previous threads and articles relating to this known memory leak:
Often-proposed fixes that DO NOT WORK:
Memory is cleared when the entire application is closed and restarted.
I'm willing to write my own browser control in COM or Windows API directly, if that's a for-sure fix to the problem. Of course, I would prefer a less complicated fix; I'd rather avoid going down to lower levels to do things, because I don't want to be reinventing the wheel in terms of supported features of a browser. Letalone duplicating IE7 features and nonstandard behaviours in a roll-your-own style browser.
Help?
This leak appears to be a leak in unmanaged memory, so nothing you do in your process is going to reclaim that memory. From your post I can see that you've tried avoid the leak quite extensively and without success.
I would suggest a different approach if feasible. Create a separate application that uses web browser control and start it from your application. Use the method described here to embed newly created application within your own existing application. Communicate with that application using WCF or .NET remoting. Restart the child process from time to time to prevent it from taking to much memory.
This of course is quite complicated solution and the restart process could probably look ugly. You might maybe resort to restarting the whole browser application every time user navigates to another page.
I took the udione's code (it worked for me, thanks!) and changed two small things:
IKeyboardInputSite is a public interface and has method Unregister() , so we don't need to use reflection after we received a reference to *_keyboardInputSinkChildren* collection.
As view not always has a direct reference to its window class (especially in MVVM) I added a method GetWindowElement(DependencyObject element) which returns the required reference by traversing through visual tree.
Thanks, 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;
}
Thank you all!
There is a way to do clear memory leaks by using reflection and removing references from private fields on mainForm. This is not a good solution but for desperate people here is the code:
//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/41832.html
上一篇: 关注第三方应用程序并返回