Copying a byte array using Marshal.Copy from IntPtr using C # doesn't work

I use an unmanaged library that generates grayscale images (about 100x200 pixels, more or less). The image is contained inside a structure that looks like this: C:

typedef struct abs_image { ABS_DWORD Width; ABS_DWORD Height; ABS_DWORD ColorCount; ABS_DWORD HorizontalDPI; ABS_DWORD VerticalDPI; ABS_BYTE ImageData[ABS_VARLEN]; } ABS_IMAGE 
 typedef unsigned int ABS_DWORD; typedef unsigned char ABS_BYTE; 

And here is my C # shell structure:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct ABS_IMAGE { public uint Width; public uint Height; public uint ColorCount; public uint HorizontalDPI; public uint VerticalDPI; public IntPtr ImageData; } 

Image capture and ABS_IMAGE structure ABS_IMAGE works fine. In a previous version, I tried using a fixed-length byte array for ImageData, which sometimes crashed. This happened, I think, because the size of the image cannot be fixed. Now I'm trying to read an array of image bytes at a later time, when I can program the actual length of the array earlier. Here is the relevant code:

 ABS_Type_Defs.ABS_IMAGE img = (ABS_Type_Defs.ABS_IMAGE)Marshal.PtrToStructure( pImage, typeof(ABS_Type_Defs.ABS_IMAGE)); int length = ((int)img.Height - 1) * ((int)img.Width - 1); byte[] data = new byte[length]; Marshal.Copy(img.ImageData, data, 0, length); 

Now my problem is: every time I want to execute Marshal.Copy to read image bytes, I get an AccessViolationException .

Does anyone have any ideas?

+4
source share
1 answer

This is what happens. Your structure is the so-called variable-length structure. Pixel data is contained within a row in the structure, starting from offset to ImageData .

 typedef struct abs_image { ABS_DWORD Width; ABS_DWORD Height; ABS_DWORD ColorCount; ABS_DWORD HorizontalDPI; ABS_DWORD VerticalDPI; ABS_BYTE ImageData[ABS_VARLEN]; } ABS_IMAGE 

Your API returns pImage , which is an IntPtr that points to unmanaged data of type ABS_IMAGE . However, if you look at the native code, you will see that ABS_VARLEN is 1 . This is because struct must be defined statically at compile time. In fact, the pixel data will have a length determined by height, width, and color.

You can continue to use Marshal.PtrToStructure to get most of the fields. But you cannot get into the ImageData field this way. This will require a little more work.

Instead, declare the structure as follows:

 [StructLayout(LayoutKind.Sequential)] public struct ABS_IMAGE { public uint Width; public uint Height; public uint ColorCount; public uint HorizontalDPI; public uint VerticalDPI; public byte ImageData; } 

When you need to get image data, follow these steps:

 IntPtr ImageData = pImage + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData"); Marshal.Copy(ImageData, data, 0, length); 

If you have not yet entered .net 4, you need to perform a casting to compile arithmetic:

 IntPtr ImageData = (IntPtr) (pImage.ToInt64() + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData").ToInt64()); 

Finally, I think that you are not calculating length correctly. Of course you need to use Height*Width . Also, you did not specify the color depth. For example, a 32-bit color would be 4 bytes per pixel.

+5
source

Source: https://habr.com/ru/post/1414346/


All Articles