设置文本框的字体会增加GDI HFONT手柄

时间:2017-09-20 10:15:40

标签: c# winforms winapi fonts gdi

我们有一个动态控件创建,允许用户自己配置面向任务的GUI。一些WinForms控件似乎与其他控件不同地处理非托管句柄。问题的最好例子是TextBox:

只要设置了文本框实例Font属性,即使字体与另一个文本框实例中的字体相同,也会创建新的GDI句柄。同样代表设置创建HBRUSH句柄的BackColor。

如果您拥有包含大量控件的丰富用户界面,则可以轻松达到每个进程的10000个句柄限制。

一种解决方法是使用WM_SETFONT设置字体并控制句柄创建,但这会导致其他问题,例如文本框不会调整为字体。

要查看GDI句柄,我们使用Nir-Soft的GDIView。带有三个文本框和两个按钮的示例代码如下所示:

public partial class Form1 : Form
{
    // Used for the work-around
    [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
    public static extern bool DeleteObject([In]IntPtr hObject);

    // Used for the work-around
    [DllImport("user32.dll")]
    internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    internal const int WM_SETFONT = 0x0030;

    private class FontFactory
    {
        private static Dictionary<Font, IntPtr> _fonts = new Dictionary<Font, IntPtr>();
        public static void SetFont(Font font, IntPtr controlHandle)
        {

            IntPtr fontHandle;
            if (!_fonts.TryGetValue(font, out fontHandle))
            {
                fontHandle = font.ToHfont();
                _fonts[font] = fontHandle;
            }

            SendMessage(controlHandle, WM_SETFONT, fontHandle, IntPtr.Zero);
        }
    }

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        // Use these lines to reproduce the problem - Will increment HFONTS by 3
        Font font = new Font("Arial", 10.5f);
        textBox1.Font = font;
        textBox2.Font = font;
        textBox3.Font = font;

        // This will reduce the number of handles but does not resize the text boxes
        // Increments HFONTS by only one
        /*          
        FontFactory.SetFont(new Font("Comic Sans MS", 12.5f), textBox1.Handle);
        FontFactory.SetFont(new Font("Comic Sans MS", 12.5f), textBox2.Handle);
        FontFactory.SetFont(new Font("Comic Sans MS", 12.5f), textBox3.Handle);
        */
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Controls.Remove(textBox3);
        textBox3.Dispose();
        textBox3 = null;

        GC.Collect();
    }
}

我希望我能够很好地解释这个问题。提前感谢您的帮助!

0 个答案:

没有答案