OutOfResources
из модуля Vcl.Graphics
.Стек вызова для исключения выглядел следующий образом:
- 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
- ...
Само исключение возбуждается такой функцией:
procedure OutOfResources; begin raise EOutOfResources.Create(SOutOfResources); end;Которая в свою очередь вызывается из:
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;
Заметьте, что такая реализация в VCL имеет проблему: вне зависимоси от ошибки возбуждается исключение классаEOutOfResources
, даже если ошибка не равнаERROR_NOT_ENOUGH_MEMORY
,ERROR_NO_SYSTEM_RESOURCES
(или аналогичной). Логичнее было бы возбуждать что-то типаEInvalidGraphicOperation
в общем случае и возбуждатьEOutOfResources
только для ошибок подобного типа.
Из полного баг-отчёта EurekaLog было видно, что показатели памяти и описателей находятся в разумной норме, т.е. проблема не в нехватке памяти. Откуда следует, что (вероятнее всего)
GetLastError
вернула 0. В самом деле, строка в TransparentStretchBlt
, которая проваливает проверку GDICheck
выглядит следующим образом:
MemBmp := GDICheck(CreateCompatibleBitmap(SrcDC, SrcW, SrcH));Из документации видно, что функция
CreateCompatibleBitmap
не устанавливает значение GetLastError
при неудаче.Впрочем, у функции не так много причин завершиться неудачей: либо ей переданы неверные аргументы, либо ей нехватает памяти для создания bitmap. Заметьте, что нехватка памяти также возможна, если в
SrcW
и SrcH
находится "мусор", который "слишком большой". Таким образом, хотя мы не знаем точную причину неудачи CreateCompatibleBitmap
, но мы можем предположить, что проблема - в аргументах.Значения
SrcDC
, SrcW
и SrcH
являются параметрами функции и приходят в неё из 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);Где
Canvas
- это поле FCanvas
bitmap-а, создаваемое по запросу, а FDIB
- поле из FImage: TBitmapImage
. Таким образом, все параметры (SrcDC
, SrcW
и SrcH
) приходят в функцию TransparentStretchBlt
из полей объекта класса TBitmap
.Следовательно,
TBitmap
, который пытается рисовать TSomeDBGrid.DrawCell
, повреждён. Поскольку исключения не происходит без EurekaLog, но происходит с EurekaLog, то содержимое памяти TBitmap
меняется при включении EurekaLog. Наиболее вероятное объяснение такого поведения: ошибка типа "use after free". Без отладочных инструментов в программе код может обратиться к уже удалённому TBitmap
и "успешно" выполнить с ним операцию - поскольку память освобождённых объектов не удаляется физически, а лишь помечается как "свободная", без изменения её содежимого. При добавлении в программу EurekaLog её конфигурация по умолчанию включает проверки памяти, которые стирают память при её освобождении.Проверить эту гипотезу можно изменив настройку "When memory is released" в положение "Do nothing" и отключив опцию "Catch memory problems". Если после этого исключение
EOutOfResources
пропадёт, то в коде имеется ошибка вида "use after free". Наиболее вероятна ошибка в коде SomeComponent
, но есть небольшой ненулевой шанс, что клиент нашёл ошибку в VCL.К сожалению, мы не получили ответа от клиента.
Комментариев нет :
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.