我正在开发一个需要与Video4Linux抽象交互的应用程序。该应用程序使用mono框架以C#开发。
我面临的问题是我无法P /调用ioctl系统调用。或者,更准确地说,我可以P /调用它,但是它崩溃严重。
ioctl
extern声明如下:
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] private extern static int KernelIoCtrl(int fd, int request, IntPtr data);
到目前为止,一切都很好。
使用的实际例程KernelIoCtrl如下:
KernelIoCtrl
protected virtual int Control(IoSpecification request, object data) { GCHandle dataHandle; IntPtr dataPointer = IntPtr.Zero; try { // Pin I/O control data if (data != null) { dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); dataPointer = dataHandle.AddrOfPinnedObject(); } // Perform I/O control int result = KernelIoCtrl(mFileDescriptor, request.RequestCode, dataPointer); int errno = Marshal.GetLastWin32Error(); // Throw exception on errors if (errno != (int)ErrNumber.NoError) throw new System.ComponentModel.Win32Exception(errno); return (result); } finally { if (dataPointer != IntPtr.Zero) dataHandle.Free(); } }
以上所有代码似乎都不错。该类IoSpecification用于按照标头规范计算I / O请求代码(基本上,它遵循处_IOC声明的宏)/usr/include/linux/asm/ioctl.h。
IoSpecification
_IOC
/usr/include/linux/asm/ioctl.h
该data参数是一个结构,声明如下:
data
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct Capability { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string Driver; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string Device; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string BusInfo; public UInt32 Version; public CapabilityFlags Capabilities; [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)] public UInt32[] Reserved; }
应该模仿以下结构(在处声明/usr/include/linux/videodev2.h):
/usr/include/linux/videodev2.h
struct v4l2_capability { __u8 driver[16]; /* i.e. "bttv" */ __u8 card[32]; /* i.e. "Hauppauge WinTV" */ __u8 bus_info[32]; /* "PCI:" + pci_name(pci_dev) */ __u32 version; /* should use KERNEL_VERSION() */ __u32 capabilities; /* Device capabilities */ __u32 reserved[4]; };
在发生崩溃之前,IOCTL请求代码计算存在问题,并且KernelIoCtrl按预期方式工作(返回errno等于 EINVAL )。当我更正了该错误(并确实具有正确的IOCTRL请求代码)后,该调用已开始导致崩溃。
errno
总而言之,似乎在结构编组中存在问题,但是我看不到它出了什么问题。
我担心问题出在变量参数列表中,因为 ioctl 例程被声明为follow(从man中获取):
int ioctl(int d, int request, ...);
但是我看到很多代码将上述例程声明为int ioctl(int d, int request, void*);,并且可以确保特定的IOCTRL请求仅接受一个参数。
int ioctl(int d, int request, void*);
用法示例:
Capability capability; if (UnsafeNativeMethods.Ioctl(handle, request, ref capability) == -1) { throw new UnixIOException(); }
能力:
[StructLayout(LayoutKind.Sequential, Size = 104)] internal struct Capability { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] public string Driver; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string Device; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string BusInfo; public uint Version; public CapabilityFlags Capabilities; }
UnsafeNativeMethods:
internal static class UnsafeNativeMethods { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] [DllImport("libc", EntryPoint = "close", SetLastError = true)] internal static extern int Close(IntPtr handle); [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] internal static extern int Ioctl(SafeUnixHandle handle, uint request, ref Capability capability); [DllImport("libc", EntryPoint = "open", SetLastError = true)] internal static extern SafeUnixHandle Open(string path, uint flag, int mode); internal static string Strerror(int error) { try { var buffer = new StringBuilder(256); var result = Strerror(error, buffer, (ulong)buffer.Capacity); return (result != -1) ? buffer.ToString() : null; } catch (EntryPointNotFoundException) { return null; } } [DllImport("MonoPosixHelper", EntryPoint = "Mono_Posix_Syscall_strerror_r", SetLastError = true)] private static extern int Strerror(int error, [Out] StringBuilder buffer, ulong length); }
SafeUnixHandle:
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] internal sealed class SafeUnixHandle : SafeHandle { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] private SafeUnixHandle() : base(new IntPtr(-1), true) { } public override bool IsInvalid { get { return this.handle == new IntPtr(-1); } } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected override bool ReleaseHandle() { return UnsafeNativeMethods.Close(this.handle) != -1; } }
UnixIOException:
[Serializable] public class UnixIOException : ExternalException { private readonly int nativeErrorCode; [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] public UnixIOException() : this(Marshal.GetLastWin32Error()) { } [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] public UnixIOException(int error) : this(error, GetErrorMessage(error)) { } [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] public UnixIOException(string message) : this(Marshal.GetLastWin32Error(), message) { } public UnixIOException(int error, string message) : base(message) { this.nativeErrorCode = error; } public UnixIOException(string message, Exception innerException) : base(message, innerException) { } protected UnixIOException(SerializationInfo info, StreamingContext context) : base(info, context) { this.nativeErrorCode = info.GetInt32("NativeErrorCode"); } public int NativeErrorCode { get { return this.nativeErrorCode; } } public override void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) { throw new ArgumentNullException("info"); } info.AddValue("NativeErrorCode", this.nativeErrorCode); base.GetObjectData(info, context); } private static string GetErrorMessage(int error) { var errorDescription = UnsafeNativeMethods.Strerror(error); return errorDescription ?? string.Format("Unknown error (0x{0:x})", error); } }