为什么对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;