I am trying to embed a WebBrowser Control in a C# Winform Application. This sounds easy enough. However I discovered that the WebBrowser control eats up a lot of memory ever
This worked for meand my app no longer climbs to 500MB ram usage anymore, it stays at 60MB forever. The trick is to set the webbrowser to null after disposing it, and before calling GC.
my program repeatedly goes to the same site, 3 different pages, only once per use, not crawling large numbers of pages or anything.
string eventBuffer;
void GetContracts_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var web = sender as WebBrowser;
if (web.Url == e.Url)
{
TaskMaster.Get_Contracts(ref web);
if(Memory.Contracts.Count==0)
{
eventBuffer="UpdateContractFailed";
web.Disposed += new EventHandler(web_Disposed);
web.Dispose();
return;
}
eventBuffer="UpdateContractList";
web.Disposed += new EventHandler(web_Disposed);
web.Dispose();
}
}
private void web_Disposed(object sender, EventArgs e)
{
WebBrowser web = (WebBrowser)sender;
FireEvent(eventBuffer);
**web = null;**
GC.Collect();
thread.Abort();
}
According to MSDN, The System.Windows.Forms.WebBrowser control is a managed wrapper for the ActiveX WebBrowser control, and uses whichever version of the control is installed on the user's computer.
You can find Dispose(bool) method in metadata of WebBrowser class(Press F12 in Visual Stuio) to release unmanaged resource.(NOT Dispose())
The code here
protected override void Dispose(bool disposing) {
if (disposing) {
if (htmlShimManager != null)
{
htmlShimManager.Dispose();
}
DetachSink();
ActiveXSite.Dispose();
}
base.Dispose(disposing);
}
But if you try to call WebBrowser.Dispose(bool), compiler error CS1540 is shown.
WebBrowser class supports Dispose(bool) method, BUT we can't use that.
I think WebBrowser class was designed by wrong way.
I have a idea to call WebBrowser.Dispose(true).
IT IS VERY SIMPLE! but it's not a good way.
Sample Code in here(3 Buttons, and 1 TextBox need)
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Test_20170308_01
{
public partial class Form1 : Form
{
[DllImportAttribute("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize);
public static void FlushMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
public Form1()
{
InitializeComponent();
}
private void addWeb()
{
WebBrowserD webBrowser1 = new WebBrowserD();
webBrowser1.Size = new Size(1070, 585);
this.Controls.Add(webBrowser1);
webBrowser1.Navigate("about:blank");
}
private void RemoveWeb()
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is WebBrowserD)
{
WebBrowserD web = (WebBrowserD)ctrl;
this.Controls.Remove(ctrl);
web.Navigate("about:blank");
web.Dispose(true);
FlushMemory();
}
}
}
private void button1_Click(object sender, EventArgs e)
{
addWeb();
}
private void button2_Click(object sender, EventArgs e)
{
RemoveWeb();
}
private void button3_Click(object sender, EventArgs e)
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is WebBrowserD)
{
WebBrowserD axweb = (WebBrowserD)ctrl;
axweb.Navigate(textBox1.Text);
FlushMemory();
}
}
}
}
public class WebBrowserD : WebBrowser
{
internal void Dispose(bool disposing)
{
// call WebBrower.Dispose(bool)
base.Dispose(disposing);
}
}
}
This code can prevent Memory Leak.
In summary, You need just one class.
public class WebBrowserD : WebBrowser
{
internal void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
I ran into this problem while writing a small "slideshow" application for different intranet pages used by my company. The simplest solution I found was restarting the application after some fixed period of time, an hour in my case. This solution worked well for us because there wasn't a lot of user interaction with the browser.
Public Class MyApplication
Private _AppTimer As Timers.Timer
Public Sub New()
_AppTimer = New Timers.Timer()
_AppTimer.Interval = 1 * 60 * 60 * 1000 '1 Hour * 60 Min * 60 Sec * 1000 Milli
AddHandler _AppTimer.Elapsed, AddressOf AppTimer_Elapsed
_AppTimer.Start()
End Sub
Private Sub AppTimer_Elapsed(s As Object, e As Timers.ElapsedEventArgs)
Application.Restart()
End Sub
End Class
This assumes of course that you have a data persistence mechanism in place.
I experienced this problem with a simple webbrowser control on a form. It navigated to one web page and stayed there. According to task manager within 10 minutes it ate up 2gb of memory from 120mb.
A simple solution for my project was to go to the webbrowser controls properties in visual studio and change the 'AllowNavigation' to false. Now when I run my program it stays at 120mb. I hope this helps someone!
It seems the Navigate() method keeps all the visited pages in memory as you can use the GoBack() method, there is no "memory leak" in fact. My program visits the same Url repeatedly. The "memory leak" problem can be eliminated by using the Refresh() method instand of Navigate() method, followed by a GC.Collect(). The Code is in the following:
try
{
if (webBrowser.Url.Equals("about:blank")) //first visit
{
webBrowser.Navigate(new Uri("http://url"));
}
else
{
webBrowser.Refresh(WebBrowserRefreshOption.Completely);
}
}
catch (System.UriFormatException)
{
return;
}
System.GC.Collect(); // may be omitted, Windows can do this automatically
I'm using the Web Control in an application but since my application navigates to one page only I haven't noticed the issue you mentioned. There's another web control that is actually a wrapper and I don't know if it has the same problem or not. You can find it here.