如何解决 .NET Webbrowser 控件中的内存泄漏问题?

发布于 2024-12-18 14:19:03 字数 2426 浏览 1 评论 0 原文

这是 .NET Webbrowser 控件的一个众所周知的老问题。

摘要:使用 .NET Web 浏览器控件导航到页面会增加永远不会释放的内存使用量。

重现内存泄漏:将 WebBrowser 控件添加到窗体中。使用它可以导航到您想要的任何页面。 about:blank 有效,在 Google Images 上向下滚动,直到您的使用量超过 100MB,然后浏览其他地方,发现几乎没有任何内存被释放,这是一个更戏剧性的演示。

我当前对应用程序的要求包括长时间运行它,显示有限的 IE7 浏览器窗口。用钩子、BHO 和组策略的一些混蛋设置来运行 IE7 本身也是不可取的,尽管目前这看起来像是后备措施。 将浏览器嵌入到 Windows 窗体应用程序中就是这样。 使用不同的浏览器基础对我来说不是一个可用的选择。需要IE7。

与此已知内存泄漏相关的以前的线程和文章:

经常提出的修复方法不起作用:

  • 转到不同的页面并不重要。 about:blank 会触发泄漏。它不需要页面具有 JavaScript 或任何其他额外的技术。
  • 使用不同版本的 Internet Explorer 并不重要。 7、8和9都表现出相同的症状,而且据我所知,所有版本的控件都有相同的内存泄漏。
  • 控件的 Dispose() 没有帮助。
  • 垃圾收集没有帮助。 (事实上​​,我对此所做的研究表明,泄漏发生在 Webbrowswer 控件包装的非托管 COM 代码中。)
  • 最小化进程可用内存并将其设置为 -1、-1(SetProcessWorkingSetSize() 或类似的值。)只会减少物理内存使用情况,对虚拟内存没有影响。
  • 调用 WebBrowser.Stop() 不是一个解决方案,并且会破坏使用除静态网页之外的任何内容的功能,而不仅仅是稍微最小化泄漏。
  • 在导航到另一个文档之前强制等待文档完全加载也没有帮助。
  • 在单独的 appDomain 中加载控件并不能解决问题。 (我自己没有这样做,但研究表明其他人在这条路线上没有成功。)
  • 使用不同的包装器(例如 csexwb2)并没有帮助,因为这也遇到了同样的问题。
  • 清除 Internet 临时文件缓存不会执行任何操作。问题出在活动内存中,而不是磁盘上。

当整个应用程序关闭并重新启动时,内存将被清除。

我愿意直接在 COM 或 Windows API 中编写自己的浏览器控件,如果这确实能解决问题的话。当然,我更喜欢不太复杂的修复;我宁愿避免下到较低的级别来做事情,因为我不想在浏览器支持的功能方面重新发明轮子。更不用说在自己设计的浏览器中复制 IE7 功能和非标准行为了。

帮助?

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:

  • Going to different pages does not matter. about:blank triggers the leak. It does not require a page to have javascript, or any other extra technology.
  • Using different versions of Internet Explorer does not matter. 7, 8, and 9 all exhibit the same symptoms, and as far as I've heard, all versions have the same memory leak in the control.
  • Dispose()ing of the control does not help.
  • Garbage Collecting does not help. (In fact, research I've done into this indicates the leak is in unmanaged COM code that the Webbrowswer control wraps.)
  • Minimizing and setting process available memory to -1, -1(SetProcessWorkingSetSize() or simimlar.) only reduces physical memory usage, has no impact on virtual memory.
  • Calling WebBrowser.Stop() is not a solution, and breaks functionality for using anything but static webpages, without doing more than just minimizing the leak slightly.
  • Forcing a wait for a document to load completely before Navigating to another also doesn't help.
  • Loading the control in a separate appDomain does not fix the issue. (I've not done this myself, but research shows others having no success with this route.)
  • Using a different wrapper such as csexwb2 does not help, as this also suffers from the same issue.
  • Clearing the Temporary Internet Files cache does nothing. The problem is in active memory, not on disk.

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

香草可樂 2024-12-25 14:19:03

此泄漏似乎是非托管内存中的泄漏,因此您在进程中执行的任何操作都不会回收该内存。从您的帖子中我可以看到您已经尝试了相当广泛的避免泄漏但没有成功。

如果可行的话,我会建议采用不同的方法。创建一个使用 Web 浏览器控件的单独应用程序,并从您的应用程序启动它。使用描述的方法 此处 将新创建的应用程序嵌入到您自己的现有应用程序中。使用 WCF 或 .NET 远程处理与该应用程序进行通信。不时地重新启动子进程,以防止其占用太多内存。

这当然是一个相当复杂的解决方案,并且重新启动过程可能看起来很丑陋。每次用户导航到另一个页面时,您可能会重新启动整个浏览器应用程序。

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.

情定在深秋 2024-12-25 14:19:03

我采用了 udione 的代码(它对我有用,谢谢!)并更改了两个小东西:

  1. IKeyboardInputSite 是一个公共接口,并且有方法 Unregister(),因此在收到 *_keyboardInputSinkChildren* 集合的引用后,我们不需要使用反射。

  2. 由于视图并不总是直接引用其窗口类(尤其是在 MVVM 中),我添加了一个方法 GetWindowElement(DependencyObject element),它通过遍历可视化树返回所需的引用。

谢谢,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;
}

谢谢大家!

I took the udione's code (it worked for me, thanks!) and changed two small things:

  1. 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.

  2. 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!

瀟灑尐姊 2024-12-25 14:19:03

有一种方法可以通过使用反射并删除 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);
        }
    }
} 

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);
        }
    }
} 
总以为 2024-12-25 14:19:03

使用 Win32workingSet / COM SHDocVw 接口等)解决了这个确切的内存不足问题.com/en-us/library/system.windows.controls.webbrowser.aspx" rel="nofollow">WPF WebBrowser 组件,我发现我们的问题是jqGrid 插件保留 IE ActiveXHost 中的非托管资源,并且在调用 WebBrowser.Dispose() 后不会释放它们。此问题通常是由运行不正常的 Javascript 造成的。奇怪的是,Javascript 在常规 IE 中运行良好 - 只是不能在 WebBrowser 控件中运行。我推测两个集成点之间的垃圾收集是不同的,因为 IE 永远无法真正关闭。

如果您正在编写源页面,我建议您删除所有 JS 组件,然后慢慢将它们添加回来。一旦您确定了有问题的 JS 插件(就像我们所做的) - 这应该很容易来解决这个问题。在我们的例子中,我们只是使用 < code>$("#jqgrid").jqGrid('GridDestroy') 正确删除它创建的事件及其关联的 DOM 元素。通过 WebBrowser.InvokeScript

如果您无法修改导航到的源页面,则必须向页面中注入一些 JS 来清理泄漏内存的 DOM 事件和元素。如果 Microsoft 找到解决方案,那就太好了,但现在我们只能探索需要清理的 JS 插件。

Having battled this exact Out of Memory issue from different directions (Win32 WorkingSet / COM SHDocVw interfaces, etc.) with the WPF WebBrowser component, I discovered the problem for us was jqGrid plugin holding onto unmanaged resources in the IE ActiveXHost and not releasing them after calling WebBrowser.Dispose(). This issue is often created by Javascript that is not behaving properly. The odd thing is that the Javascript works fine in regular IE - just not from within the WebBrowser control. I surmise that the garbage collection is different between the two integration points as IE can never really be closed.

One thing I'd suggest if you are authoring the source pages is to remove all JS components and slowly adding them back in. Once you identify the offending JS plugin (like we did) - it should be easy to remedy the problem. In our case we just used $("#jqgrid").jqGrid('GridDestroy') to properly remove the events and associated DOM elements it created. This took care of the issue for us by invoking this when the browser is closed via WebBrowser.InvokeScript.

If you don't have the ability to modify the source pages you navigate to - you would have to inject some JS into the page to clean up the DOM events and elements that are leaking memory. It would be nice if Microsoft found a resolution to this, but for now we are left probing for JS plugins that need cleansed.

掩耳倾听 2024-12-25 14:19:03

我认为这个问题已经很长时间没有答案了。这么多线程都有同样的问题,但没有结论性的答案。

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

步骤1:创建一个新表单(例如form2)并在其上添加一个Web浏览器控件。
步骤2:在您拥有网络浏览器控件的form1中,将其删除。
步骤3:现在,转到Form2并将此Web浏览器控件的访问修饰符设为公共,以便可以在Form1中访问它
步骤4:在form1中创建一个面板并创建form2的对象并将其添加到面板中。
Form2 frm = new Form2();
frm.TopLevel = false;
frm.Show();
panel1.Controls.Add(frm);
步骤5:定期调用以下代码
frm.Controls.Remove(frm.webBrowser1);
frm.Dispose();

就是这样。现在,当您运行它时,您可以看到 Web 浏览器控件已加载,并且它将定期进行处理,并且应用程序不再挂起。

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

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


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

I think this question has gone unanswered for a long time now. So many threads with the same question but not conclusive answer.

I have found a work around for this issue and wanted to share with you all who are still facing this issue.

step1: Create a new form say form2 and add a web-browser control on it.
step2: In the form1 where you have your webbrowser control, just remove it.
step3: Now, go to Form2 and make the access modifier for this webbrowser control to be public so that it can be accessed in Form1
step4: Create a panel in form1 and create object of form2 and add this into panel.
Form2 frm = new Form2 ();
frm.TopLevel = false;
frm.Show();
panel1.Controls.Add(frm);
step5: Call the below code at regular intervals
frm.Controls.Remove(frm.webBrowser1);
frm.Dispose();

Thats it. Now when you run it, you can see that webbrowser control loaded and it will get disposed at regular intervals and there is no more hanging of the application.

You can add the below code to make it more efficient.

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


        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
淑女气质 2024-12-25 14:19:03

以下解决方案对我有用:

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
        Me.DollarLogoutSub()
        If dollarLoggedIn Then
            Exit Sub
        End If

        '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()
        tmpWeb = Nothing

        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()

        '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)
        disposeOfBrowsers = Now.AddMinutes(20)
    End If
End Sub

祝你好运,
莱拉

The below solution worked for me:

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
        Me.DollarLogoutSub()
        If dollarLoggedIn Then
            Exit Sub
        End If

        '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()
        tmpWeb = Nothing

        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()

        '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)
        disposeOfBrowsers = Now.AddMinutes(20)
    End If
End Sub

Best of luck,
Layla

記憶穿過時間隧道 2024-12-25 14:19:03

相信它更多的是 .Net Framework 问题而不是 Web 浏览器控件。使用处理程序挂钩浏览器的导航事件,该处理程序将处理浏览器,然后导航到 about:blank 将是一个很好的解决方法。例如:

private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
  var browser = (WebBrowser) _stackPanel.Children[_stackPanel.Children.Count - 1];
  _stackPanel.Children.RemoveAt(_stackPanel.Children.Count-1);

  NavigatedEventHandler dispose = null;
  dispose = (o, args) =>
  {
    browser.Navigated -= dispose;
    browser.Dispose();
  };
  browser.Navigated += dispose;
  browser.Navigate(new Uri("about:blank"));
}

Believe its more of an .Net Framework issue rather than Web Browser control. Hooking to the navigated event of the browser with a handler which will dispose the browser and then navigating to about:blank will be a good workaround. For example:

private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
  var browser = (WebBrowser) _stackPanel.Children[_stackPanel.Children.Count - 1];
  _stackPanel.Children.RemoveAt(_stackPanel.Children.Count-1);

  NavigatedEventHandler dispose = null;
  dispose = (o, args) =>
  {
    browser.Navigated -= dispose;
    browser.Dispose();
  };
  browser.Navigated += dispose;
  browser.Navigate(new Uri("about:blank"));
}
ゃ懵逼小萝莉 2024-12-25 14:19:03

这很有效,特别是如果您的应用程序是 Winforms 应用程序!关闭浏览器后,句柄将立即释放。

使用代码:

using system.windows.forms;
[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();

// **Release handles invoking the above**
IntPtr pHandle = GetCurrentProcess();
SetProcessWorkingSetSize(pHandle, -1, -1);

快乐编码!

THIS WORKS, especially if yours is a Winforms application! Handles will be released immediately after closing the webbrowser.

USE CODE:

using system.windows.forms;
[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();

// **Release handles invoking the above**
IntPtr pHandle = GetCurrentProcess();
SetProcessWorkingSetSize(pHandle, -1, -1);

Happy coding!

时光瘦了 2024-12-25 14:19:03

尝试这个解决方案,我知道它并不理想。每次页面加载后粘贴代码

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

Try this solution, i know its not ideal. Paste the code after each page load

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文