具有从 C# 到非托管驱动程序的嵌入式指针的编组结构

发布于 2024-08-10 08:21:01 字数 3742 浏览 4 评论 0原文

我正在尝试使用 P/Invoked DeviceIoControl() 调用将 C# (.NET Compact Framework 3.5) 与 Windows CE 6 R2 流驱动程序连接起来。对于其中一个 IOCTL 代码,驱动程序需要一个 DeviceIoControl 输入缓冲区,该缓冲区是以下包含嵌入指针的非托管结构:

typedef struct {
    DWORD address;
    const void* pBuffer;
    DWORD size; // buffer size
} IOCTL_TWL_WRITEREGS_IN;

我在 C# 中将该结构定义为:

[StructLayout(LayoutKind.Sequential)]
public struct IoctlWriteRegsIn
{
    public uint Address;
    public byte[] Buffer;
    public uint Size;
}

以及我的 P/Invoke 签名为:

[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool DeviceIoControl(IntPtr hDevice,
                                    UInt32 dwIoControlCode,
                                    ref IoctlWriteRegsIn lpInBuffer,
                                    UInt32 nInBufferSize,
                                    UInt32[] lpOutBuffer,
                                    UInt32 nOutBufferSize,
                                    ref UInt32 lpBytesReturned,
                                    IntPtr lpOverlapped);

但是,每当我调用 DeviceIoControl()在 C# 中,它始终返回 false,最后一个 Win32 错误为 ERROR_INVALID_PARAMETER。下面是驱动程序中 IOCTL switch 语句的源代码片段,用于处理 IOCTL 代码并对输入缓冲区进行错误检查,其中 inSize 是 nInBufferSize 参数:

    case IOCTL_TWL_WRITEREGS:
        if ((pInBuffer == NULL) || 
            (inSize < sizeof(IOCTL_TWL_WRITEREGS_IN)))
            {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
            }
        address = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->address;
        pBuffer = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->pBuffer;
        size = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->size;
        if (inSize < (sizeof(IOCTL_TWL_WRITEREGS_IN) + size))
            {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
            }
        rc = TWL_WriteRegs(context, address, pBuffer, size);

我尝试了应该通过驱动程序错误检查的硬编码大小,但没有成功,表明这是一个编组问题。我可能没有正确定义 C# 结构中的嵌入指针,或者我的 P/Invoke 签名错误。有什么想法吗?

提前致谢, Ben

作为参考,我可以从 C++ 与驱动程序交谈,不会出现这样的问题:

IOCTL_TWL_WRITEREGS_IN reg;
reg.address = 0x004B0014;
unsigned char data = 0xBE;
reg.pBuffer = &data;
reg.size = sizeof(char);

BOOL writeSuccess = DeviceIoControl(driver, IOCTL_TWL_WRITEREGS, &reg, sizeof(IOCTL_TWL_WRITEREGS_IN) + 1, NULL, 0, NULL, NULL);

更新:这就是有效的! 使用 JaredPar 的 IntPtr 建议并通过 SwDevMan81 的建议清理了我的 P/Invoke 签名:

    [StructLayout(LayoutKind.Sequential)]
    public struct IoctlWriteRegsIn
    {
        public uint Address;
        public IntPtr Buffer;
        public uint Size;
    }

    // elided

    byte regData = 0xFF;
    GCHandle pin = GCHandle.Alloc(regData, GCHandleType.Pinned);
    IoctlWriteRegsIn writeInBuffer = new IoctlWriteRegsIn{Address = twlBackupRegA, Buffer = pin.AddrOfPinnedObject(), Size = 1};
    bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, (uint) Marshal.SizeOf(writeInBuffer) + 1, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero);

    // P/Invoke signature
    [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool DeviceIoControl(IntPtr hDevice,
                                        UInt32 dwIoControlCode,
                                        ref IoctlWriteRegsIn lpInBuffer,
                                        UInt32 nInBufferSize,
                                        IntPtr lpOutBuffer,
                                        UInt32 nOutBufferSize,
                                        ref UInt32 lpBytesReturned,
                                        IntPtr lpOverlapped);

I'm trying to interface C# (.NET Compact Framework 3.5) with a Windows CE 6 R2 stream driver using P/Invoked DeviceIoControl() calls . For one of the IOCTL codes, the driver requires a DeviceIoControl input buffer that is the following unmanaged struct that contains an embedded pointer:

typedef struct {
    DWORD address;
    const void* pBuffer;
    DWORD size; // buffer size
} IOCTL_TWL_WRITEREGS_IN;

I defined the struct in C# as:

[StructLayout(LayoutKind.Sequential)]
public struct IoctlWriteRegsIn
{
    public uint Address;
    public byte[] Buffer;
    public uint Size;
}

and my P/Invoke signature as:

[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool DeviceIoControl(IntPtr hDevice,
                                    UInt32 dwIoControlCode,
                                    ref IoctlWriteRegsIn lpInBuffer,
                                    UInt32 nInBufferSize,
                                    UInt32[] lpOutBuffer,
                                    UInt32 nOutBufferSize,
                                    ref UInt32 lpBytesReturned,
                                    IntPtr lpOverlapped);

However, whenever I call DeviceIoControl() in C#, it always returns false, with a last Win32 error of ERROR_INVALID_PARAMETER. Here's a source code snippet from the IOCTL switch statement in the driver that handles the IOCTL code and does error checking on the input buffer, where inSize is the nInBufferSize parameter:

    case IOCTL_TWL_WRITEREGS:
        if ((pInBuffer == NULL) || 
            (inSize < sizeof(IOCTL_TWL_WRITEREGS_IN)))
            {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
            }
        address = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->address;
        pBuffer = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->pBuffer;
        size = ((IOCTL_TWL_WRITEREGS_IN*)pInBuffer)->size;
        if (inSize < (sizeof(IOCTL_TWL_WRITEREGS_IN) + size))
            {
            SetLastError(ERROR_INVALID_PARAMETER);
            break;
            }
        rc = TWL_WriteRegs(context, address, pBuffer, size);

I tried hard coding sizes that should pass the driver's error checking with no success, suggesting that it's a marshalling problem. I probably did not define the embedded pointer in the C# struct correctly or have my P/Invoke signature wrong. Any ideas?

Thanks in advance,
Ben

For reference, I can talk to the driver from C++ with no problems like this:

IOCTL_TWL_WRITEREGS_IN reg;
reg.address = 0x004B0014;
unsigned char data = 0xBE;
reg.pBuffer = &data;
reg.size = sizeof(char);

BOOL writeSuccess = DeviceIoControl(driver, IOCTL_TWL_WRITEREGS, ®, sizeof(IOCTL_TWL_WRITEREGS_IN) + 1, NULL, 0, NULL, NULL);

Update: here's what worked!
Used JaredPar's IntPtr suggestion and cleaned up my P/Invoke signature by SwDevMan81's suggestion:

    [StructLayout(LayoutKind.Sequential)]
    public struct IoctlWriteRegsIn
    {
        public uint Address;
        public IntPtr Buffer;
        public uint Size;
    }

    // elided

    byte regData = 0xFF;
    GCHandle pin = GCHandle.Alloc(regData, GCHandleType.Pinned);
    IoctlWriteRegsIn writeInBuffer = new IoctlWriteRegsIn{Address = twlBackupRegA, Buffer = pin.AddrOfPinnedObject(), Size = 1};
    bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, (uint) Marshal.SizeOf(writeInBuffer) + 1, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero);

    // P/Invoke signature
    [DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool DeviceIoControl(IntPtr hDevice,
                                        UInt32 dwIoControlCode,
                                        ref IoctlWriteRegsIn lpInBuffer,
                                        UInt32 nInBufferSize,
                                        IntPtr lpOutBuffer,
                                        UInt32 nOutBufferSize,
                                        ref UInt32 lpBytesReturned,
                                        IntPtr lpOverlapped);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一抹苦笑 2024-08-17 08:21:01

当编组具有内联指针的结构时,您需要将值定义为 IntPtr 而不是数组

[StructLayout(LayoutKind.Sequential)]
public struct IoctlWriteRegsIn
{
    public uint Address;
    public IntPtr Buffer;
    public uint Size;
}

When marshaling a struct which has an inline pointer, you need to define the value as an IntPtr and not an array

[StructLayout(LayoutKind.Sequential)]
public struct IoctlWriteRegsIn
{
    public uint Address;
    public IntPtr Buffer;
    public uint Size;
}
亣腦蒛氧 2024-08-17 08:21:01

通过用 IntPtr 替换 byte[] array 来尝试一下。

Give it a shot by replacing the byte[] array with an IntPtr..

回眸一遍 2024-08-17 08:21:01

您可能必须指定 byte[] 的大小(将 64 替换为实际大小)

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct IoctlWriteRegsIn
{    
   public uint Address; 
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
   public byte[] Buffer; 
   public uint Size;
}

然后您应该能够像这样设置大小:

IoctlWriteRegsIn io_struct = new IoctlWriteRegsIn();
io_struct.Address = 5;
io_struct.Buffer = new byte[1] { 0xBE };
// size of buffer, not struct
io_struct.Size = 1;//Marshal.SizeOf(io_struct); 

我将 P/Invoke 调用更改为:

[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool DeviceIoControl(IntPtr hDevice,  
   UInt32 dwIoControlCode,
   ref IoctlWriteRegsIn lpInBuffer, 
   UInt32 nInBufferSize,
   IntPtr lpOutBuffer,
   UInt32 nOutBufferSize,
   ref UInt32 lpBytesReturned,
   IntPtr lpOverlapped);

并使用以下方式调用它:

uint num_bytes = (uint)Marshal.SizeOf(writeInBuffer);
bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, num_bytes, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero);

You might have to specify the size of the byte[] (replace 64 with the actual size)

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct IoctlWriteRegsIn
{    
   public uint Address; 
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] 
   public byte[] Buffer; 
   public uint Size;
}

Then you should be able to set the size like this:

IoctlWriteRegsIn io_struct = new IoctlWriteRegsIn();
io_struct.Address = 5;
io_struct.Buffer = new byte[1] { 0xBE };
// size of buffer, not struct
io_struct.Size = 1;//Marshal.SizeOf(io_struct); 

I would change the P/Invoke call to this:

[DllImport("coredll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool DeviceIoControl(IntPtr hDevice,  
   UInt32 dwIoControlCode,
   ref IoctlWriteRegsIn lpInBuffer, 
   UInt32 nInBufferSize,
   IntPtr lpOutBuffer,
   UInt32 nOutBufferSize,
   ref UInt32 lpBytesReturned,
   IntPtr lpOverlapped);

and call it using this:

uint num_bytes = (uint)Marshal.SizeOf(writeInBuffer);
bool writeSuccess = DeviceIoControl(driverHandle, IoctlTwlWriteRegs, ref writeInBuffer, num_bytes, IntPtr.Zero, 0, ref numBytesReturned, IntPtr.Zero);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文