【问题标题】:UserControl Leaks when Called from Windows Service to Render Image从 Windows 服务调用渲染图像时 UserControl 泄漏
【发布时间】:2021-01-02 23:00:03
【问题描述】:

我有一个 C# Windows 服务,它加载一个自定义 WPF UserControl 来呈现稍后通过电子邮件发送给用户的图像。这很好用,但是,当从 .Net Memory Profiler 查看内存使用情况时,我看到控件的每个实例化实例都被泄露了。在查看泄漏时,它似乎是焦点处理程序的委托。自定义用户控件不订阅任何事件处理程序。我需要一种以编程方式卸载控件的方法。代码如下:

        public string GetPercentGauge(long percent, long? max = 100)
        {
            var t = new Thread(() =>
            {
                GetPercentGaugeThread(fileName, percent, max);
            });
            t.Name = "UIThread";
            t.SetApartmentState(ApartmentState.STA); 
            t.IsBackground = true; 
            t.Start();

            while (t.IsAlive)
                Thread.Sleep(30);

            return fileName;
        }
     
        private void GetPercentGaugeThread(string fileName, long percent, long? max = 100)
        {
            try
            {                
                var ctrl = new RowThresholdChartControl()
                {
                    DataContext = new BaseColumnChartItemViewModel(percent, max.Value)
                };                
                ctrl.DrawToPng(fileName, 200, 20);
                ctrl.Dispose();
            }
            catch (Exception)
            {
            }
        }

【问题讨论】:

  • “自定义用户控件不订阅任何事件处理程序”它必须这样做,否则它不会泄漏。检查委托的所有者
  • 另外,设置为 null 是没有意义的,请使用 using 语句。那个空的 catch 块太难闻了
  • 一个 WPF 控件需要在带有 UI 的桌面上运行。 Windows 服务在没有 UI 和桌面的情况下运行。我的猜测是,你的计划很有可能注定要失败。
  • 我已经把它缩小到调用 Arrange(new Rect(new Size(width, height)));它允许控件渲染图像。这是 Windows 窗体一直具有的功能。从服务渲染图形是正常行为。这就是所有服务器监控软件包的运行方式。我发现很难相信 MS 会在 WPF over .Net Forms 上迈出一大步。我试着寻找父母,看看是否有帮助……没有父母。我所要做的就是备注我对 Arrange 的调用,然后控件就完成了……但是没有图像。
  • @CamiloTerevinto 非常感谢您对 null 的评论。我正在尽一切努力尝试让它发布。之后我什至尝试中止线程。我找到了修复程序,很快就会发布。

标签: c# .net memory-leaks windows-services


【解决方案1】:

我自己找到了一个修复方法,尽管它对我来说似乎是一个 hack。如上所述,内存分析器指向一个有意义的键盘处理程序,但 WPF API 不包含清除违规委托的方法。我打算开始使用本机 C API 将其添加到 Window 的路径,然后向它发送 WM_CLOSE 希望它应该关闭它,但这听起来像一个讨厌的老鼠洞。我不知道这是否可以做到。我的下一个想法是启动一个 EXE 来处理文件。我也不喜欢这个主意,因为这个函数可以在几秒钟内被调用 1000 次。

修复: 完成控件的呈现后,我只需将控件的大小设置为 0。我认为底层代码足够聪明,可以删除控件,因为它不再想要呈现它或处理 TAB 按键事件。我还期望删除以清理有问题的代表,所以它是。这是渲染调用,现在允许完成上述代码中的“ctrl”变量。

        public void DrawToPng(string fileName, int width, int height)
        {
            Initialize();

            Measure(new Size(width, height));
            Arrange(new Rect(new Size(width, height)));            

            var bitmap = new RenderTargetBitmap((int)ActualWidth, (int)ActualHeight, 96, 96, PixelFormats.Pbgra32);
            bitmap.Render(this);

            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(bitmap));

            using (var file = File.OpenWrite(fileName))
            {
                encoder.Save(file);
            }
            
            Measure(new Size(0, 0));
            InvalidateArrange();
            UpdateLayout();
        }

【讨论】:

    猜你喜欢
    • 2019-08-13
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-03
    • 2012-06-04
    相关资源
    最近更新 更多