2012-06-10 51 views
3

我想Video4Linux的在我的管理應用程序集成。事實上,我已經宣佈了所有必需的結構和相對的ioctl。在這個問題上我提出了兩種ioctlSetFormatGetFormat;而前者運行良好(就像我實際使用的其他打法一樣),後者正在返回我糟糕的記憶行爲。C#聯盟結構編組站

GetFormat IOCTL是實際執行,但一旦應用程序被訪問到的ioctl參數(無論是調試器或我的應用程序本身),它總是與下面的堆棧崩潰:

System.NullReferenceException: ... 
at System.String.mempy4(...) in <filename unknown>:0 
at System.String.memcpy(...) in <filename unknown>:0 
at Derm.Platform.Video4Linux.ControlGetFormat(...) in <...> 
... 

我上過一些調查p/invoking ioctl,再次,我不明白爲什麼我的應用程序崩潰。我懷疑這是監守結構的內存佈局,但我無法解釋爲什麼SetFormat工作而GetFormat沒有(因爲它們使用了同樣的參數)。使用以下格式,

struct v4l2_format { 
     enum v4l2_buf_type type; 
     union { 
       struct v4l2_pix_format   pix;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ 
       struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ 
       struct v4l2_window    win;  /* V4L2_BUF_TYPE_VIDEO_OVERLAY */ 
       struct v4l2_vbi_format   vbi;  /* V4L2_BUF_TYPE_VBI_CAPTURE */ 
       struct v4l2_sliced_vbi_format sliced; /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */ 
       __u8 raw_data[200];     /* user-defined */ 
     } fmt; 
}; 

struct v4l2_pix_format { 
    __u32     width; 
    __u32     height; 
    __u32     pixelformat; 
    enum v4l2_field   field; 
    __u32     bytesperline; /* for padding, zero if unused */ 
    __u32     sizeimage; 
    enum v4l2_colorspace colorspace; 
    __u32     priv;   /* private data, depends on pixelformat */ 
}; 

我traduced結構v4l2_format

[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi)] 
public struct Format 
{ 
    public Format(BufferType bufferType) 
    { 
     Type = bufferType; 
     Pixmap = new PixmapFormat(); 
     RawData = new byte[RawDataLength]; 
    } 

    public Format(BufferType bufferType, PixmapFormat pixmapFormat) 
    { 
     Type = bufferType; 
     Pixmap = pixmapFormat; 
     RawData = new byte[RawDataLength]; 
    } 

    [FieldOffset(0)] 
    public BufferType Type; 

    [FieldOffset(4)] 
    public PixmapFormat Pixmap; 

    [FieldOffset(4)] 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=RawDataLength)] 
    public byte[] RawData; 

    public const int RawDataLength = 200; 
} 

[StructLayout(LayoutKind.Sequential, Pack = 4)] 
public struct PixmapFormat 
{ 
    public PixmapFormat(uint width, uint height, PixelFormatCode pixelFormat) 
    { 
     Width = width; 
     Height = height; 
     PixelFormat = pixelFormat; 
     Field = Video4Linux.BufferField.Any; 
     BytesPerLine = 0; 
     SizeImage = 0; 
     Colorspace = Video4Linux.PixelColorspace.None; 
     Private = 0; 
    } 

    public UInt32 Width; 

    public UInt32 Height; 

    public PixelFormatCode PixelFormat; 

    public BufferField Field; 

    public UInt32 BytesPerLine; 

    public UInt32 SizeImage; 

    public PixelColorspace Colorspace; 

    public UInt32 Private; 
} 

最後,在這裏:

我要的P/Invoke一個ioctl常規接收/返回結構v4l2_format是調用ioctl的方法:

public static void ControlGetFormat(IntPtr fd, BufferType pixmapType, out PixmapFormat pixmapFormat) 
{ 
    if (fd == IntPtr.Zero) 
     throw new ArgumentException("invalid file descriptor", "fd"); 

    Format format = new Format(pixmapType); 

    int result = IoCtrlGetFormat(fd, GetFormat.ControlCode, ref format); 

    if (result < 0) 
     ThrowExceptionOnError(); 

    pixmapFormat = format.Pixmap; 
} 

private static readonly IoCtrlRequest GetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 4, typeof(Format)); 

[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] 
private static extern int IoCtrlGetFormat(IntPtr fd, Int32 code, ref Format argument); 

public static void ControlSetFormat(IntPtr fd, BufferType pixmapType, ref PixmapFormat pixmapFormat) 
{ 
    if (fd == IntPtr.Zero) 
     throw new ArgumentException("invalid file descriptor", "fd"); 

    PixmapFormat pixmapFormatCopy = pixmapFormat; 
    Format format = new Format(pixmapType, pixmapFormatCopy); 

    int result = IoCtrlSetFormat(fd, SetFormat.ControlCode, ref format); 

    if (result < 0) 
     ThrowExceptionOnError(); 

    pixmapFormat = format.Pixmap; 
} 

private static readonly IoCtrlRequest SetFormat = new IoCtrlRequest(IoCtrlDirection.Read | IoCtrlDirection.Write, 'V', 5, typeof(Format)); 

[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] 
private static extern int IoCtrlSetFormat(IntPtr fd, Int32 code, ref Format argument); 
+1

我不認爲當領域之一是一個對象,具有明確工會可以工作。我會嘗試使用順序佈局,只聲明數組字段。然後使用Marshal類讀取/寫入其他字段。 –

+0

除了數組之外,一切都是結構。是否有可能強制結構大小,排除數組字段? – Luca

+0

我很想將v4l結構包裝成更簡單的方法,並且有非託管函數來在它們之間進行轉換 – IanNorton

回答

4

問題已經通過迫使結構尺寸與實際(,在我的情況)解決,並刪除數組字段RawData(由David Heffernan所建議的)。

事實上:

[StructLayout(LayoutKind.Explicit, Pack = 4, CharSet = CharSet.Ansi, Size = 204)] 
public struct Format 
{ 
    public Format(BufferType bufferType) 
    { 
     Type = bufferType; 
     Pixmap = new PixmapFormat(); 
    } 

    public Format(BufferType bufferType, PixmapFormat pixmapFormat) 
    { 
     Type = bufferType; 
     Pixmap = pixmapFormat; 
    } 

    [FieldOffset(0)] 
    public BufferType Type; 

    [FieldOffset(4)] 
    public PixmapFormat Pixmap; 
} 

當然,Marshal.SizeOf(typeof(Format)) == 204