0

在某些配置不同的机器(操作系统、显卡和内存)上,我得到了 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;
}

该类直接从标签派生。

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

[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 时,我还没有找到关于函数和案例的文档。

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

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

干杯,迈克尔

4

2 回答 2

1

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

另一种解决方案可能不是缓存Graphics对象而是缓存大小。由于无论如何您都没有使用,因此只需在or属性更改proposedSize时测量文本并将该缓存值返回到.TextFontGetPreferredSize

于 2014-04-04T19:18:50.217 回答
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上的文档指出“您不能缓存 Graphics 对象以供重用,除非使用像 Graphics 这样的非可视方法.MeasureString”,这正是您想要做的。如果在绘制控件之前调用 GetPreferredSize,这将失败,并且您需要 Dispose 图形对象,但这可能有助于您更接近可行的解决方案。

于 2013-05-28T19:52:00.797 回答