web-dev-qa-db-ja.com

IE WebBrowser Controlのメモリリークを修正する方法?

C#WinformアプリケーションにWebBrowserコントロールを埋め込もうとしています。これはとても簡単に聞こえます。しかし、Navigateメソッドを呼び出すたびに、WebBrowserコントロールが大量のメモリを消費することを発見しました。メモリが解放されることはありません。メモリ使用量は増加し、増加します…

ネット上の多くの人がまったく同じ問題を抱えていますが、満足のいく答えはまだ見つかりません。これは私がこれまでに見つけたこの問題についての最高の議論です:

IE WebBrowser Control のメモリリーク

問題を解決するために、IE8へのアップグレードを提案する人がいました。

ただし、ユーザーが最新のIE=バージョンがインストールされているかどうかに関係なく機能するソリューションが必要です。ユーザー環境を制御できません。

WebBrowserコントロールによって取得されたメモリを解放する方法を誰かが知っていますか?回避策はありますか? WebBrowserコントロールに代わるものはありますか?

更新:さらにいくつかのテストを行いました。職場でWindowsを実行していますXPおよびIE6。メモリはそこで増加していません。navigateメソッドを呼び出すとメモリが増加しますが、しばらくすると解放されます。自宅でVistaを実行してアップグレードしていますIE8への問題です。ここでも問題は発生していません。問題はIE7に固有のようです。したがって、質問は、「IE WebBrowser Control IE7がインストールされている場合」というメッセージが表示されます。この問題がIE7に固有のものであることを誰かが確認できますか?

29
Rainer Falle

私のアプリは、ナビゲート時に常にメモリを消費し、解放しなくなりました。ここで私のために解決策を見つけます: 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を正しく行うため、私はそれを見つけることができました;)

ご挨拶

編集:これが問題をすばやく解決するのに役立つ場合-良いです。しかし、あなたはあなたのアプリケーション設計、もしあればあなたが使用するパターンを上書きしなければなりません、あなたがそれよりずっと長く構築するならそれをリファクタリングしてください...

11
womd

結果を再現するためのWebブラウザーコントロールを備えたシンプルなアプリを作成しました。私が見つけたのは、はい。ページに移動するたびに、使用されているメモリが大幅に増加します。ただし、これはメモリリークではありません。ナビゲートを続けると、しばらくするとメモリが大幅に減少し、ガベージコレクタが処理を行ったことを示します。それを証明するために、Navigateを呼び出すたびにガベージコレクターを強制的に収集しました。使用したメモリ全体は、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クラスのメタデータで見つけ(Visual StuioでF12を押す)、アンマネージリソースを解放できます。(NOT Dispose())

コード ここ

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

ただし、WebBrowser.Dispose(bool)を呼び出そうとすると、コンパイラエラー CS154 が表示されます。

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

このコードは、メモリリークを防ぐことができます。

要約すると、必要なクラスは1つだけです。

    public class WebBrowserD : WebBrowser
    {
        internal void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }
    }
3
Tony Jang

Tridentの代わりにGecko(Firefoxが使用するエンジン)を使用する alternative control があり、MSHTMLインターフェイスと非常にうまく機能します。

ページがGeckoでレンダリングされ、ブラウザーの設定、プラグイン、セキュリティ、その他のカスタマイズ可能な機能を完全に制御できます。

欠点は、アプリにGeckoを同梱する必要があることです。私は最後にFirefox 2と同等のものを使用しましたが、サイズは約8MBでした。

IEとFirefoxのレンダリングを並べて比較したアプリをかなり前にリリースしました。どちらもCSSを編集すると更新されます。メモリに関する問題には遭遇していません。 Webブラウザーコントロールですが、Geckoコントロールの操作は非常に簡単でした。netWebBrowserコントロールと同じマネージラッパークラスはありませんが、それを回避するのは簡単です。

3
Matt Brindley

フォーム上の単純なWebブラウザーコントロールでこの問題が発生しました。 1つのWebページに移動し、そこにとどまりました。タスクマネージャによると、10分以内に120MBから2GBのメモリを消費しました。

私のプロジェクトの簡単な解決策は、Visual StudioのWebブラウザーコントロールのプロパティに移動し、 'AllowNavigation'をfalseに変更することでした。プログラムを実行すると、120MBのままです。これが誰かに役立つことを願っています!

1
tonyk123

GoBack()メソッドを使用できるため、Navigate()メソッドはアクセスしたすべてのページをメモリに保持しているようです。実際には「メモリリーク」はありません。私のプログラムは同じURLに繰り返しアクセスします。 「メモリリーク」の問題は、Navigate()メソッドの代わりにRefresh()メソッドを使用し、その後に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

会社で使用しているさまざまなイントラネットページ用の小さな「スライドショー」アプリケーションを作成しているときに、この問題に遭遇しました。私が見つけた最も簡単な解決策は、一定の時間、私の場合は1時間後にアプリケーションを再起動することでした。このソリューションは、ユーザーがブラウザをあまり操作しなかったため、うまく機能しました。

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と言う新しいフォームを作成し、それにWebブラウザコントロールを追加します。 step2:webbrowserコントロールがあるform1で、それを削除します。 step3:次に、Form2に移動し、このWebブラウザーコントロールのアクセス修飾子をパブリックにして、Form1でアクセスできるようにします。step4:form1にパネルを作成し、form2のオブジェクトを作成して、これをパネルに追加します。 Form2 frm =新しい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();
0
nh53019

アプリケーションでWebコントロールを使用していますが、アプリケーションが1つのページに移動するだけなので、あなたが言及した問題に気づきませんでした。実際にラッパーである別のWebコントロールがあり、同じ問題があるかどうかはわかりません。あなたはそれを見つけることができます ここ

0
Beatles1692

これは私にとってはうまくいきました、100%がメモリをクリアするかどうかはわかりませんが、キャッシュされたページは保持されているようですが、RAMの使用量が500MBに達しておらず、60MBのままです。

私のプログラムは同じサイトの3つの異なるページに繰り返しアクセスします。使用するのは1回だけで、多数のページなどはクロールしません。

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

私は同じ問題に直面していました。代わりに、新しいページに移動するのではなく、system.oi.streamreader/writerオブジェクトを使用して同じhtmlページを書き直し、refreshを呼び出しました。ブラウザーのコンテンツがオンラインで提供されている状況では、これは機能しませんが、私にとってはうまくいきます。

また、私は現在、8つ以上のブラウザーコントロールをすべてアクティブにして、.netアプリ内のJavaScriptを通じてレポートを提供しています。ユーザーが1つのブラウザーをアクティブにすると、他のブラウザーが指しているHTMLがクリアされ、ブラウザーが更新されます。これら2つの方法で8つのブラウザーを実行すると、3つのタブを開くだけで、Firefoxのメモリ使用量の下でアプリを簡単に維持できます。

0
JNevill

2017年の終わりが近づいていますが、それでもこの迷惑なバグはWebBrowserに存在します。

ここではすべての解決策を試しましたが、どれもうまくいきませんでした。メモリリークはまだ持続します...私が呼び出すとき、最も奇妙なことはそれです:

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

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

それは実際にメモリをたくさん減らします!しかし、次のNavigate命令が呼び出されると、リークされたすべてのメモリがスコープに戻ります...(メモリが450MBの場合など)。そしてメモリリークが続きます...

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

"using"キーワードを使用してWebBrowserコントロールを宣言するだけです。 Navigate()メソッドを呼び出すと、メモリリークを停止します。私はそれをテストしました、そしてそれは私のためにうまくいきました。

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

同じような問題がありました。動的なページをこするために、Webブラウザーを介して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