31

我正在尝试在 C# Winform 应用程序中嵌入 WebBrowser 控件。这听起来很容易。但是我发现每次调用 Navigate 方法时,WebBrowser 控件都会占用大量内存。内存永远不会被释放。内存使用量越来越大……</p>

网上很多人都有同样的问题,但我还没有找到令人满意的答案。这是迄今为止我发现的关于这个问题的最佳讨论:

IE WebBrowser 控件中的内存泄漏

有人建议升级到 IE8 来解决这个问题。

但是,无论用户是否安装了最新的 IE 版本,我都需要一个可行的解决方案。我无法控制用户环境。

有人知道如何释放 WebBrowser 控件占用的内存吗?有解决方法吗?WebBrowser 控件是否有替代品?

更新: 我刚刚做了一些测试。在工作中,我正在运行 Windows XP 和 IE6。记忆没有在那里增长。调用导航方法时内存会增加,但会在一段时间后释放。在家里,我正在运行 Vista 并升级到 IE8。在这里我也看不到问题了。看起来这个问题是特定于 IE7 的。所以问题应该改写为“安装 IE7 时如何修复 IE WebBrowser Control 中的内存泄漏”。任何人都可以确认这个问题是特定于 IE7 的吗?

4

18 回答 18

12

我的应用程序在导航时也不断消耗内存,不再释放。我在这里为我找到了解决方案:http: //social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765-46e8-833d-6021ef79e0c8

为了完整起见,请发布值得注意的摘录:

-- in class definition

    [DllImport("KERNEL32.DLL", EntryPoint = "SetProcessWorkingSetSize", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    internal static extern bool SetProcessWorkingSetSize(IntPtr pProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);

    [DllImport("KERNEL32.DLL", EntryPoint = "GetCurrentProcess", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    internal static extern IntPtr GetCurrentProcess();

-- 当你想减少内存时调用的代码

        IntPtr pHandle = GetCurrentProcess();
        SetProcessWorkingSetSize(pHandle, -1, -1);

所有荣誉: http ://social.msdn.microsoft.com/profile/mike_t2e/?type=forum&referrer=http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/88c21427-e765 -46e8-833d-6021ef79e0c8 用于发布解决方案。

http://ict-engineer.blogspot.com/2010/10/net-webbrowser-control-memory-leak.html 用于 SEO 正确,所以我可以找到它;)

问候

编辑:如果这可以帮助您快速解决问题 - 好。但是你应该覆盖你的应用程序设计,如果有的话,你使用的模式,如果你构建的时间更长的话,重构它......

于 2011-06-06T21:10:30.563 回答
8

基本思想是,

“杀死我自己,然后重生。”

Windows 将解决所有内存问题。

但是如果你先关闭你的应用程序,你就不能开始一个新的。

所以,开始一个新的,并关闭旧的。

首先打开一个新的,然后关闭一个旧的。


public void SOLVE_ALL_MY_MEMORY_PROBLEM()
{
  System.Diagnostics.Process.Start("MyProgram.exe");
  Application.Exit();
}

https://www.youtube.com/watch?v=aTBlKRzNf74

如果有参数,

public void SOLVE_ALL_MY_MEMORY_PROBLEM()
{
  System.Diagnostics.Process.Start("MyProgram.exe", "PARA_para_dance");
  Application.Exit();
}

转到 Program.cs

    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if(args.Count() > 0)
            Application.Run(new Form1(args[0]));
        else
            Application.Run(new Form1());
    }

并且,转到 Form1.cs 并制作另一个 Form1()

    public Form1()
    {
        InitializeComponent();
    }

    public Form1(string dance_name)
    {
        InitializeComponent();

        ...
    }

或者你可以使用临时文件!!!

于 2015-06-12T17:41:29.237 回答
7

我刚刚创建了一个带有 Web 浏览器控件的简单应用程序来尝试复制您的结果。我发现是的,每次导航到一个页面时,所使用的内存都会显着增加。但是,这不是内存泄漏,因为如果您继续导航,您会看到不久之后,内存显着下降,表明垃圾收集器做了它的事情。为了证明这一点,我在每次调用 Navigate 后强制垃圾收集器进行收集,并且每次调用 Navigate 后使用的总内存保持在几乎相同的数量。

因此,虽然每次您“导航”时它都会占用内存,但它不是内存泄漏,您的内存将被释放。如果它收集得太快,只需调用 GC.Collect();

于 2009-05-25T01:41:48.390 回答
4

根据MSDN, System.Windows.Forms.WebBrowser 控件是 ActiveX WebBrowser 控件的托管包装,并使用安装在用户计算机上的任何版本的控件。

您可以在 WebBrowser 类的元数据中找到Dispose(bool)方法(在 Visual Stuio 中按 F12)来释放非托管资源。(不是 Dispose())

这里的代码

protected override void Dispose(bool disposing) {
    if (disposing) {
        if (htmlShimManager != null)
        {
            htmlShimManager.Dispose();
        }
        DetachSink();
        ActiveXSite.Dispose();
    }
    base.Dispose(disposing);
}

但是如果您尝试调用 WebBrowser.Dispose(bool),则会显示编译器错误CS1540

WebBrowser 类支持 Dispose(bool) 方法,但我们不能使用它。
我认为 WebBrowser 类的设计方式错误。

我有个主意叫 WebBrowser.Dispose(true)。
非常简单!但这不是一个好方法。

此处的示例代码(需要 3 个按钮和 1 个文本框)

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);
        }
    }
}

此代码可以防止内存泄漏。

总之, 你只需要一堂课。

    public class WebBrowserD : WebBrowser
    {
        internal void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }
于 2017-03-09T06:11:17.633 回答
3

有一个替代控件使用 Gecko(Firefox 使用的引擎)而不是 Trident,并且可以很好地与 MSHTML 界面配合使用。

您的页面将在 Gecko 中呈现,您将完全控制浏览器的设置、插件、安全性和任何其他可自定义的功能。

缺点是您需要将 Gecko 与您的应用程序一起发布,我上次使用的是 Firefox 2 的等效版本,它大约为 8MB。

我很久以前发布了一个应用程序,它比较了 IE 和 Firefox 的渲染,两者都在你编辑 CSS 时更新。我没有遇到您在使用 Web 浏览器控件时遇到的内存问题,但我发现 Gecko 控件非常易于使用。它没有与 .net WebBrowser 控件相同的托管包装类,但它很容易解决这个问题。

于 2009-05-24T19:53:20.370 回答
2

似乎 Navigate() 方法将所有访问过的页面保存在内存中,因为您可以使用 GoBack() 方法,实际上没有“内存泄漏”。我的程序反复访问同一个 URL。“内存泄漏”问题可以通过使用 Refresh() 方法代替 Navigate() 方法,然后使用 GC.Collect() 来消除。代码如下:

       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
于 2014-03-22T00:14:58.117 回答
1

我想这个问题已经很久没有答案了。这么多线程都有相同的问题,但没有决定性的答案。

我找到了解决此问题的方法,并希望与仍然面临此问题的所有人分享。

step1:创建一个新表单,比如 form2,并在其上添加一个 Web 浏览器控件。step2:在您拥有 webbrowser 控件的 form1 中,将其删除。step3:现在,转到 Form2 并将此 webbrowser 控件的访问修饰符设为 public,以便可以在 Form1 中访问它。 step4:在 form1 中创建一个面板并创建 form2 的对象并将其添加到面板中。Form2 frm = new Form2 (); frm.TopLevel = 假;frm.Show(); panel1.Controls.Add(frm); step5:定期调用以下代码 frm.Controls.Remove(frm.webBrowser1); frm.Dispose();

而已。现在,当您运行它时,您可以看到 webbrowser 控件已加载,它将定期处理,并且不再挂起应用程序。

您可以添加以下代码以提高效率。

    IntPtr pHandle = GetCurrentProcess();
    SetProcessWorkingSetSize(pHandle, -1, -1);


    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
于 2013-05-09T05:42:31.580 回答
1

我浏览了整个互联网,但无法找到这个问题的答案。我使用以下方法修复了它:

Protected Sub disposeBrowers()
    If debug Then debugTrace()
    If Me.InvokeRequired Then
        Me.Invoke(New simple(AddressOf disposeBrowers))
    Else
        Dim webCliffNavigate As String = webCliff.Url.AbsoluteUri

        'Dim webdollarNavigate As String = webDollar.Url.AbsoluteUri
        Me.splContainerMain.SuspendLayout()
        Me.splCliffDwellers.Panel2.Controls.Remove(webCliff)
        Me.splDollars.Panel2.Controls.Remove(webDollar)
        RemoveHandler webCliff.DocumentCompleted, AddressOf webCliff_DocumentCompleted
        RemoveHandler webDollar.DocumentCompleted, AddressOf webDollar_DocumentCompleted
        RemoveHandler webCliff.GotFocus, AddressOf setDisposeEvent
        RemoveHandler webCliff.LostFocus, AddressOf setDisposeEvent
        RemoveHandler webDollar.GotFocus, AddressOf setDisposeEvent
        RemoveHandler webDollar.LostFocus, AddressOf setDisposeEvent
        webCliff.Stop()
        webDollar.Stop()

        Dim tmpWeb As SHDocVw.WebBrowser = webCliff.ActiveXInstance
        System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpWeb)
        webCliff.Dispose()

        tmpWeb = webDollar.ActiveXInstance
        System.Runtime.InteropServices.Marshal.ReleaseComObject(tmpWeb)
        webDollar.Dispose()

        webCliff = Nothing
        webDollar = Nothing
        GC.AddMemoryPressure(50000)
        GC.Collect()
        GC.WaitForPendingFinalizers()
        GC.Collect()
        GC.WaitForFullGCComplete()
        GC.Collect()
        GC.RemoveMemoryPressure(50000)
        webCliff = New WebBrowser()
        webDollar = New WebBrowser()
        webCliff.CausesValidation = False
        webCliff.Dock = DockStyle.Fill
        webDollar.CausesValidation = webCliff.CausesValidation
        webDollar.Dock = webCliff.Dock
        webDollar.ScriptErrorsSuppressed = True
        webDollar.Visible = True
        webCliff.Visible = True
        Me.splCliffDwellers.Panel2.Controls.Add(webCliff)
        Me.splDollars.Panel2.Controls.Add(webDollar)
        Me.splContainerMain.ResumeLayout()

        'vb.net for some reason automatically recreates these and the below is not needed
        'AddHandler webCliff.DocumentCompleted, AddressOf webCliff_DocumentCompleted
        'AddHandler webDollar.DocumentCompleted, AddressOf webDollar_DocumentCompleted
        'AddHandler webCliff.GotFocus, AddressOf setDisposeEvent
        'AddHandler webCliff.LostFocus, AddressOf setDisposeEvent
        'AddHandler webDollar.GotFocus, AddressOf setDisposeEvent
        'AddHandler webDollar.LostFocus, AddressOf setDisposeEvent

        webCliff.Navigate(webCliffNavigate)
        'webDollar.Navigate(webdollarNavigate)
        disposeOfBrowsers = Now.AddMinutes(20)
    End If
End Sub

我知道这不是最漂亮或完美的解决方案,但对我来说效果很好。——莱拉

于 2012-02-21T14:14:09.990 回答
1

我在为我公司使用的不同 Intranet 页面编写一个小型“幻灯片”应用程序时遇到了这个问题。我找到的最简单的解决方案是在一段固定的时间后重新启动应用程序,在我的情况下是一个小时。这个解决方案对我们很有效,因为用户与浏览器的交互并不多。

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

这当然假设您有一个数据持久性机制。

于 2014-03-14T15:55:11.327 回答
1

WebBrowser 控件中存在已知的内存泄漏。请参阅以下 Microsoft 知识库文章 - KB893629

于 2011-01-03T15:58:57.683 回答
1

2017 年快结束了,WebBrowser 中仍然存在这个烦人的错误。

我在这里尝试了所有解决方案,但没有一个对我有用。内存泄漏仍然存在......最奇怪的是,当我打电话时:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

IntPtr pHandle = GetCurrentProcess();
SetProcessWorkingSetSize(pHandle, -1, -1);

它确实减少了很多内存!但是当下一个 Navigate 指令被调用时,所有泄漏的内存都会回到范围内......就像(如果内存为 450mb.. 这个指令减少到大约 20mb 并且在再次调用 .Navigate(string) 之后它会跳转到 460mb并且内存泄漏继续......

我什至尝试过 Dispose(),在下一页之前导航到 about:blank,将 webbrowser 对象设置为 null 并创建一个新对象。所有这些尝试都属于内存泄漏......这真的很令人沮丧......还有其他解决方案吗?

于 2017-07-25T15:20:46.620 回答
1

我在表单上使用简单的 webbrowser 控件时遇到了这个问题。它导航到一个网页并停留在那里。根据任务管理器在 10 分钟内它从 120mb 吃掉了 2gb 的内存。

我的项目的一个简单解决方案是转到 Visual Studio 中的 webbrowser 控件属性并将“AllowNavigation”更改为 false。现在当我运行我的程序时,它保持在 120mb。我希望这可以帮助别人!

于 2018-06-18T21:21:40.457 回答
0

我在应用程序中使用 Web 控件,但由于我的应用程序仅导航到一页,我没有注意到您提到的问题。还有另一个 web 控件实际上是一个包装器,我不知道它是否有同样的问题。你可以在这里找到它。

于 2009-05-24T19:32:37.683 回答
0

作为替代方案,我遇到了同样的问题,而不是导航到新页面,我只是使用 system.oi.streamreader/writer 对象重写了相同的 html 页面并调用刷新。显然,这在浏览器内容在线提供的情况下不起作用,但它对我有用。

此外,我目前正在使用 8 个以上的浏览器控件同时处于活动状态,以通过我的 .net 应用程序中的 javascript 提供报告。当用户使一个浏览器处于活动状态时,其他浏览器指向的 html 将被清除并刷新浏览器。使用这两种方法运行的 8 个浏览器,我可以轻松地将我的应用程序保持在 Firefox 的内存使用情况下,只需打开 3 个选项卡。

于 2009-06-23T14:48:39.173 回答
0

我有同样的类似问题。我通过网络浏览器发送了超过 5000 个导航请求来抓取动态页面。在大约 50 个请求之后,我会用完内存,因为导航请求内存使用量在每次请求后都没有释放。我在导航后使用了 webBrowser.Dispose(),它解决了这个问题。它与IE7左右无关。我正在使用 IE 11 并且遇到了同样的问题。这是因为我没有处理导航对象。希望这可以帮助。

于 2015-06-17T19:30:09.113 回答
0

这意味着我的应用程序不再使用 500MB 内存,它永远保持在 60MB。诀窍是在处理它之后和调用 GC 之前将 webbrowser 设置为 null。

我的程序反复进入同一个站点,3个不同的页面,每次使用一次,没有抓取大量页面或任何东西。

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();
        }
于 2016-05-15T04:13:06.773 回答
0

只需使用“using”关键字声明 WebBrowser 控件。当调用 Navigate() 方法时,它将停止泄漏内存。我刚刚对其进行了测试,它对我来说效果很好。

using (var webBrowser = new System.Windows.Forms.WebBrowser())
{
    webBrowser.Navigate(url);
}
于 2018-04-27T21:48:24.347 回答
-1

页面加载后粘贴以下代码

System.Diagnostics.Process loProcess = System.Diagnostics.Process.GetCurrentProcess();
try
{
     loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet - 1);
     loProcess.MinWorkingSet = (IntPtr)((int)loProcess.MinWorkingSet - 1);
}
catch (System.Exception)
{
     loProcess.MaxWorkingSet = (IntPtr)((int)1413120);
     loProcess.MinWorkingSet = (IntPtr)((int)204800);
}
于 2012-06-22T11:10:25.077 回答