C#联盟结构编组

我正在尝试将Video4Linux集成到我的托管应用程序中。 事实上,我已经宣布了所有必需的结构和相对的ioctl 。 在这个问题中,我提出了两个ioctlSetFormatGetFormat ; 虽然前者工作得很好(就像我实际使用的那样),但后者却让我记忆力不好。

GetFormat ioctl实际上正在执行,但是一旦应用程序访问ioctl参数(调试器或我的应用程序本身),它总是会崩溃以下堆栈:

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

我对p /调用ioctl进行了一些调查,再一次,我无法理解为什么我的应用程序崩溃了。 我怀疑它是由于结构内存布局,但我无法解释为什么SetFormat工作而GetFormat不工作(因为他们使用相同的参数)。

我必须P / Invoke接收/返回结构v4l2_formatioctl例程:

 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 */ }; 

我使用下面的表格来描述结构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; } 

最后,这是调用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); 

通过将结构大小强制为实际大小(在我的情况下为204 )解决了问题,并删除了数组字段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