如何在Windows窗体中创建GDI泄漏!

时间:2009-05-18 15:02:11

标签: winforms gdi+ gdi

我正在调查大型应用程序中的GDI资源泄漏。为了进一步了解这些问题是如何发生的,我创建了一个非常小的应用程序,我故意将其“泄漏”。这是一个简单的用户控件,它应该导致创建100个Pen对象:

public partial class TestControl : UserControl
{
    private List pens = new List();

    public TestControl()
    {
        InitializeComponent();

        for (int i = 0; i < 100; i++)
        {
            pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))));
        }

        this.Paint += new PaintEventHandler(TestControl_Paint);
    }

    void TestControl_Paint(object sender, PaintEventArgs e)
    {
        for (int i = 0; i < 100; i++)
        {
            e.Graphics.DrawLine(pens[i], 0, i, Width, i);
        }
    }
}

然而,当我创建一个我的对象的实例并将其添加到一个表单时,用TaskManager查看我的应用程序我现在看到~37个GDI对象。如果我重复向表单添加新的TestObject用户控件,我只能看到~37个GDI对象。

这里发生了什么!我认为System.Drawing.Pen的构造函数将使用GDI + API创建一个新的Pen,从而使用一个新的GDI对象。

我一定要疯了。如果我不能编写一个创建GDI对象的简单测试应用程序,我该如何创建一个泄漏它们的应用程序!

非常感谢任何帮助。

最诚挚的问候,科林E.

6 个答案:

答案 0 :(得分:2)

GDI +是否使用GDI句柄?我不确定,虽然我在某处读到有一个依赖于裸GDI的.NET System.Drawing实现。

但是,也许您可​​以尝试使用像AQTime这样的分析器来查找泄漏。

您如何确定您的大型应用正在泄漏GDI句柄?任务管理器中的计数是大吗?如果是这样,你总是使用GDI +,还是GDI?如果您多次创建控件,您的测试应用程序GDI会处理计数增加吗?

答案 1 :(得分:2)

您的样本中并没有真正泄漏资源。从Load事件中删除此代码:

for (int i = 0; i < 100; i++)
    {
        pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))));
    }

您的Paint事件处理程序应如下所示:

void TestControl_Paint(object sender, PaintEventArgs e)
{
    for (int i = 0; i < 100; i++)
    {
        e.Graphics.DrawLine(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))), 0, i, Width, i);
    }
}

现在你将在每次油漆电话中泄漏。开始最小化/恢复你的表格,看看GDI对象天空火箭...

希望这有帮助。

答案 2 :(得分:1)

如果你想从.NET泄漏GDI对象,那么只需创建一个GDI对象而不释放它:

[DllImport("gdi32.dll", EntryPoint="CreatePen", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
private static extern IntPtr CreatePen(int fnStyle, int nWidth, int crColor);

CreatePen(0, 0, 0); //(PS_SOLID, 0=1px wide, 0=black)

Blingo blango,你正在泄漏GDI笔。

  

我不知道为什么你想要创建GDI泄漏。但是你的问题是如何从WinForm创建GDI泄漏 - 所以就是这样。

答案 3 :(得分:0)

我认为以下博客可能已经回答了这个问题:

Using GDI Objects the Right Way

未明确处置的GDI对象应由其finalize隐式处理。 (鲍勃鲍威尔也在GDI+ FAQ

中提到了这个问题

但我怀疑CLR垃圾收集器是否可以如此快速地删除GDI资源,甚至无法从TaskManager中看到内存使用情况的变化。也许当前的GDI +实现不使用GDI。

我尝试了以下代码来生成更多GDI对象。但我仍然看不到GDI句柄数量的任何变化。

void Form1_Paint(object sender, PaintEventArgs e) 
{
    Random r = new Random();
    while (true)
    {
        for (int i = 0; i < 100; i++)
        {
            e.Graphics.DrawLine(
            new Pen(new SolidBrush(Color.FromArgb(r.Next()))), 0, i, Width, i);
        }
    }
}

答案 4 :(得分:0)

我认为编译器只使用一个句柄。

如果我在delphi中创建了很多字体,我只需要记忆 但如果我使用WinAPI CreateFont(),我会使用GDI对象。

答案 5 :(得分:0)

在表单上创建两个按钮。在每个按钮内,添加以下代码。在一个按钮中,注释掉Dispose方法。

    Form _test = null;
    for (int i = 0; i < 20; i++)
    {
        _test = new Form();
        _test.Visible = false;
        _test.Show();
        _test.Hide();
        _test.Dispose();
    }

Dispose注释掉的按钮显示泄漏。另一个显示Dispose导致User和GDI句柄保持不变。

This可能是我发现的解释它的最佳页面。