I'm using an unmanaged library, which generates grayscale images (about 100x200 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
.
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 then you will see that ABS_VARLEN
is equal to 1
. This is because the struct
has 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.PtrToStructure
to get at most of the fields. But you can't get at the ImageData
field that way. That's going to need a little more work.
Declare the struct like this instead:
[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 the image data do this:
IntPtr ImageData = pImage + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData");
Marshal.Copy(ImageData, data, 0, length);
If you are not yet on .net 4 then you need casting to make the arithmetic compile:
IntPtr ImageData = (IntPtr) (pImage.ToInt64() +
Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData").ToInt64());
Finally, I think you are calculating length
incorrectly. Surely you need to use Height*Width
. Also you have not accounted for the color depth. For example, 32 bit color will be 4 bytes per pixel.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With