Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 主详细信息视图中的RenderTargetBitmap GDI句柄泄漏_C#_Wpf_Bitmap_Gdi_Resource Leak - Fatal编程技术网

C# 主详细信息视图中的RenderTargetBitmap GDI句柄泄漏

C# 主详细信息视图中的RenderTargetBitmap GDI句柄泄漏,c#,wpf,bitmap,gdi,resource-leak,C#,Wpf,Bitmap,Gdi,Resource Leak,我有一个带有主详细信息视图的应用程序。当您从“主”列表中选择一个项目时,它会用一些图像(通过RenderTargetBitmap创建)填充“详细信息”区域 每次我从列表中选择不同的主项时,我的应用程序正在使用的GDI句柄数(如Process Explorer中所报告的)都会增加,最终会超过(有时会锁定)10000个正在使用的GDI句柄 我不知道如何解决这个问题,所以任何关于我做错了什么的建议(或者只是关于如何获得更多信息的建议)都将不胜感激 在一个名为“DoesThisLeak”的新WPF应用程

我有一个带有主详细信息视图的应用程序。当您从“主”列表中选择一个项目时,它会用一些图像(通过RenderTargetBitmap创建)填充“详细信息”区域

每次我从列表中选择不同的主项时,我的应用程序正在使用的GDI句柄数(如Process Explorer中所报告的)都会增加,最终会超过(有时会锁定)10000个正在使用的GDI句柄

我不知道如何解决这个问题,所以任何关于我做错了什么的建议(或者只是关于如何获得更多信息的建议)都将不胜感激

在一个名为“DoesThisLeak”的新WPF应用程序(.NET 4.0)中,我将我的应用程序简化为以下内容:

在MainWindow.xaml.cs中

public partial class MainWindow : Window
{
    public MainWindow()
    {
        ViewModel = new MasterViewModel();
        InitializeComponent();
    }

    public MasterViewModel ViewModel { get; set; }
}

public class MasterViewModel : INotifyPropertyChanged
{
    private MasterItem selectedMasterItem;

    public IEnumerable<MasterItem> MasterItems
    {
        get
        {
            for (int i = 0; i < 100; i++)
            {
                yield return new MasterItem(i);
            }
        }
    }

    public MasterItem SelectedMasterItem
    {
        get { return selectedMasterItem; }
        set
        {
            if (selectedMasterItem != value)
            {
                selectedMasterItem = value;

                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("SelectedMasterItem"));
                }
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class MasterItem
{
    private readonly int seed;

    public MasterItem(int seed)
    {
        this.seed = seed;
    }

    public IEnumerable<ImageSource> Images
    {
        get
        {
            GC.Collect(); // Make sure it's not the lack of collections causing the problem

            var random = new Random(seed);

            for (int i = 0; i < 150; i++)
            {
                yield return MakeImage(random);
            }
        }
    }

    private ImageSource MakeImage(Random random)
    {
        const int size = 180;
        var drawingVisual = new DrawingVisual();
        using (DrawingContext drawingContext = drawingVisual.RenderOpen())
        {
            drawingContext.DrawRectangle(Brushes.Red, null, new Rect(random.NextDouble() * size, random.NextDouble() * size, random.NextDouble() * size, random.NextDouble() * size));
        }

        var bitmap = new RenderTargetBitmap(size, size, 96, 96, PixelFormats.Pbgra32);
        bitmap.Render(drawingVisual);
        bitmap.Freeze();
        return bitmap;
    }
}
公共部分类主窗口:窗口
{
公共主窗口()
{
ViewModel=新的主视图模型();
初始化组件();
}
公共主视图模型视图模型{get;set;}
}
公共类主视图模型:INotifyPropertyChanged
{
私有主项选择主项;
公共IEnumerable主项目
{
得到
{
对于(int i=0;i<100;i++)
{
退回新主项目(i);
}
}
}
公共主项已选择主项
{
获取{返回selectedMasterItem;}
设置
{
如果(selectedMasterItem!=值)
{
selectedMasterItem=值;
if(PropertyChanged!=null)
{
PropertyChanged(这是新的PropertyChangedEventArgs(“SelectedMasterItem”);
}
}
}
}
公共事件属性更改事件处理程序属性更改;
}
公共类主项
{
私有只读int-seed;
公共主项目(整数种子)
{
这个种子=种子;
}
公共数字图像
{
得到
{
GC.Collect();//确保问题不是由于缺少集合造成的
var random=新随机(种子);
对于(int i=0;i<150;i++)
{
收益率返回MakeImage(随机);
}
}
}
私有ImageSource MakeImage(随机)
{
常数int size=180;
var drawingVisual=新的drawingVisual();
使用(DrawingContext DrawingContext=drawingVisual.RenderOpen())
{
drawingContext.DrawRectangle(brusks.Red,null,new Rect(random.NextDouble()*大小,random.NextDouble()*大小,random.NextDouble()*大小,random.NextDouble()*大小));
}
var bitmap=新的RenderTargetBitmap(大小,大小,96,96,PixelFormats.Pbgra32);
位图。渲染(drawingVisual);
bitmap.Freeze();
返回位图;
}
}
在MainWindow.xaml中

<Window x:Class="DoesThisLeak.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="900" Width="1100"
        x:Name="self">
  <Grid DataContext="{Binding ElementName=self, Path=ViewModel}">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="210"/>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <ListBox Grid.Column="0" ItemsSource="{Binding MasterItems}" SelectedItem="{Binding SelectedMasterItem}"/>

    <ItemsControl Grid.Column="1" ItemsSource="{Binding Path=SelectedMasterItem.Images}">
      <ItemsControl.ItemTemplate>
        <DataTemplate>
          <Image Source="{Binding}"/>
        </DataTemplate>
      </ItemsControl.ItemTemplate>
    </ItemsControl>
  </Grid>
</Window>

如果单击列表中的第一项,然后按住向下光标键,则可以重现该问题


从看!gcroot在WinDbg和SOS中,我找不到任何保持这些RenderTargetBitmap对象活动的东西,但如果我这样做了
!dumpheap-类型为System.Windows.Media.Imaging.RenderTargetBitmap
它仍然显示数千个尚未收集的文件。

尝试使用此处描述的解决方案:

更新:
另外,看看。

TL;医生:修好了。看看下面。请继续阅读我的探索之旅和我走过的所有错误的小巷

我已经用这个做了一些调查,我不认为它是这样泄漏的。如果我通过在图像中放置循环的任一侧来增强GC:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
您可以(慢慢地)沿着列表往下走,几秒钟后看到GDI句柄没有变化。 事实上,通过MemoryProfiler检查可以确认这一点——当从一个项目缓慢移动到另一个项目时,没有.net或GDI对象泄漏

在列表中快速向下移动确实会遇到麻烦-我看到进程内存超过1.5G,当GDI对象撞到墙上时,它会攀升到10000。此后每次调用MakeImage时,都会抛出一个COM错误,并且无法对进程执行任何有用的操作:

A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in PresentationCore.dll
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in PresentationCore.dll
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=SelectedMasterItem; DataItem='MasterViewModel' (HashCode=28657291); target element is 'ListBox' (Name=''); target property is 'SelectedItem' (type 'Object') COMException:'System.Runtime.InteropServices.COMException (0x88980003): Exception from HRESULT: 0x88980003
   at System.Windows.Media.Imaging.RenderTargetBitmap.FinalizeCreation()
我认为,这解释了为什么会看到这么多渲染目标位图。它还向我建议了一种缓解策略——假设它是一个框架/GDI错误。尝试将渲染代码(RenderImage)推送到允许重新启动基础COM组件的域中。最初,我会在它自己的单元(SetApartmentState(ApartmentState.STA))中尝试一个线程,如果不起作用,我会尝试一个AppDomain

但是,处理问题的根源会更容易,因为分配如此多的图像速度如此之快,因为即使我将其分配到9000个GDI句柄并等待一段时间,计数也会在下一次更改后下降到基线(在我看来,COM对象中有一些空闲处理,不需要几秒钟的时间,然后进行另一次更改以释放其所有句柄)

我不认为有任何简单的修复方法可以解决这个问题-我已经尝试添加睡眠来降低移动速度,甚至调用ComponentDispatched.RaiseIdle()-这两种方法都没有任何效果。如果我必须这样做,我将尝试以可重启的方式运行GDI处理(并处理可能发生的错误)或者改变用户界面

根据详细视图中的要求,最重要的是,右侧图像的可见性和大小,您可以利用ItemsControl的功能虚拟化列表(但您可能至少必须定义包含图像的高度和数量,以便它可以正确管理滚动条).我建议返回可观察的图像集合,而不是IEnumerable

事实上,在刚刚测试过这一点之后,此代码似乎解决了问题:

public ObservableCollection<ImageSource> Images
{
    get 
    {
        return new ObservableCollection<ImageSource>(ImageSources);
    }
}

IEnumerable<ImageSource> ImageSources
{
    get
    {
        var random = new Random(seed);

        for (int i = 0; i < 150; i++)
        {
            yield return MakeImage(random);
        }
    }
}
公共可观测集合