2012-04-30 31 views
1

我正在开发一个需要与Video4Linux抽象交互的应用程序。该应用程序是使用单一框架在C#中开发的。P /调用ioctl系统调用

我面临的问题是我无法P/Invoke ioctl系统调用。或者,更确切地说,我可以P /调用它,但它崩溃严重。

的extern声明如下:

[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] 
private extern static int KernelIoCtrl(int fd, int request, IntPtr data); 

到目前为止好。

使用该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请求的代码的报头规范以下(基本上它遵循在/usr/include/linux/asm/ioctl.h宣布_IOC

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声明):

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请求代码)时,调用已开始导致崩溃。

总之,看来结构编组存在问题,但我看不出它出错了。

我担心的问题是可变的参数列表,因为IOCTL例行声明如下(从人获得):

int ioctl(int d, int request, ...); 

但是我看到很多代码,宣布根据上述程序为int ioctl(int d, int request, void*); ,并且我可以确保特定的IOCTRL请求只接受一个参数。

+0

我没有类似的东西而回,并结束了每次使用的ioctl不同的方法声明。尝试:'私人extern静态int KernelIoCtrl(int fd,诠释请求,参考能力);'这应该让你摆脱钉住的东西。如果它仍然崩溃,那么你的结构声明是错误的。 – dtb

+0

@dtb哇...它的工作!有没有一种通用的方式来处理多个IOCTL请求,而无需声明一堆extern?必须有办法做到这一点! – Luca

+0

也许有一些方法可以使它工作,但是我猜想宣布一堆extern更容易,更可靠。 – dtb

回答

3

实例:

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); 
    } 
} 
+0

您的建议已经奏效,但我担心它不能工作,因为P/Invoking时结构内存不会被固定。除此之外,我尝试了几乎所有的东西:不安全的外部(使用void * arg),声明第三个参数(作为类型化委托),使用* __ arglist *因为可变参数声明;一切都没有奏效。 我认为最后一次尝试(__arglist)是正确的,而固定参数内存指针。可悲*单声道*不能正确处理它(但它支持* printf *例程!)。 – Luca

+0

我知道这是旧的,但什么是'CapabilityFlags'属性? – Andy