gdi32.GetObject не работает при запуске 64-разрядной версии

Этот код отлично работает при запуске 32-разрядной версии. Но когда я переключаюсь на 64-битную версию, метод GetObject не работает, а структура BITMAP пуста.

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);

Реализация нативного метода:

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.
    );
}

Реализация структуры BITMAP (документация удалена для сохранения компактности кода):

[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;
}

Идея этого кода полностью описана в этом вопрос.

Сначала я подумал, что проблема вызвана разным размером IntPtr, что приводит к разному размеру cbBuffer, но кажется, что это не так, поскольку изменение размера cbBuffer не помогло.

Как правильно использовать метод GetObject GDI в 64-битной системе?


person SiliconMind    schedule 11.04.2013    source источник
comment
Как выглядит NativeMethods?   -  person Tigran    schedule 11.04.2013
comment
@Tigran Я отредактировал свой вопрос, включив в него часть класса NativeMethods.   -  person SiliconMind    schedule 11.04.2013
comment
Что вы имеете в виду под не работает? Разве нет никаких исключений?   -  person King King    schedule 11.04.2013
comment
@KingKing, как я уже сказал в вопросе: структура BITMAP пуста. Нет никаких исключений или замечаний. После вызова GetObject структура BITMAP по-прежнему пуста (все ее значения равны нулю).   -  person SiliconMind    schedule 11.04.2013
comment
Какое значение кода возврата для GetObject?   -  person Simon Mourier    schedule 11.04.2013
comment
Я думаю, вам следует сначала проверить результат, возвращаемый GetObject, по нему есть документация msdn.microsoft.com/en-us/library/windows/desktop/ Я действительно не понимаю, что у вас есть из дескриптора, возвращенного ObtainValidBitmapHandleFromSystem(), я думаю, что hBmp должен указывать на память растрового изображения, которая, безусловно, содержит данные.   -  person King King    schedule 11.04.2013
comment
@KingKing hBmp — это HBITMAP, и это непрозрачный указатель.   -  person David Heffernan    schedule 11.04.2013
comment
@DavidHeffernan Я гуглил непрозрачный указатель, и кажется, что он произошел из мира C, я немного его понимаю, однако какое значение имеет тот факт, что он (возможно, базовый указатель) должен указывать на некоторый адрес памяти в которые мы можем получить данные. Спасибо.   -  person King King    schedule 11.04.2013
comment
@KingKing Но дело в том, что только система знает расположение этих данных. Это не то, что вы когда-либо читали.   -  person David Heffernan    schedule 11.04.2013
comment
@DavidHeffernan Хорошо, спасибо. Вот почему он называется «непрозрачным». :)   -  person King King    schedule 12.04.2013


Ответы (1)


Проблема в этой строке:

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

Это работает в 32-битной версии, потому что выравнивание структуры не имеет заполнения. Но в 64-битной версии перед указателем есть 4 байта заполнения. Таким образом, cbBuffer составляет 4 байта.

Это проблема. Решение состоит в том, чтобы прекратить вычислять размер самостоятельно и использовать Marshal.SizeOf(), который предназначен именно для этой цели.

person David Heffernan    schedule 11.04.2013
comment
если это так, было бы достаточно использовать комбинацию [StructLayout(LayoutKind.Explicit)] и [FieldOffset(OFFSET_NUMBER)]? - person Tigran; 11.04.2013
comment
@Tigran Это используется, когда стандартные шаблоны выравнивания не соответствуют фактическому макету вашей структуры. Эти сценарии редки для структур C. Они неизменно выровнены, иногда упакованы. И то, и другое может быть обработано LayoutKind.Sequential. Основное использование для LayoutKind.Explicit - это объединение C. Это когда вы используете FieldOffset(0) для всех участников. P.S. Спасибо за голосование и то, как вы по-взрослому отнеслись к своему теперь удаленному ответу. Многие пользователи здесь отреагировали бы менее хорошо! - person David Heffernan; 11.04.2013
comment
Кто удалил первый ответ? Что за привилегия такая мощная? - person King King; 11.04.2013
comment
@KingKing Автор/владелец ответа удалил его. Владелец всегда может это сделать, если только ответ не был принят. Кроме того, если ответ имеет отрицательные баллы, то 3 голоса за удаление от модов с высоким сообществом удалят его. И посты всегда могут быть удалены алмазными модами. - person David Heffernan; 11.04.2013
comment
Спасибо @DavidHeffernan - это работает, как и ожидалось ... теперь мне нужно выяснить, почему на 64-битных моих растровых изображениях с альфа-каналом появляются эти странные психоделические цвета :/ - person SiliconMind; 11.04.2013