it-swarm.com.ru

Как исправить утечку памяти в IE Управление веб-браузером?

Я пытаюсь встроить элемент управления WebBrowser в приложение C # Winform. Это звучит достаточно просто. Однако я обнаружил, что элемент управления WebBrowser поглощает много памяти каждый раз, когда я вызываю метод Navigate. Память никогда не освобождается. Использование памяти растет и растет ...

У многих людей в сети точно такая же проблема, но я пока не нашел удовлетворительного ответа. Это лучшее обсуждение этой проблемы, которое я нашел до сих пор:

Утечка памяти в IE WebBrowser Control

Один человек предложил обновить до IE8, чтобы исправить проблему.

Однако мне нужно решение, которое работает независимо от того, установлена ​​ли у пользователя последняя версия IE или нет. У меня нет контроля над пользовательской средой.

Кто-нибудь знает, как освободить память, занятую элементом управления WebBrowser? Есть ли обходные пути? Существуют ли альтернативы элементу управления WebBrowser?

Update: Я только что сделал еще несколько тестов. На работе я использую Windows XP и IE6. Память там не растет. Память увеличивается при вызове метода навигации, но освобождается через некоторое время. Дома я использую Vista и обновился до IE8. Здесь я тоже не вижу проблемы больше. Похоже, что проблема специфична для IE7. Поэтому вопрос следует перефразировать следующим образом: «Как устранить утечку памяти в IE элементе управления WebBrowser, когда установлен IE7». Кто-нибудь может подтвердить, что эта проблема специфична для IE7?

29
Rainer Falle

мое приложение также постоянно потребляло память при навигации и больше не высвобождало . я нашел решение для меня здесь: http://social.msdn.Microsoft.com/Forums/en-US/ieextensiondevelopment/резьба/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/ нить/88c21427-e765-46e8-833d-6021ef79e0c8 для публикации решения.

и http://ict-engineer.blogspot.com/2010/10/net-webbrowser-control-memory-leak.html для SEO, и я смог найти его ;)

привет

Правка: если это поможет вам быстро решить проблему - хорошо. но вы должны переоценить дизайн своего приложения, шаблон, который вы используете, если таковой имеется, рефакторировать объект, если вы будете использовать его гораздо дольше ...

11
womd

Я только что создал простое приложение с контролем веб-браузера, чтобы попытаться повторить ваши результаты. Я обнаружил, что да, каждый раз, когда вы переходите на страницу, используемая память значительно увеличивается. ОДНАКО, это НЕ утечка памяти, потому что если вы продолжите навигацию, вы увидите, что через некоторое время память значительно падает, указывая на то, что сборщик мусора сделал свое дело. Чтобы доказать это, я заставлял сборщик мусора собирать данные после каждого вызова Navigate, и общий объем используемой памяти оставался на прежнем уровне после каждого вызова навигации. 

Таким образом, несмотря на то, что он НАБИРАЕТ память каждый раз, когда вы «переходите», это НЕ утечка памяти, и вы освободите память. Если он набирает слишком быстро, просто вызовите GC.Collect ();

8
BFree

ОСНОВНОЙ IDEA,

"Убей себя и возродись". 

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

        ...
    }

или вы можете использовать временный файл !!!

6
Yong-nam Kim

Согласно MSDN , элемент управления System.Windows.Forms.WebBrowser является управляемой оболочкой для элемента управления ActiveX WebBrowser и использует любую версию элемента управления, установленную на компьютере пользователя.

Вы можете найти метод Dispose (bool) в метаданных класса WebBrowser (нажмите F12 в Visual Stuio) для освобождения неуправляемого ресурса. (НЕ 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).
IT IS ОЧЕНЬ ПРОСТО! но это не очень хороший способ.

Пример кода здесь (нужно 3 кнопки и 1 TextBox)

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);
        }
    }
3
Tony Jang

Существует альтернативный элемент управления который использует Gecko (движок Firefox использует) вместо Trident и очень хорошо работает с интерфейсами MSHTML.

Ваши страницы будут отображаться в Gecko, и вы будете иметь полный контроль над настройками, плагинами, безопасностью и любыми другими настраиваемыми функциями браузера.

Недостатком является то, что вам нужно будет поставлять Gecko вместе с вашим приложением, в последний раз я использовал эквивалент Firefox 2, и он занимал около 8 МБ. 

Некоторое время назад я выпустил приложение, в котором сравнивались IE и рендеринг Firefox друг с другом, и оба обновлялись по мере того, как вы редактировали CSS. У меня не было проблем с памятью, которые у вас были с веб-браузером, но я нашел, что с Gecko очень легко работать. Он не имеет того же класса управляемых оболочек, что и элемент управления .net WebBrowser, но его достаточно легко обойти.

3
Matthew Brindley

Кажется, что метод 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
1
ghostyguo

Я столкнулся с этой проблемой при написании небольшого приложения для создания слайд-шоу для разных страниц интрасети, используемых моей компанией. Самым простым решением, которое я нашел, было перезапуск приложения через некоторый фиксированный промежуток времени, в моем случае - час. Это решение хорошо сработало для нас, потому что не было большого взаимодействия пользователя с браузером.

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

Это предполагает, конечно, что у вас есть механизм сохранения данных.

1
D-Jones

В элементе управления WebBrowser есть известная утечка памяти. См. Следующую статью Microsoft KB - KB893629 .

1
Dave Black

Я думаю, что этот вопрос давно остался без ответа. Так много тем с одинаковым вопросом, но не окончательным ответом.

Я нашел способ обойти эту проблему и хотел бы поделиться со всеми вами, кто все еще сталкивается с этой проблемой.

шаг 1. Создайте новую форму, скажем, form2, и добавьте в нее элемент управления веб-браузера. Шаг 2: В форме 1, где у вас есть элемент управления веб-браузера, просто удалите его. Шаг 3: Теперь перейдите к Form2 и сделайте модификатор доступа для этого элемента управления веб-браузера общедоступным, чтобы к нему можно было получить доступ в Form1. Шаг 4. Создайте панель в form1 и создайте объект form2 и добавьте его в панель. Form2 frm = новая Form2 (); frm.TopLevel = false; frm.Show (); panel1.Controls.Add (FRM); step5: вызывать приведенный ниже код с регулярными интервалами frm.Controls.Remove (frm.webBrowser1); frm.Dispose ();

Это оно. Теперь, когда вы запустите его, вы увидите, что элемент управления веб-браузера загружен, и он будет регулярно удаляться, и приложение больше не будет зависать.

Вы можете добавить приведенный ниже код, чтобы сделать его более эффективным.

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


    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
0
nh53019

Я столкнулся с этой проблемой при простом управлении веб-браузером в форме. Он перешел на одну веб-страницу и остался там. По словам диспетчера задач, в течение 10 минут он израсходовал 2 ГБ памяти из 120 МБ.

Простым решением для моего проекта было перейти к свойствам элементов управления веб-браузера в Visual Studio и изменить значение параметра «AllowNavigation» на false. Теперь, когда я запускаю свою программу, она остается на 120 МБ. Я надеюсь, что это помогает кому-то!

0
tonyk123

Я использую веб-элемент управления в приложении, но поскольку мое приложение перемещается только на одну страницу, я не заметил проблему, о которой вы упомянули .. Есть еще один веб-элемент управления, который на самом деле является оберткой, и я не знаю, имеет ли он та же проблема или нет . Вы можете найти его здесь .

0
Beatles1692

Это сработало для меня, не уверен, что если 100% очищает память, похоже, он по-прежнему сохраняет кэшированные страницы, но больше не использует 500 МБ оперативной памяти, он остается на 60 МБ.

моя программа неоднократно переходит на один и тот же сайт, 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)
        {
            FireEvent(eventBuffer);
            GC.Collect();
            thread.Abort();
        }
0
Captain Awesome

Я столкнулся с той же проблемой, в качестве альтернативы, вместо перехода на новую страницу, я просто переписал ту же HTML-страницу, используя объект system.oi.streamreader/writer и вызвав обновление. Очевидно, что это не сработает в ситуации, когда контент для браузера подается онлайн, но для меня это помогает.

Кроме того, в настоящее время я использую более 8 элементов управления браузером, которые активны одновременно, для предоставления отчетов через javascript в моем приложении .net. Когда пользователь активирует один браузер, html, на который указывают другие браузеры, очищается, а браузеры обновляются. Имея 8 браузеров, работающих с этими двумя методами, я могу легко держать свое приложение под управлением памяти Firefox, открыв всего 3 вкладки.

0
JNevill

Это почти конец 2017 года, и все же эта досадная ошибка присутствует в WebBrowser.

Я перепробовал все решения здесь, и ни одно из них не помогло мне. Утечка памяти все еще сохраняется ... Самое странное, что когда я звоню:

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

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

Это действительно уменьшает память много! но когда вызывается следующая инструкция Navigate, вся просочившаяся память возвращается в область видимости .... например (если объем памяти составляет 450 МБ .. эта инструкция уменьшается до приблизительно 20 МБ и сразу после повторного вызова .Navigate (string) она переходит к 460 МБ и утечка памяти продолжается ...

Я даже пытался использовать Dispose (), переходя к about: blank перед следующей страницей, устанавливая объект webbrowser в null и создавая новый. Все эти попытки приводят к утечке памяти ... это действительно расстраивает ... Какие-нибудь другие решения?

0
rickrvo

Вставьте следующий код после загрузки страницы

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);
}
0
Stefan Kruger

Просто объявите элемент управления WebBrowser, используя ключевое слово «using». Это прекратит утечку памяти при вызове метода Navigate () больше. Я только что проверил это, и он работал хорошо для меня.

using (var webBrowser = new System.Windows.Forms.WebBrowser())
{
    webBrowser.Navigate(url);
}
0
Ashraf Tirawi

У меня были такие же похожие проблемы. Я отправлял более 5000 запросов навигации через веб-браузер для очистки динамических страниц. Приблизительно после 50 запросов мне не хватило бы памяти, так как использование памяти для навигационных запросов не освобождается после каждого запроса. Я использовал webBrowser.Dispose () после навигации, и это решило проблему. Это не связано с IE7 или около того. Я использую IE 11 и получил ту же проблему. Это потому, что я не выбрасывал навигационные объекты. Надеюсь, это поможет.

0
Try_Learning

Я просмотрел весь интернет и не смог найти ответ на эту проблему. Я исправил это, используя ниже:

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

Я знаю, что это не самое красивое или идеальное решение, но оно очень хорошо сработало для меня ...

0
Layla