Reputation: 37
on certain machines that vary in configuration (OS, graphics card and memory) I get an OutOfMemory exception. Some tests showed that there is no significant increase in virtual memory consumed. That's the piece of code where the exception is raised:
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;
}
The class is derived directly from label.
CreateGraphics() makes a call to the GDI+ function GdipCreateFromHWND which could in some cases return a status (3) that raises the OutOfMemoryException I face:
[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);
}
But unfortunately I have not found documentation on the function and cases when it returns Out Of Memory.
The issue is at least repeatable on one customers machine very fast. All he has to do is click on a button that creates a new window where one of this derived Label is placed and which is used to display content in a WebBrowser control.
If you have any ideas that could help me find reasons of the exception it would be great!
Cheers, Michael
Upvotes: 0
Views: 1097
Reputation: 3788
The un-solution: Don't use Graphics
. If your application runs SetCompatibleTextRenderingDefault(false)
at startup (which it should, because it yields better text rendering), you should use TextRenderer.MeasureText instead of MeasureString
, because otherwise you will use GDI+ for measuring and GDI for drawing which will create discrepancies between actual rendering and measurement.
Another solution could be not caching the Graphics
object but caching the size. Since you are not using proposedSize
anyway, simply measure the text whenever the Text
or Font
property change and return that cached value in GetPreferredSize
.
Upvotes: 2
Reputation: 434
This might work:
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;
}
The documentation at http://msdn.microsoft.com/en-us/library/system.windows.forms.control.creategraphics.aspx states "you cannot cache the Graphics object for reuse, except to use non-visual methods like Graphics.MeasureString", which is exactly what you are trying to do. This will fail if GetPreferredSize is called before the control is painted, and you'll want to Dispose the Graphics object, but maybe this will help get you closer to a viable solution.
Upvotes: 0