OutOfResources
helper function from the Vcl.Graphics
unit.A call stack for the exception looked like this:
- Vcl.Graphics.OutOfResources
- Vcl.Graphics.GDIError
- Vcl.Graphics.GDICheck
- Vcl.Graphics.TransparentStretchBlt
- Vcl.Graphics.TBitmap.Draw
- Vcl.Graphics.TCanvas.Draw
- SomeComponent.TSomeDBGrid.DrawCell
- Vcl.Grids.DrawCells
- Vcl.Grids.TCustomGrid.Paint
- Vcl.Controls.TCustomControl.PaintWindow
- Vcl.Controls.TWinControl.PaintHandler
- Vcl.Controls.TWinControl.WMPrintClient
- ...
The exception itself is raised by this function:
procedure OutOfResources; begin raise EOutOfResources.Create(SOutOfResources); end;Which in turn is called from:
procedure GDIError; const BufSize = 256; var ErrorCode: Integer; Buf: array [Byte] of Char; begin ErrorCode := GetLastError; if (ErrorCode <> 0) and (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil, ErrorCode, LOCALE_USER_DEFAULT, Buf, BufSize, nil) <> 0) then raise EOutOfResources.Create(Buf) else OutOfResources; end; function GDICheck(Value: THandle): THandle; begin if Value = 0 then GDIError; Result := Value; end;
Note that this VCL implementation has a problem: regardless of the error, an exception of theEOutOfResources
class is thrown - even if the error is notERROR_NOT_ENOUGH_MEMORY
,ERROR_NO_SYSTEM_RESOURCES
(or a similar one). It would make more sense to raise something likeEInvalidGraphicOperation
for a general case, and raiseEOutOfResources
specifically only for errors of this type.
From the full EurekaLog bug report, it was clear that the memory and handles count are within a reasonable range, i.e. the problem is not a lack of memory. Which means that (most likely)
GetLastError
returned 0. Indeed, the line in TransparentStretchBlt
that fails the GDICheck
check looks like this:
MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, SrcW, SrcH));You can see from the documentation that the
CreateCompatibleBitmap
function does not set the GetLastError
value on failure.However, there are not many reasons for the function to fail: either wrong arguments were passed to it, or it ran out of memory to create a bitmap. Note that running out of memory is also possible if
SrcW
and/or SrcH
are trashed in such way, so these values become "too large". So while we don't know the exact reason for CreateCompatibleBitmap
's failure, we can assume that the problem is in its arguments.The
SrcDC
, SrcW
, and SrcH
values are arguments of the TransparentStretchBlt
function and come from TBitmap.Draw
:
TransparentStretchBlt (ACanvas.FHandle, Left, Top, Right - Left, Bottom - Top, Canvas.FHandle { SrcDC }, 0, 0, FDIB.dsbm.bmWidth { SrcW }, FDIB.dsbm.bmHeight { SrcH }, MaskDC, 0, 0);Where
Canvas
is the FCanvas
field of TBitmap
created on demand, and FDIB
is the FImage: TBitmapImage
TBitmap
's field. Thus, all arguments (SrcDC
, SrcW
, and SrcH
) come to the TransparentStretchBlt
function from fields of the TBitmap
class object.Therefore the
TBitmap
that TSomeDBGrid.DrawCell
is trying to draw is corrupted. Since the exception does not occur without EurekaLog, but does happen with EurekaLog, the TBitmap
's memory contents is changed when EurekaLog is enabled. The most likely explanation for this behavior is a "use after free" bug. Without debugging tools in the program: a code can access an already deleted TBitmap
and "successfully" perform an operation with it - since the memory of freed objects is not physically deleted, but only marked as "free", without changing its content. However, when addeding EurekaLog to an application, its default configuration includes memory checks that erase the memory when it is freed.You can check this hypothesis by changing the "When memory is released" option to "Do nothing" and unchecking the "Catch memory problems" option. If the
EOutOfResources
exception disappears after changing these options, then the code contains a "use after free" bug. Most likely the bug is in SomeComponent
's code, but there is a small nonzero chance that the client has found a bug in the VCL itself - similar to this one.Unfortunately, we have not received a response from the client.
P.S. Read more stories like this one or read feedback from our customers.