Passing a struct with a byte array inside to a int

2020-05-03 11:20发布

问题:

I have a situation where I have to pass a struct to a C method (declared as extern in my C# file).

This struct however is quite complicated. I already used successfully the approach with AllocHGlobal, but I would like to understand if is possible to make it works in this way, by only passing a reference to the struct.

[StructLayout(LayoutKind.Sequential)]
struct lgLcdBitmapHeader
{
    public Formats Format;
}

[StructLayout(LayoutKind.Explicit)]
struct lgLcdBitmap
{
    [FieldOffset(0)]
    public lgLcdBitmapHeader hdr;
    [FieldOffset(0)]
    public lgLcdBitmap160x43x1 bmp_mono;
    [FieldOffset(0)]
    public lgLcdBitmapQVGAx32 bmp_qvga32;
}

[StructLayout(LayoutKind.Sequential)]
struct lgLcdBitmap160x43x1 : IDisposable
{
    /// <summary>
    /// Format = LGLCD_BMP_FORMAT_160x43x1
    /// </summary>
    public lgLcdBitmapHeader hdr;
    /// <summary>
    /// byte array of size LGLCD_BMP_WIDTH * LGLCD_BMP_HEIGHT, use AllocHGlobal to make code safe
    /// </summary>
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)BWBitmapSizes.Size)]
    internal byte[] pixels;
}

[StructLayout(LayoutKind.Sequential)]
struct lgLcdBitmapQVGAx32 : IDisposable
{
    /// <summary>
    /// Format = LGLCD_BMP_FORMAT_160x43x1
    /// </summary>
    public lgLcdBitmapHeader hdr;
    /// <summary>
    /// byte array of size LGLCD_QVGA_BMP_WIDTH * LGLCD_QVGA_BMP_HEIGHT * LGLCD_QVGA_BMP_BPP, use AllocHGlobal to make code safe
    /// </summary>
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)QVGABitmapSizes.Size)]
    internal byte[] pixels;
}

The biggest problem is that I have to wrap everything in a union (the lgLcdBitmap struct). Actually C# is complaning because there is an object at offset 0 that is not correctly aligned or overlaps another object.

I think that the problem is connected with the fact that my byte array doesn't have a real size (only the one declared with SizeConst). Because I don't want to use fixed (it forces me to use unsafe code), I would like to understand how can I solve this.

The only idea that I can think about is declaring Size (inside StructLayout) to allow C# understand what size is my struct, but I'm not sure it will works.

The method I have to call is this one:

public extern static uint lgLcdUpdateBitmap([In] int device, [In] ref lgLcdBitmap bitmap, [In] Priorities priority);

Any suggestion on how make everything works?

回答1:

Since you don't want to use fixed, your byte arrays are best declared as IntPtr, allocated with AllocHGlobal and filled with data using Marshal.Copy. There's no way to get the P/invoke marshaller to do what you need using MarshalAs.



回答2:

If you don't need the union as such in managed code, don't make it a union. Declare two structs and two overloads of your p/invoke function.