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

时间:2009-11-05 15:59:53

标签: c# struct pinvoke pointers marshalling

我正在尝试使用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);

但是,每当我在C#中调用DeviceIoControl()时,它总是返回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#struct中的嵌入指针或者我的P / Invoke签名错误。有什么想法吗?

提前致谢, 本

作为参考,我可以用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);

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

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

答案 2 :(得分:0)

您可能必须指定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);