问题描述
我正在开发一个需要与 Video4Linux 抽象交互的应用程序.该应用程序使用 C# 开发,使用 mono 框架.
I'm developing an application which needs to interact with Video4Linux abstraction. The application is developed in C#, using the mono framework.
我面临的问题是我无法 P/Invoke ioctl
系统调用.或者,更准确地说,我可以 P/Invoke 它,但它会严重崩溃.
The problem I'm facing is that I can't P/Invoke the ioctl
system call. Or, more precisely, I can P/Invoke it, but it crashes badly.
extern 声明如下:
The extern declaration is the following:
[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/中声明的
._IOC
宏asm/ioctl.h
All the above code seems good. The class IoSpecification
is used for computing the I/O request code following the header specification (basically it follows the _IOC
macro declared at /usr/include/linux/asm/ioctl.h
.
data
参数是一个结构体,声明如下:
The data
parameter is a structure, declared as follow:
[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
中声明):
which should mimic the following structure (declared at /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 请求代码)时,调用已开始导致崩溃.
Before having the crash, there was a problem in the IOCTL request code computation, and the KernelIoCtrl
was working as expected (returning an errno
equals to EINVAL). When I corrected the bug (and indeed having the correct IOCTRL request code), the call has started to cause a crash.
总之,结构编组似乎存在问题,但我看不出它出了什么问题.
In conclusion, it seems there's a problem in the structure marshalling, but I cannot see what it's going wrong.
我担心问题出在变量参数列表上,因为 ioctl 例程声明如下(取自 man):
I fear that the problem is the variable argument list, because the ioctl routine is declared as follow (taken from man):
int ioctl(int d, int request, ...);
但我看到很多代码将上述例程声明为int ioctl(int d, int request, void*);
,我可以确保特定的IOCTRL请求只接受一个参数.
But I saw lot of code declaring the above routine as int ioctl(int d, int request, void*);
, and I can ensure that the specific IOCTRL request takes only one argument.
推荐答案
示例用法:
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);
}
}
这篇关于P/Invoke ioctl 系统调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!