使用C#通过Marshal.Copy从IntPtr复制字节数组不起作用

5

我正在使用一个非托管库,它生成灰度图像(大约100x200像素左右)。一个图像包含在一个结构体中,在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;

以下是我的C#包装结构:

[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;
}

抓取图像和调用ABS_IMAGE结构体没有问题。在之前的版本中,我尝试使用固定长度的字节数组,但有时会崩溃。我认为这是因为图像大小不是固定的。 现在我尝试在稍后读取图像字节数组,当我可以计算出真实的数组长度时。下面是相关的代码:

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);

现在我的问题是:每次我想使用Marshal.Copy读取图像字节时,都会出现“AccessViolationException”的错误。有人有什么想法吗?
1个回答

5
这就是发生的事情。您的结构体是所谓的可变长度结构体。像素数据在结构体中以内联方式包含,从偏移量 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

你的API返回一个名为pImageIntPtr,它指向类型为ABS_IMAGE的非托管数据。然而,如果查看本机代码,你会发现ABS_VARLEN等于1。这是因为struct必须在编译时静态定义。实际上,像素数据的长度将由高度、宽度和颜色计数字段确定。
你仍然可以继续使用Marshal.PtrToStructure来获取大多数字段。但是你无法通过这种方式访问ImageData字段。这需要做更多的工作。
改为声明以下结构体:
[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;
}

当您需要获取图像数据时,请执行以下操作:
IntPtr ImageData = pImage + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData");
Marshal.Copy(ImageData, data, 0, length);

如果您还没有使用 .net 4 版本,则需要进行强制转换才能使算术编译通过:

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

最后,我认为你计算length的方式是错误的。你需要使用Height*Width。此外,你没有考虑颜色深度。例如,32位颜色将每个像素占用4个字节。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接