c# - P/Invoke ioctl 系统调用

标签 c# linux mono pinvoke

我正在开发一个需要与 Video4Linux 抽象交互的应用程序。该应用程序是在 C# 中开发的,使用的是 mono 框架。

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

外部声明如下:

[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 类用于计算 header 规范之后的 I/O 请求代码(基本上它遵循在 /usr/include/linux/声明的 _IOC 宏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 请求代码)时,调用开始导致崩溃。

总之,结构编码似乎有问题,但我看不出问题出在哪里。

我担心问题是可变参数列表,因为 ioctl 例程声明如下(取自 man):

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

但我看到很多代码将上述例程声明为 int ioctl(int d, int request, void*);,我可以确保特定的 IOCTRL 请求只接受一个参数。

最佳答案

示例用法:

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

安全Unix句柄:

[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);
    }
}

关于c# - P/Invoke ioctl 系统调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10387603/

相关文章:

c# - 这段 Winform C# 代码有什么问题?它在 Windows 中运行良好,但在 mono(Linux) 中,将抛出 NullReferenceException

c++ - 带有接收器的 boost_log 示例无法编译

c# - 为什么具有默认值枚举参数的泛型类的构造函数无法调用该类的 protected 方法?

C#基础多线程问题: Call Method on Thread A from Thread B (Thread B started from Thread A)

c# - 将带有 std::vector 的 C++ 结构映射到 C#

python - 在 nvidia jetson tx2 上安装 tensorflow 的问题

python - Linux 下的 Python 中未读取输入

asp.net - 单声道与 SQL Server 成员资格提供程序?

c# - Mono for Android 库中的 Resources 文件夹

c# - 如何等到对话框真正关闭