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

mrs*_*oof 4 .net c# pinvoke marshalling

我正在使用一个非托管库,它可以生成灰度图像(大约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
Run Code Online (Sandbox Code Playgroud)
typedef unsigned int     ABS_DWORD;
typedef unsigned char     ABS_BYTE;
Run Code Online (Sandbox Code Playgroud)

在这里我的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;
}
Run Code Online (Sandbox Code Playgroud)

抓取图像并对ABS_IMAGE结构进行marshallign 工作就好了.在以前的版本中,我尝试使用固定长度的字节数组用于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);
Run Code Online (Sandbox Code Playgroud)

现在我的问题:每次我想执行Marshal.Copy来读取图像字节,我得到一个AccessViolationException.

有没有人有想法?

Dav*_*nan 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
Run Code Online (Sandbox Code Playgroud)

您的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;
}
Run Code Online (Sandbox Code Playgroud)

当您需要获取图像数据时,请执行以下操作:

IntPtr ImageData = pImage + Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData");
Marshal.Copy(ImageData, data, 0, length);
Run Code Online (Sandbox Code Playgroud)

如果您尚未使用.net 4,则需要进行转换以进行算术编译:

IntPtr ImageData = (IntPtr) (pImage.ToInt64() + 
    Marshal.OffsetOf(typeof(ABS_IMAGE), "ImageData").ToInt64());
Run Code Online (Sandbox Code Playgroud)

最后,我认为你的计算length不正确.当然你需要使用Height*Width.你还没有考虑颜色深度.例如,32位颜色将是每像素4个字节.