4

I am investigating a GDI resource leak in a large application. In order to further my understanding of how these problems occur, I have created a very small application which I have deliberately made 'leaky'. Here is a simple user control which should result in the creation of 100 Pen objects:

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);
        }
    }
}

However, when I create an instance of my object and add it to a form, looking at my application with TaskManager I currently see ~37 GDI objects. If I repeatedly add new TestObject user controls to my form, I still only see ~37 GDI objects.

What is going on here! I thought that the constructor for System.Drawing.Pen would use the GDI+ API to create a new Pen, thus using a new GDI object.

I must be going nuts here. If I cannot write a simple test application that creates GDI objects, how can I create one which leaks them!

Any help would be much appreciated.

Best Regards, Colin E.

4

6 回答 6

2

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

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

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

于 2009-05-18T15:14:27.327 回答
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 对象天空火箭...

希望这可以帮助。

于 2009-05-18T18:15:23.013 回答
2

如果你想从 .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 blago,你在泄漏 GDI 笔。

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

于 2011-07-05T01:59:51.070 回答
0

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

如果我在 delphi 中创建很多字体,我只会占用内存
,但如果我使用 WinAPI CreateFont(),我会占用 GDI 对象。

于 2012-08-22T07:08:33.627 回答
0

我想下面的博客可能已经回答了这个问题:

正确使用 GDI 对象

未显式释放的 GDI 对象应通过它们的终结隐式释放。(Bob Powell 在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);
        }
    }
}
于 2011-05-30T08:05:10.347 回答
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 句柄保持不变。

可能是我找到的最好的解释它的页面。

于 2014-10-14T18:14:12.920 回答