procedure Test(const BitmapHandle: HBITMAP; Value: Boolean); var BitmapInfo: Windows.TBitmap; begin if not Value then Exit; FillChar(BitmapInfo, SizeOf(BitmapInfo), 0); if Windows.GetObject(BitmapHandle, SizeOf(BitmapInfo), @BitmapInfo) = 0 then ShowMessage('FAIL'); // ... end;Этот код может не работать (показывать
'FAIL'
). Объяснить почему и как это исправить. В предположении, конечно же, что передали корректный HBITMAP
и Value = True
.Бонус-задачка: объяснить когда код может работать. И почему.
Ответ на задачку будет выложен как обычно: примерно через месяц.
Ответ.
Совсем недавно столкнулся с подобным. Хотел сделать BitmapInfo полем класса (точнее только размеры), но чудесным образом GetObject в его конструкторе возвращало 0. Вынес все в гдобальные переменные - заработало:
ОтветитьУдалитьvar
ArrowBitmap: HBITMAP = 0;
ArrowBitmapSize: TSize = (cx: 0; cy: 0);
procedure LoadArrowBitmap;
var
Bitmap: Windows.Bitmap;
begin
if (ArrowBitmap = 0) then
begin
ArrowBitmap := LoadBitmap(HInstance, 'BTNMNU');
if (ArrowBitmap <> 0) and
(GetObject(ArrowBitmap, SizeOf(Bitmap), @Bitmap) <> 0) then
begin
ArrowBitmapSize.cx := Bitmap.bmWidth;
ArrowBitmapSize.cy := Bitmap.bmHeight;
end;
end;
end;
{ TMySplitButton }
constructor TMySplitButton.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
LoadArrowBitmap;
UpdateSplitWidth;
end;
PS. Ничего не понял :)
Если входной параметр BitmapHandle: HBITMAP получен через CreateDIBSection, то GetObject попытается в BitmapInfo: Windows.TBitmap записать информацию типа Windows.TDIBSection, что немного больше по размеру.
ОтветитьУдалить"Если входной параметр BitmapHandle: HBITMAP получен через CreateDIBSection, то GetObject попытается в BitmapInfo: Windows.TBitmap записать информацию типа Windows.TDIBSection, что немного больше по размеру. "
ОтветитьУдалитьif Object type is HBITMAP returned from a call to CreateDIBSection then Data written to buffer is DIBSECTION, if cbBuffer is set to sizeof(DIBSECTION), or BITMAP, if cbBuffer is set to sizeof(BITMAP)
(Platform SDK)
Код работает с включенной оптимизацией и не работает с выключенной. На мой взгляд, причина в том, что адрес буфера не выровнен на границу 4-х байт в случае отключенной оптимизации (Delphi 2006).
Изменив код на
procedure Test(const BitmapHandle: HBITMAP; Value: Boolean);
var
RetValue: LongInt;
BitmapInfo: Windows.TBitmap;
begin
if not Value then
Exit;
FillChar(BitmapInfo, SizeOf(BitmapInfo), 0);
RetValue := Windows.GetObject(BitmapHandle, SizeOf(BitmapInfo), @BitmapInfo);
if RetValue = 0 then
ShowMessage('FAIL')
else
ShowMessage('SUCCESS');
end;
он заработал вне зависимости от настроек оптимизации.
При выключенной оптимизации компилятор резервирует место на стеке под Value, размером в один байт. Так как Windows.TBitmap объявлена как packed record, то компилятор не заботится о выравнивании этой переменной и она располагается сразу за местом для Value, по нечетному адресу.
ОтветитьУдалитьПри включенной оптимизации на стеке размещается единственная переменная типа Windows.TBitmap, адрес которой, естественно, выровнен на 4-х байтовую границу.
Собственно, багов никаких нет. Единственное, что есть, это неполное описание параметров функции GetObject в Platform SDK, например, что требуется выровненный буфер (по моему мнению)
ОтветитьУдалить