Member 13373000 Ответов: 1

BITMAPINFOHEADER : заполнение данных с помощью copymemory


Я использую COM-интерфейс, где я получаю байтовые данные, представляющие DIP (Device Independent Bitmap). Я хочу взять этот байт[] и скопировать его данные в переменную BITMAPINFOHEADER.

У меня есть длинный кусок кода, и это только его часть, однако он написан на VBA и работает:

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)

Dim l() As Byte: l = v
Dim bim As BITMAPINFOHEADER
  
Debug.Print ("Length: " & (UBound(l) - LBound(l) + 1))
Debug.Print "Size of BitMapInfoHeader: " & Len(bim)
Debug.Print "---------------"
Debug.Print "l(0) : " & l(0)
  
Debug.Print "biSize before: " & bim.biSize
CopyMemory bim, l(0), Len(bim)
Debug.Print "biSize after: " & bim.biSize
Debug.Print "---------------"


Выход:

Length: 897832
Size of BitMapInfoHeader: 40
---------------
l(0): 40
biSize before: 0
biSize after: 40
---------------


Приведенный выше код показывает, что переменная b имеет biSize 0 до и 40 после вызова метода CopyMemory.

Я попытался скопировать код на C#, но он не работает.

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

byte[] l;
BITMAPINFOHEADER bim = new BITMAPINFOHEADER();

BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
    bf.Serialize(ms, v);
    l = ms.ToArray();
}

int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(BITMAPINFOHEADER));
Debug.Print("Size of BitMapInfoHeader: " + size + "");
Debug.Print("---------------");

IntPtr bimPtr = Marshal.AllocHGlobal(Marshal.SizeOf(bim));
GCHandle pinned = GCHandle.Alloc(l, GCHandleType.Pinned);
IntPtr lPtr = pinned.AddrOfPinnedObject();
Debug.Print("Byte Array Pointer: " + lPtr + "");
Debug.Print("BITMAPINFOHEADER Pointer: " + bimPtr + "");
Debug.Print("---------------");
            
Debug.Print("Byte Array Lenght: " + l.Length + "");
Debug.Print("---------------");
Debug.Print("biSize before: " + bim.biSize + "");
CopyMemory(bimPtr, lPtr, Convert.ToUInt32(size));
Debug.Print("biSize after: " + bim.biSize + "");


Что я уже пробовал:

Приведенная ниже строка кода работала в соответствии с принятым ниже ответом.


BITMAPINFOHEADER bim = (BITMAPINFOHEADER) Marshal.PtrToStructure(lPtr, typeof(BITMAPINFOHEADER));


последняя попытка

байт[] l;
BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, v);
                l = ms.ToArray();
            }

            int size = 
            System.Runtime.InteropServices.Marshal.SizeOf(typeof(BITMAPINFOHEADER));
            Debug.Print("Size of BitMapInfoHeader: " + size + "");
            Debug.Print("---------------");

            GCHandle pinned = GCHandle.Alloc(l, GCHandleType.Pinned);
            IntPtr lPtr = pinned.AddrOfPinnedObject();
            Debug.Print("Byte Array Pointer: " + lPtr + "");

            Debug.Print("---------------");

            Debug.Print("Byte Array Lenght: " + l.Length + "");
            Debug.Print("---------------");
            
            BITMAPINFOHEADER bim = (BITMAPINFOHEADER)Marshal.PtrToStructure(lPtr, typeof(BITMAPINFOHEADER));

            Debug.Print("biWidth after: " + bim.biWidth + "");
            Debug.Print("biHeight after: " + bim.biHeight + "");
            Debug.Print("biSize after: " + bim.biSize + "");
            Debug.Print("biSizeImage after: " + bim.biSizeImage + "");
            Debug.Print("biBitCount after: " + bim.biBitCount + "");


Выход:

Byte Array Pointer: 56182800
---------------
Byte Array Lenght: 897860
---------------
biWidth after: -256
biHeight after: 511
biSize after: 256
biSizeImage after: 3005743104


Приведенные выше значения кажутся неправильными по сравнению с тем, что я получаю из кода VBA.

Значения VBA

Byte Array Length: 897832
bim.biSize: 0
bim.biSize: 40
bim.biWidth: 501
bim.biHeight: 448


окончательное решение

Поэтому я сохранил массив байтов, который я получал, в текстовый файл, открыл этот текстовый файл в notepad++, а затем с помощью плагина HEX Editor увидел фактические данные.

Я заметил, что заголовок DIB начинается с байта 28, поэтому я увеличил свой указатель на 27, а затем получил правильные значения.

https://stackoverflow.com/questions/45696075/byte-array-to-image-parameter-is-not-valid

lPtr = new IntPtr(lPtr.ToInt64() + 27);
BITMAPINFOHEADER bim = (BITMAPINFOHEADER)Marshal.PtrToStructure(lPtr, typeof(BITMAPINFOHEADER));

1 Ответов

Рейтинг:
1

Jochen Arndt

Вы копируете в bimPtr (выделенная память) но распечатайте bim.biSize (bim выделяется, но никогда не изменяется).

Вместо того чтобы пытаться преобразовать код VBA, вызывающий функцию API для копирования памяти (которая вызывает стандартную функцию библиотеки C memcpy в фоновом режиме), вы должны понять, что делает код и реализовать его на C#.

Эквивалент C# CopyMemory это Маршал.Метод Копирования (System. Runtime.InteropServices)[^].

Как всегда при обработке каких-либо двоичных данных, относитесь к ним как к byte массив. Вы можете, например, использовать Marshal.Copy метод, который копирует неуправляемые данные (источник) в массив байтов и приводит их к структуре.

Но в вашем случае все еще проще. Просто используйте Маршал.Метод PtrToStructure (System. Runtime.InteropServices)[^] чтобы получить копию в структуру, передающую IntPtr неуправляемого источника.


Member 13373000

Маршал.PtrToStructure работал, он начал давать мне какие-то значения в BITMAPINFOHEADER

Member 13373000

Однако проблема в том, что он дает мне то, что мне кажется неправильным, например, bim.biSize должен быть 40, но он дает мне 256, а bim.biWidth должен быть 401, что дает мне -256. Я сравниваю это со значениями, полученными из кода VBA. Есть идеи, в чем может быть проблема?

Jochen Arndt

Наверное потому что вы проходите мимо
IntPtr lPtr = закреплено.AddrOfPinnedObject();
вместо l (которые являются данными изображения, насколько я понимаю ваш обновленный код).