2025-12-03 20:40:34 游戏周边

为什么对GetDIBits的调用在Win64上失败?

更新对此答案的注释指出了代码失败的原因。bitmap.Handle和bitmap.Canvas.Handle的评价顺序。由于参数计算顺序未定义,所以程序有未定义的行为。这也解释了为什么x86和x64程序在行为上有差异。

因此,您可以通过将位图句柄和设备上下文按适当的顺序分配给局部变量,然后将这些参数作为参数传递给GetDIBits来解决这个问题。但我仍然认为,代码最好避免使用VCL TBitmap类,直接使用GDI调用,如下面的代码所示。

我相信您的错误是传递位图句柄及其画布句柄。相反,您应该传递通过调用CreateCompatibleDC(0)获得的设备上下文。或者将IconInfo.hbmColor传递给GetDIBits。但是不要传递TBitmap的句柄和画布的句柄。

我也看不出您创建的TBitmap有什么用途。您所做的就是获得IconInfo.hbmColor的宽度和高度。您不需要创建TBitmap就可以做到这一点。

因此,如果我是您,我将删除TBitmap,并使用CreateCompatibleDC(0)获取设备上下文。这将大大简化代码。

您还需要删除调用GetIconInfo返回的位图,但我猜您已经知道这一点,并且为了简单起见,将代码从问题中删除。

坦率地说,VCL对象只是在这里受到阻碍。直接调用GDI函数实际上要简单得多。也许是这样的:

代码语言:javascript复制procedure Main;

var

res: Cardinal;

Bits: PRGBALine;

bitmap: Winapi.Windows.TBitmap;

DC: HDC;

buffer: TMyBitmapInfo;

BitmapInfo: TBitmapInfo absolute buffer;

BitsSize: Cardinal;

IconInfo: TIconInfo;

begin

if not GetIconInfo(LoadIcon(0, IDI_WINLOGO), IconInfo) then begin

Writeln('Error GetIconInfo: ', GetLastError);

Exit;

end;

try

if GetObject(IconInfo.hbmColor, SizeOf(bitmap), @bitmap) = 0 then begin

Writeln('Error GetObject');

Exit;

end;

BitsSize := BytesPerScanline(bitmap.bmWidth, 32, 32) * abs(bitmap.bmHeight);

Bits := AllocMem(BitsSize);

try

buffer := TMyBitmapInfo.Create(bitmap.bmWidth, abs(bitmap.bmHeight));

DC := CreateCompatibleDC(0);

res := GetDIBits(DC, IconInfo.hbmColor, 0, abs(bitmap.bmHeight), Bits, BitmapInfo,

DIB_RGB_COLORS);

DeleteDC(DC);

if res = 0 then begin

Writeln('Error GetDIBits: ', GetLastError);

Exit;

end;

Writeln('Succeed');

finally

FreeMem(Bits);

end;

finally

DeleteObject(IconInfo.hbmMask);

DeleteObject(IconInfo.hbmColor);

end;

end;