1

This code works fine when running 32-bit. But when I switch to 64-bit the GetObject method does not work and BITMAP struct is empty.

IntPtr hBmp = ObtainValidBitmapHandleFromSystem();
BITMAP bmpData = new BITMAP();
/* BITMAP consists of four 32bit ints,
 * two 16 bit uints and one IntPtr */
 * 4 + sizeof(UInt16) * 2 + IntPtr.Size;
int cbBuffer = sizeof(Int32) * 4 + sizeof(UInt16) * 2 + IntPtr.Size;
NativeMethods.GetObject(hBmp, cbBuffer, out bmpData);

Bitmap bmp = new Bitmap(bmpData.Width, bmpData.Height, PixelFormat.Format32bppPArgb);

The native method implementation:

private static class NativeMethods
{
    [DllImport("gdi32", CharSet = CharSet.Auto)]
    internal extern static int GetObject(
        IntPtr hgdiobj,     // handle to graphics object
        int cbBuffer,       // size of buffer for object information
        out BITMAP lpvObject    // Should be IntPtr, but we know we will use it only for BITMAP.
    );
}

The BITMAP structure implementation (removed documentation to keep code compact):

[StructLayoutAttribute(LayoutKind.Sequential)]
private struct BITMAP
{
    public Int32 Type;
    public Int32 Width;
    public Int32 Height;
    public Int32 WidthBytes;
    public UInt16 Planes;
    public UInt16 BitsPixel;
    public IntPtr Bits;
}

The idea behind this code is fully described in this question.

At first I thought that the issue is caused by different size of IntPtr resulting in different size of cbBuffer, but it seems that this is not the case as changing cbBuffer size did not help.

What is the correct way to use GDI's GetObject method on 64-bit system?

Community
  • 1
  • 1
SiliconMind
  • 2,185
  • 4
  • 25
  • 49
  • How does NativeMethods look like ? – Tigran Apr 11 '13 at 13:38
  • @Tigran I've edited my question to include part of the `NativeMethods` class. – SiliconMind Apr 11 '13 at 13:49
  • What do you mean by "does not work"? Isn't there any exception? – King King Apr 11 '13 at 13:52
  • @KingKing as I said in the question: "BITMAP struct is empty". There is no exception or noting. After call to `GetObject` the `BITMAP` struct is still empty (all it's values are zero). – SiliconMind Apr 11 '13 at 13:58
  • What's the return code value for GetObject? – Simon Mourier Apr 11 '13 at 14:15
  • I think you should check the result returned by GetObject first, there is a documentation on it http://msdn.microsoft.com/en-us/library/windows/desktop/dd144904(v=vs.85).aspx I really don't understand what you have from the handle returned by ObtainValidBitmapHandleFromSystem(), I think the hBmp should point to a Bitmap's memory which certainly contains data. – King King Apr 11 '13 at 14:20
  • @KingKing hBmp is an HBITMAP and that's an opaque pointer – David Heffernan Apr 11 '13 at 17:59
  • @DavidHeffernan I've googled for opaque pointer and it seems that it is originated from the world of C, I can understand it a bit however what does it matter the fact that it (maybe the underlying pointer) should point to some memory address at which we can obtain data. Thanks. – King King Apr 11 '13 at 19:32
  • @KingKing But the point is that only the system knows the layout of that data. It's not something you ever read. – David Heffernan Apr 11 '13 at 19:43
  • @DavidHeffernan OK, thank you. That's why it's called 'opaque'. :) – King King Apr 11 '13 at 20:16

1 Answers1

3

The problem is this line:

cbBuffer = sizeof(Int32) * 4 + sizeof(UInt16) * 2 + IntPtr.Size;

That works on the 32 bit version because the alignment of the struct has no padding. But on the 64 bit version, there are 4 bytes of padding before the pointer. So cbBuffer is 4 bytes short.

That's the problem. The solution is to stop calculating the size yourself and use Marshal.SizeOf() which is designed for this very purpose.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • if so wouldn's be enough to use compbination of [StructLayout(LayoutKind.Explicit)] and [FieldOffset(OFFSET_NUMBER)] ? – Tigran Apr 11 '13 at 14:43
  • 1
    @Tigran That's used when the standard alignment patterns don't fit the actual layout of your struct. Those scenarios are rare for C structs. They are invariably aligned, sometimes packed. Both of which can be handled by `LayoutKind.Sequential`. The main use for `LayoutKind.Explicit` is to make a C union. That's when you use `FieldOffset(0)` for all members. P.S. Thanks for the upvote and the grown up way you handled to your now deleted answer. Many users here would have reacted less well! – David Heffernan Apr 11 '13 at 14:49
  • Who deleted the first answer? What kind of privilege is so powerful? – King King Apr 11 '13 at 15:00
  • @KingKing The author/owner of the answer deleted it. The owner can always do that, unless the answer has been accepted. Other than that, if an answer has negative scores, then 3 delete votes from high community mods will delete it. And posts can always be deleted by diamond mods. – David Heffernan Apr 11 '13 at 15:00
  • Thanks @DavidHeffernan - this works as expected... now I have to figure out why on 64-bit my bitmaps with alpha channel get that weird psychedelic colors :/ – SiliconMind Apr 11 '13 at 15:03