I’m using an unmanaged library, which generates grayscale images (about 100×200 pixels, more or less). An image is contained within a struct, which looks like this in 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 my C# wrapper struct:
[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;
}
Grabbing the image and marshallign the ABS_IMAGE struct works just fine. In a previous version, I tried using a byte array with fixed length for ImageData, which crashed sometimes. This happened, I think, because the image size is not fix.
Now I try to read the image byte array at later time, when I can calulate the real array length before. Here 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: Every time I want to execute Marshal.Copy to read the image bytes, I get an AccessViolationException.
Does anyone have an idea?
This is what is happening. Your struct is what is known as a variable length struct. The pixel data is containined inline in the struct, starting at the offset to
ImageData.Your API returns
pImagewhich is anIntPtrthat points to unmanaged data of typeABS_IMAGE. However, if you look at the native code then you will see thatABS_VARLENis equal to1. This is because thestructhas to be defined statically at compile time. In reality the pixel data will have length determined by the height, width and colour count fields.You can carry on using
Marshal.PtrToStructureto get at most of the fields. But you can’t get at theImageDatafield that way. That’s going to need a little more work.Declare the struct like this instead:
When you need to get the image data do this:
If you are not yet on .net 4 then you need casting to make the arithmetic compile:
Finally, I think you are calculating
lengthincorrectly. Surely you need to useHeight*Width. Also you have not accounted for the color depth. For example, 32 bit color will be 4 bytes per pixel.