HwndHost leaks memory

156 Views Asked by At

we have an external video player to which i need to give a reference to a HwndHost, and then the external video player renders into it.

all works fine, but i cant find out why memory is leaked, as new HwndHosts are created.

here is my HwndHost implementation:

namespace WpfApp2
{
    using System;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Windows.Interop;

    [SuppressUnmanagedCodeSecurity]
    public class MediaFoundationVideoHost : HwndHost, IDisposable
    {
        internal const int WS_CHILD = 0x40000000;
        internal const int WS_VISIBLE = 0x10000000;
        internal const int LBS_NOTIFY = 0x00000001;
        internal const int SS_NOTIFY = 0x00000100;
        internal const int SS_BLACKFRAME = 0x00000007;
        internal const int SS_WHITERECT = 0x00000006;
        internal const int HostId = 0x00000002;
        internal const int LISTBOX_ID = 0x00000001;
        internal const int WS_VSCROLL = 0x00200000;
        internal const int GC_ALLGESTURES = 0x00000001;
        internal const int WS_BORDER = 0x00800000;
        internal const int WS_DISABLED = 0x8000000;

        private HandleRef handleRef;
        private IntPtr hwndHost;
        private IntPtr parentHandle = IntPtr.Zero;
        private readonly object lockObj = new object();

        [DllImport("user32.dll", SetLastError = true)]
        internal static extern IntPtr SetParent(IntPtr child, IntPtr parent);

        [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
        internal static extern IntPtr CreateWindowEx(int extendedWindowStyle,
                                                     string classname,
                                                     string windowname,
                                                     int style,
                                                     int x,
                                                     int y,
                                                     int width,
                                                     int height,
                                                     IntPtr hwndParent,
                                                     IntPtr menu,
                                                     IntPtr instance,
                                                     [MarshalAs(UnmanagedType.AsAny)] object param);

        [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
        internal static extern bool DestroyWindow(IntPtr hwnd);

        protected override void Dispose(bool disposing)
        {
            lock (this.lockObj)
            {
                handleRef = new HandleRef(null, IntPtr.Zero);
                parentHandle = IntPtr.Zero;
                hwndHost = IntPtr.Zero;
                
                base.Dispose(disposing);
              
            }
        }

        protected override HandleRef BuildWindowCore(HandleRef hwndParent)
        {
            this.hwndHost = IntPtr.Zero;
            this.parentHandle = hwndParent.Handle;

            int style = WS_CHILD | WS_VISIBLE | SS_BLACKFRAME | SS_WHITERECT;
            style |= SS_NOTIFY;


            this.hwndHost = CreateWindowEx(0,
                                           "static",
                                           string.Empty,
                                           style,
                                           0,
                                           0,
                                           (int)200,
                                           (int)200,
                                           hwndParent.Handle,
                                           (IntPtr)HostId,
                                           IntPtr.Zero,
                                           0);


            this.handleRef = new HandleRef(this, this.hwndHost);
            return this.handleRef;
        }

        protected override void DestroyWindowCore(HandleRef hwnd)
        {
            DestroyWindow(hwnd.Handle);

        }

      
        protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
        {
            handled = false;
            return IntPtr.Zero;

        }
    }
}

i create these 25 of HwndHosts in my MainWindow(wpf), and then i dispose them, hoping they get released and no memory leaks:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Input;


namespace WpfApp2
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }

        public event PropertyChangedEventHandler? PropertyChanged;

        public ObservableCollection<MediaFoundationVideoHost> Items { get; set; } = new ObservableCollection<MediaFoundationVideoHost>();
        private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
        {
            while (true)
            {

                Thread.Sleep(200);
                for (int i = 0; i < 25; i++)
                {
                    var videoHost = new MediaFoundationVideoHost();
                    Items.Add(videoHost);
                }

                foreach (var item in Items)
                {
                    item.Dispose();
                }
                Items.Clear();
            }
        }
    }
}

<Window x:Class="WpfApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid Background="Black" MouseDown="Grid_MouseDown">

        <ItemsControl ItemsSource="{Binding Items}"></ItemsControl>
    </Grid>
</Window>

my program goes from 50mb to 400mb in like 60 minutes, and i cant find anything useful while analyzing the dump of the process. BuildWindowCore is called, and DestroyWindowCore is called aswell.

thankies

0

There are 0 best solutions below