今天下午突然接到一个很奇怪的bug,发现在打开Theme的环境下,show OverflowTip时会导致每次1个GDI的泄漏,拿到示例,跟了许久,居然没有发现任何线索,只能通过GDIUsage查到泄漏了相当多的GDI Region,很郁闷,按照新的设计,应该不会有明显的GDI泄漏问题,即使有也可能只是开发时的“手误”,只要打开GDI的Trace工具,肯定就可以发现,结果什么都没有,难道Trace工具也会错?
只好再查,发现问题只发生在Theme打开的环境,恰好这时看到代码中有一段关于Theme的处理,于是抱着试试的心理,封了这段处理(代码3-11行),意外竟然发现不再有GDI Leak了,显然就是这里有鬼了。
1
public override Region CreateRegion()
2
{
3
if (this.UseSystemVisualStyle
4
&& Application.RenderWithVisualStyles
5
&& VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolTip.Standard.Normal))
6
{
7
VisualStyleRenderer render = new VisualStyleRenderer(VisualStyleElement.ToolTip.Standard.Normal);
8![](/Images/OutliningIndicators/InBlock.gif)
9
return render.GetBackgroundRegion(DefaultGraphics, this.Bounds);
10
}
11
else
12
return new Region(this.Bounds);
13
}
但是这里很简单,基本上没有什么复杂的,构造了个VisualStyleRenderer,调用GetBackgroundRegion获得一个Region,于是再次把这段代码打开,GDI又泄漏了,嗯,既然外表看来没有问题,那只能去查查他的家底了。![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](/Images/OutliningIndicators/InBlock.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
打开Reflector,找到VisaulStyleRenderer.GetBackgroundRegion,发现这里是通过uxTheme的API——GetThemeBackgroundRegion来获取一个GDI Region Object,然后通过System.Drawing.Region.FromHrgn获得Region并返回;或许你也发现了,这里的GDI Region Object没有释放!就是说就是这个函数导致了GDI泄漏。
1
[SuppressUnmanagedCodeSecurity]
2
public Region GetBackgroundRegion(IDeviceContext dc, Rectangle bounds)
3
{
4
if (dc == null)
5
{
6
throw new ArgumentNullException("dc");
7
}
8
if ((bounds.Width < 0) || (bounds.Height < 0))
9
{
10
return null;
11
}
12
IntPtr zero = IntPtr.Zero;
13
using (WindowsGraphicsWrapper wrapper = new WindowsGraphicsWrapper(dc, TextFormatFlags.PreserveGraphicsTranslateTransform | TextFormatFlags.PreserveGraphicsClipping))
14
{
15
HandleRef hdc = new HandleRef(wrapper, wrapper.WindowsGraphics.DeviceContext.Hdc);
16
this.lastHResult = SafeNativeMethods.GetThemeBackgroundRegion(new HandleRef(this, this.Handle), hdc, this.part, this.state, new NativeMethods.COMRECT(bounds), ref zero);
17
}
18
if (!(zero != IntPtr.Zero))
19
{
20
return null;
21
}
22
return Region.FromHrgn(zero);
23
}
分析到此,已经可以得出是MS在这里的bug了。为了再次验证,我写了个简单的Demo,当每次点击Button后,就会发现系统资源里多一个GDI Object:![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/None.gif)
3
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
6
![](/Images/OutliningIndicators/InBlock.gif)
7
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/InBlock.gif)
17
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
20
![](/Images/OutliningIndicators/InBlock.gif)
21
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
22
![](/Images/OutliningIndicators/InBlock.gif)
23
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
1
public partial class Form1 : Form
2
{
3
public Form1()
4
{
5
InitializeComponent();
6![](/Images/OutliningIndicators/InBlock.gif)
7
this.Text = "Current GDI Object Number: " + GetGuiResources(Process.GetCurrentProcess().Handle, 0).ToString();
8
}
9![](/Images/OutliningIndicators/InBlock.gif)
10
private void button1_Click(object sender, EventArgs e)
11
{
12
if (Application.RenderWithVisualStyles
13
&& VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolTip.Standard.Normal))
14
{
15
VisualStyleRenderer render = new VisualStyleRenderer(VisualStyleElement.ToolTip.Standard.Normal);
16
using (Graphics g = this.CreateGraphics())
17
{
18
using (Region region = render.GetBackgroundRegion(g, new Rectangle(0, 0, 100, 100)))
19
{
20
g.FillRegion(Brushes.Red, region);
21
}
22
}
23
}
24![](/Images/OutliningIndicators/InBlock.gif)
25
GC.Collect();
26
GC.WaitForPendingFinalizers();
27![](/Images/OutliningIndicators/InBlock.gif)
28
this.Text = "Current GDI Object Number: " + GetGuiResources(Process.GetCurrentProcess().Handle, 0).ToString();
29
}
30![](/Images/OutliningIndicators/InBlock.gif)
31
[DllImport("User32.dll")]
32
private static extern int GetGuiResources(IntPtr hProcess, uint uiFlags);
33
}
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](/Images/OutliningIndicators/InBlock.gif)
4
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/InBlock.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/InBlock.gif)
17
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
18
![](/Images/OutliningIndicators/InBlock.gif)
19
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
20
![](/Images/OutliningIndicators/InBlock.gif)
21
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
22
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
23
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
24
![](/Images/OutliningIndicators/InBlock.gif)
25
![](/Images/OutliningIndicators/InBlock.gif)
26
![](/Images/OutliningIndicators/InBlock.gif)
27
![](/Images/OutliningIndicators/InBlock.gif)
28
![](/Images/OutliningIndicators/InBlock.gif)
29
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
30
![](/Images/OutliningIndicators/InBlock.gif)
31
![](/Images/OutliningIndicators/InBlock.gif)
32
![](/Images/OutliningIndicators/InBlock.gif)
33
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)
下面是运行结果截图:
OK,到此为止,确定这是MS VisualStyleRenderer导致GDI leak了,面对bug,该怎么解决呢?既然这个接口会导致bug,我们只能提供一个相似功能的函数,来作为短期替代方案,待MS修复bug之后再恢复现在code。
下面是解决方案:
1
public override Region CreateRegion()
2
{
3
if (this.UseSystemVisualStyle
4
&& Application.RenderWithVisualStyles
5
&& VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolTip.Standard.Normal))
6
{
7
VisualStyleRenderer render = new VisualStyleRenderer(VisualStyleElement.ToolTip.Standard.Normal);
8![](/Images/OutliningIndicators/InBlock.gif)
9
// MS VisualStyleRenderer.GetBackgroundRegion(
) would cause GDI leak, so I write a same logic
10
// but clean the bug.
11
// If MS have fixed this bug, please take care to rollback if possible.
12
// ---------------------------------------------------------------
13
//return render.GetBackgroundRegion(DefaultGraphics, this.Bounds);
14
IntPtr hrgn = IntPtr.Zero;
15
IntPtr hdc = DefaultGraphics.GetHdc();
16![](/Images/OutliningIndicators/InBlock.gif)
17
try
18
{
19
Platform.UnsafeNativeMethods.GetThemeBackgroundRegion(render.Handle, hdc,
20
render.Part, render.State, new Platform.NativeMethods.RECT(this.Bounds), ref hrgn);
21
if (hrgn == IntPtr.Zero)
22
{
23
return null;
24
}
25![](/Images/OutliningIndicators/InBlock.gif)
26
return Region.FromHrgn(hrgn);
27
}
28
finally
29
{
30
if (hdc != IntPtr.Zero)
31
{
32
DefaultGraphics.ReleaseHdc(hdc);
33
}
34
if (hrgn != IntPtr.Zero)
35
{
36
Platform.UnsafeNativeMethods.DeleteObject(hrgn);
37
}
38
}
39
}
40
else
41
return new Region(this.Bounds);
42
}
![](/Images/OutliningIndicators/None.gif)
2
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](/Images/OutliningIndicators/InBlock.gif)
4
![](/Images/OutliningIndicators/InBlock.gif)
5
![](/Images/OutliningIndicators/InBlock.gif)
6
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](/Images/OutliningIndicators/InBlock.gif)
8
![](/Images/OutliningIndicators/InBlock.gif)
9
![](/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/dot.gif)
10
![](/Images/OutliningIndicators/InBlock.gif)
11
![](/Images/OutliningIndicators/InBlock.gif)
12
![](/Images/OutliningIndicators/InBlock.gif)
13
![](/Images/OutliningIndicators/InBlock.gif)
14
![](/Images/OutliningIndicators/InBlock.gif)
15
![](/Images/OutliningIndicators/InBlock.gif)
16
![](/Images/OutliningIndicators/InBlock.gif)
17
![](/Images/OutliningIndicators/InBlock.gif)
18
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
19
![](/Images/OutliningIndicators/InBlock.gif)
20
![](/Images/OutliningIndicators/InBlock.gif)
21
![](/Images/OutliningIndicators/InBlock.gif)
22
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
23
![](/Images/OutliningIndicators/InBlock.gif)
24
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
25
![](/Images/OutliningIndicators/InBlock.gif)
26
![](/Images/OutliningIndicators/InBlock.gif)
27
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
28
![](/Images/OutliningIndicators/InBlock.gif)
29
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
30
![](/Images/OutliningIndicators/InBlock.gif)
31
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
32
![](/Images/OutliningIndicators/InBlock.gif)
33
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
34
![](/Images/OutliningIndicators/InBlock.gif)
35
![](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
36
![](/Images/OutliningIndicators/InBlock.gif)
37
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
38
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
39
![](/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
40
![](/Images/OutliningIndicators/InBlock.gif)
41
![](/Images/OutliningIndicators/InBlock.gif)
42
![](/Images/OutliningIndicators/ExpandedBlockEnd.gif)