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

まだ.net4を使用していない場合は、算術コンパイルを行うためにキャストする必要があります。

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

length最後に、あなたは間違って計算していると思います。確かにを使用する必要がありますHeight*Width。また、あなたは色深度を考慮していません。たとえば、32ビットカラーはピクセルあたり4バイトになります。

于 2012-05-25T10:20:01.780 に答える