CreateGraphics(GdipCreateFromHWND)中的内存不足异常

时间:2013-05-13 16:18:17

标签: winforms exception gdi+ out-of-memory

在配置不同的某些机器上(操作系统,显卡和内存)我得到一个OutOfMemory异常。一些测试表明,虚拟内存消耗没有显着增加。这是引发异常的代码段:

public override Size GetPreferredSize(Size proposedSize)
{
    try
    {
        using (Graphics g = this.CreateGraphics())
        {
            SizeF measured = g.MeasureString(this.Text, this.Font); // <= OutOfMemoryException
            measured += new SizeF(1, 1);
            return measured.ToSize();
        }
    }
    catch (OutOfMemoryException oom)
    {
        System.Diagnostics.Trace.WriteLine(oom.ToString());
    }
    return proposedSize;
}

该类直接来自label。

CreateGraphics()调用GDI +函​​数GdipCreateFromHWND,在某些情况下可能会返回一个状态(3),它会引发我面对的OutOfMemoryException:

[EditorBrowsable(EditorBrowsableState.Advanced), SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)]
public static Graphics FromHwndInternal(IntPtr hwnd)
{
    IntPtr zero = IntPtr.Zero;
    int status = SafeNativeMethods.Gdip.GdipCreateFromHWND(new HandleRef(null, hwnd), out zero);
    if (status != 0)
    {
        throw SafeNativeMethods.Gdip.StatusException(status); // status = 3 throws an OutOfMemoryException with text "Out of memory"
    }
    return new Graphics(zero);
}

但遗憾的是,当它返回Out Of Memory时,我没有找到关于函数和案例的文档。

这个问题至少可以在一台客户机器上快速重复。他所要做的就是单击一个按钮,该按钮创建一个新窗口,其中放置了一个派生的Label,用于在WebBrowser控件中显示内容。

如果您有任何想法可以帮助我找到异常的原因,那就太棒了!

干杯, 迈克尔

2 个答案:

答案 0 :(得分:0)

可能有效:

private Graphics _graphics;
protected override void OnPaint(PaintEventArgs e)
{
    _graphics = e.Graphics;
    base.OnPaint(e);
}

public override Size GetPreferredSize(Size proposedSize)
{
    try
    {
        SizeF measured = _graphics.MeasureString(this.Text, this.Font);
        measured += new SizeF(1, 1);
        return measured.ToSize();
    }
    catch (OutOfMemoryException oom)
    {
        System.Diagnostics.Trace.WriteLine(oom.ToString());
    }
    return proposedSize;
}

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.creategraphics.aspx州的文档&#34;你不能缓存Graphics对象以供重用,除非使用非可视方法,如Graphics.MeasureString&#34;,这正是你想要做的。如果在绘制控件之前调用GetPreferredSize,并且您想要Dispose Graphics对象,这将失败,但这可能会帮助您更接近可行的解决方案。

答案 1 :(得分:0)

解决方法:不要使用Graphics。如果您的应用程序在启动时运行SetCompatibleTextRenderingDefault(false)(因为它会产生更好的文本呈现),您应该使用TextRenderer.MeasureText而不是MeasureString,否则您将使用GDI +进行测量和GDI用于绘图会产生实际渲染和测量之间的差异。

另一种解决方案可能不是缓存Graphics对象,而是缓存大小。由于您未使用proposedSize,只需在TextFont属性更改时测量文本,并在GetPreferredSize中返回该缓存值。