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

Winforms 如何在Windows窗体中创建GDI泄漏!,winforms,gdi+,gdi,Winforms,Gdi+,Gdi,我正在调查一个大型应用程序中的GDI资源泄漏。为了进一步了解这些问题是如何发生的,我创建了一个非常小的应用程序,并故意将其设置为“泄漏”。下面是一个简单的用户控件,它将创建100个笔对象: public partial class TestControl : UserControl { private List pens = new List(); public TestControl() { InitializeComponent();

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

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); } } } 公共部分类TestControl:UserControl { 私有列表笔=新列表(); 公共测试控制() { 初始化组件(); 对于(int i=0;i<100;i++) { 添加(新画笔(新的SolidBrush(Color.FromArgb(255,i*2,i*2,255-i*2))); } this.Paint+=新的PaintEventHandler(TestControl\u Paint); } void TestControl_Paint(对象发送器,PaintEventArgs e) { 对于(int i=0;i<100;i++) { e、 图形.绘图线(笔[i],0,i,宽度,i); } } } 但是,当我创建对象的实例并将其添加到表单中时,使用TaskManager查看我的应用程序,我目前看到约37个GDI对象。如果我反复向表单中添加新的TestObject用户控件,我仍然只能看到约37个GDI对象

这是怎么回事!我认为System.Drawing.Pen的构造函数将使用GDI+API创建一个新的笔,从而使用一个新的GDI对象

我一定是疯了。如果我不能编写一个简单的测试应用程序来创建GDI对象,我怎么能创建一个泄漏它们的应用程序呢

任何帮助都将不胜感激


向您问好,科林E.

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

然而,也许你可以尝试用类似AQTime的探查器来查找你的漏洞


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

您的示例中并没有真正泄漏资源。从加载事件中删除此代码:

for (int i = 0; i < 100; i++)
    {
        pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))));
    }
for(int i=0;i<100;i++)
{
添加(新画笔(新的SolidBrush(Color.FromArgb(255,i*2,i*2,255-i*2)));
}
绘制事件处理程序应如下所示:

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);
    }
}
void TestControl\u Paint(对象发送器,PaintEventArgs e)
{
对于(int i=0;i<100;i++)
{
e、 绘图线(新画笔(新的SolidBrush(颜色.FromArgb(255,i*2,i*2,255-i*2))),0,i,宽度,i);
}
}
现在你每次油漆都会漏。开始最小化/恢复表单,并查看GDI对象天空火箭


希望这有帮助。

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

没有显式处理的GDI对象应该由它们的finalize隐式处理。 (鲍伯·鲍威尔在文章中也提到了这个问题)

但我怀疑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);
        }
    }
}
void Form1\u Paint(对象发送器,PaintEventArgs e)
{
随机r=新随机();
while(true)
{
对于(int i=0;i<100;i++)
{
e、 拉丝(
新画笔(新的SolidBrush(Color.FromArgb(r.Next()))),0,i,Width,i);
}
}
}

如果要从.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泄漏-就是这样


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

如果我在delphi中创建了很多字体,我只需要占用内存

但是如果我使用WinAPI
CreateFont()
我会使用GDI对象

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

    Form _test = null;
    for (int i = 0; i < 20; i++)
    {
        _test = new Form();
        _test.Visible = false;
        _test.Show();
        _test.Hide();
        _test.Dispose();
    }
Form\u test=null;
对于(int i=0;i<20;i++)
{
_测试=新形式();
_测试可见=错误;
_test.Show();
_test.Hide();
_test.Dispose();
}
带有Dispose注释的按钮显示泄漏。另一个显示Dispose导致用户和GDI句柄保持不变


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

我同意这是一个奇怪的问题。您是否验证了如果循环次数少于37次,GDI对象的数量是否会从37减少?是的-37个GDI对象似乎与简单测试应用程序本身的开销有关。这与上述代码中的循环数无关。我认为OregonGhost(如下)发现了一些GDI+没有使用GDI句柄的东西,我认为它已经使用了!我希望有一些文档可以验证这一点。感谢您的回复。是的,我怀疑GDI+并不总是使用GDI句柄。我确信大型应用程序会泄漏GDI处理。它使用了大量(>500)并且随着时间的推移而增加。至于我们是否一直在使用GDI+,我们只使用系统。我认为只使用GDI+的绘图是正确的吗?谢谢与此相关的是,如果System.Drawing.Pen不使用GDI句柄,那么通过处理它可以释放哪些资源?对不起,如果我的一个问题变成了三个!实际上我不确定。如前所述,我在某个地方读到,有一个System.Drawing实现使用GDI,而人们通常期望System.Drawing使用GDI+。可能是在Windows CE或其他软件上。我也不确定创建窗口句柄(窗体和控件)是否会增加GDI句柄数。在许多情况下,当我泄漏某种句柄时,是因为泄漏了控件,而不是GDI+东西。@Pen.Dispose:您正在释放