Click here to Skip to main content
15,881,938 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am using a COM Interface where I am receiving byte data representing a DIP (Device Independent Bitmap). I want take this byte[] and copy its data into a BITMAPINFOHEADER variable.

I have a long piece of code and this is only part of it, however it is written in VBA and it works:

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 "---------------"


Output:

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


Above code shows the variable b has a biSize of 0 before and 40 after the CopyMemory Method is called upon.

I tried to replicate the code in C# however it doesn't work.

[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 + "");


What I have tried:

The below line of code worked as per accepted answer below.


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


Latest Attempt

byte[] 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 + "");


Output:

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


Above values don't seem right as compared to what I am getting from the VBA Code.

VBA Values

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


Final Solution

So I saved the byte array I was getting into a text file, open this text file in notepad++ and then using the HEX Editor plugin saw the actual data.

I notice the DIB Header what starting at byte 28, so I incremented my pointer by 27 and then I got the correct values.

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));
Posted
Updated 19-Sep-17 3:35am
v11

1 solution

You are copying to bimPtr (allocated memory) but print out bim.biSize (bim is allocated but never changed).

Instead of trying to convert VBA code that calls an API function to copy memory (which calls the standard C library function memcpy in the background), you should understand what the code is doing and implement in C#.

The C# equivalent of CopyMemory is the Marshal.Copy Method (System.Runtime.InteropServices)[^].

As always when handling some kind of binary data, treat them as byte array. You can for example use the Marshal.Copy method that copies unmanaged data (the source) to a byte array and cast that to the structure.

But in your case it is even simpler. Just use the Marshal.PtrToStructure Method (System.Runtime.InteropServices)[^] to get a copy into the structure passing the IntPtr of the unmanaged source.
 
Share this answer
 
Comments
Member 13373000 30-Aug-17 12:08pm    
Marshal.PtrToStructure Worked, it started giving me some sort of values in the the BITMAPINFOHEADER
Member 13373000 13-Sep-17 14:38pm    
However the problem is that it is giving me what seems to me incorrect values, for example, bim.biSize should be 40 but its giving me 256 and bim.biWidth should br 401 its giving me -256. I am comparing this to the values I got from the VBA code. Any idea what the problem could be?
Jochen Arndt 14-Sep-17 8:52am    
Probably because you are passing
IntPtr lPtr = pinned.AddrOfPinnedObject();
instead of l (which are the image data as far as I understand your updated code).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900