2012-04-30 7 views
1

私はVideo4Linux抽象化と対話する必要があるアプリケーションを開発しています。このアプリケーションは、モノフレームワークを使用してC#で開発されています。P/Invoke ioctlシステムコール

私が直面している問題は、ioctlシステムコールをP/Invokeできないことです。または、より正確には、私はP/Invokeすることができますが、それはひどくクラッシュします。

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

これまでのところは良い:

extern宣言は次のようです。

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で宣言以下

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要求コードを持っているとき)、コールはクラッシュを引き起こし始めました。

結論としては、マーシャリングの構造に問題があるようですが、何がうまくいかないのか分かりません。

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

しかし、私はint ioctl(int d, int request, void*);上記のようなルーチンを宣言するコードをたくさん見ました:のioctlルーチンは、次のように宣言されているため

は、私は(男から取られた)、問題は可変引数リストであることを恐れます特定のIOCTRL要求が1つの引数しか取らないようにすることができます。

+0

私はしばらく前に似た何かをしたとのioctlを使用するたびに異なるメソッドの宣言になってしまいました。 'private extern static int KernelIoCtrl(int fd、int request、refケイパビリティ能力);'これはあなたがピン止めをするのを止めるはずです。それでもクラッシュした場合は、構造体宣言が間違っています。 – dtb

+0

@dtbうわー...うまくいった! externsの束を宣言することなく、複数のIOCTL要求を処理する一般的な方法はありますか?そうする方法がなければなりません! – Luca

+0

それを動作させる方法があるかもしれませんが、私はexternsの束を宣言するのが簡単で、はるかに信頼性が高いと思います。 – 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中に構造体のメモリが固定されていないため、動作しない恐れがあります。 それ以外にも、私はほとんどすべてを試しました。つまり、unsigned extern(void * argを使って)、3番目の引数を(型付きのデリゲートとして)宣言します。* __ arglist *は可変引数の宣言です。すべてがうまくいきませんでした。 引数のメモリポインタを固定している間は、最後のtry(__arglist)が正しいと思います。残念ながら* mono *は正しく処理できません(ただし、* printf *ルーチンをサポートしています)。 – Luca

+0

私はこれが古いと知っていますが、 'CapabilityFlags'プロパティは何ですか? – Andy