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结构工作得很好。在以前的版本中,我尝试对 ImageData 使用固定长度的字节数组,但有时会崩溃。这发生了,我认为,因为图像大小不固定。现在我尝试稍后读取图像字节数组,当我可以计算之前的实际数组长度时。这里是相关代码:

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.

有人有想法吗?

4

1 回答 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 返回指向非托管数据类型的pImage一个。但是,如果您查看本机代码,您将看到等于. 这是因为必须在编译时静态定义。实际上,像素数据的长度由高度、宽度和颜色计数字段决定。IntPtrABS_IMAGEABS_VARLEN1struct

您可以继续使用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 个字节。

于 2012-05-25T10:20:01.780 回答