Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leak when using WPF WebBrowser control in multiple windows

I am working on a project that makes use of the WPF WebBrowser control (System.Windows.Controls.WebBrowser). The web browser element of the program is one of many activities the user can engage in, and is opened in a separate window. After the user navigates away from the browser, the window is closed, and a new window is created each time the user returns to the browser. We were noticing a significant memory leak / performance downgrade in our program (usage getting up to ~700mb from ~200 initial) upon continually using the browser. After failing to find any points of resource leaks within our own code, I decided to determine if the issue was with our own WebBrowser wrapper control, or the WPF control.

I created a new simple project consisting of only a MainWindow and a WebWindow. A button on the main window launches a browser directed at gmail (the site we noticed the biggest issue with of the few we examined). Upon closing this window, there is no freeing of resources (no reduction in VM size in Task Manager or Process Explorer) and the number of GDI objects the process has handles to does not decrease (the program starts with ~30, opening the browser takes it to ~140 and after closing the browser ~140 are still open). Opening another browser causes more handles, and more resources to be allocated. Furthermore, this problem is not fixed by specifically calling Dispose() on the WebBrowser control. The code is simple, and is as follows:

Main Window:

<Window x:Class="WebBrowserMemory.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
            <Button Click="Button_Click">Gmail</Button>
        </StackPanel>
    </Grid>
</Window>

Button_Click:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var win = new WebWindow();
    win.Show();
    win.Browser.Navigate("http://www.gmail.com");
}

Web Window:

<Window x:Class="WebBrowserMemory.WebWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WebWindow" Height="300" Width="300">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <WebBrowser Grid.Row="0" x:Name="_browser" />
    <Button Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Padding="10" Click="Button_Click">Close</Button>
</Grid>
</Window>

Relevant Code:

public WebBrowser Browser {
    get { return _browser; }
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    Close();
}

protected override void OnClosed(EventArgs e)
        {
            _browser.Dispose();
            base.OnClosed(e);
        }

Has anyone else encountered this issue using the WPF WebBrowser control?

[UPDATE: Updated post to indicate Dispose() call as per itowlson's answer - even calling Dispose() on the web browser control does not free the resources]

like image 354
jeffora Avatar asked Jan 15 '10 03:01

jeffora


1 Answers

Unlike most WPF controls, WebBrowser (because it inherits from HwndHost) is IDisposable and encapsulates unmanaged resources. The WPF Window, unlike the WinForms Form, does not automatically dispose its children (because native WPF controls do not encapsulate unmanaged resources and do not require disposal).

Add an OnClosed override to your window (or handle the Closed event), and call Dispose on the WebBrowser control.

like image 185
itowlson Avatar answered Sep 19 '22 16:09

itowlson