如何初始化C#中的不安全指针并将其转换为byte []?

时间:2010-12-09 14:37:34

标签: c# pointers embedded arm

我昨天发了一篇文章,如何创建最初用C ++编写的C#结构。

感谢您的回复。

我正在努力,在运行WinCE 6.0和.NET Compact框架2.0的ARM平台上使用DeviceIOControl,但是我试图实现的是控制端口引脚,这被证明是一场噩梦。 / p>

以下是PInvoke声明:

[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(int hDevice,
                                             int dwIoControlCode,
                                             byte[] lpInBuffer,
                                             int nInBufferSize,
                                             byte[] lpOutBuffer,
                                             int nOutBufferSize,
                                             ref int lpBytesReturned,
                                             IntPtr lpOverlapped);

PInvoke声明表明可以简单地将byte []传递给它。当然,将值写入结构的每个成员,将其转换为字节数组并将其传递给dll是一件容易的事。

我有以下内容:

    [StructLayout(LayoutKind.Sequential)]  
    public struct pio_desc 
    {
        unsafe byte* pin_name;      //Length???
        public uint pin_number;     //4 bytes
        public uint default_value;  //4 bytes
        public byte attribute;      //1 byte
        public uint pio_type;       //4 bytes
    }

pio_desc PA13 =新pio_desc();

现在肯定是做了类似的事情:

PA13.pin_number = AT91_PIN_PA13;      //Length 4 bytes
PA13.default_value = 0;               //Length 4 bytes
PA13.attribtue = PIO_DEFAULT;         //Length 1 byte
PA13.pio_type = PIO_OUTPUT;           //Length 4 bytes

并将(例如pin_number)转换为byte []:

byte[] temp = BitConverter.GetBytes(PA13.pin_number); //uints are 4 bytes wide
        byteArray[++NumberOfChars] = temp[0];
        byteArray[++NumberOfChars] = temp[1];
        byteArray[++NumberOfChars] = temp[2];
        byteArray[++NumberOfChars] = temp[3];     //Will need to check on Endianess

问题:

在结构PA13中,如何初始化不安全指针pin_name?该司机的作者指出,这可能是他的司机使用的。 Windows是否需要这个值?

PA13.pin_name = ??????

然后,如何将此指针转换为适合我的byte []数组的字节以传递给DeviceIOControl?

我对改变端口引脚电压的难度感到非常失望和沮丧 - 我几天来一直在努力解决这个问题。因为我来自硬件背景,所以我认为在另一个控制器上实现IO控制并通过COM端口将控制数据传递给它会更容易(并且更少资格)。

再次感谢任何(简单)帮助。

3 个答案:

答案 0 :(得分:1)

你需要在这里做一些不同的事情。首先,替换此成员:

unsafe byte* pin_name;      //Length???

使用:

[MarshalAs(UnmanagedType.LPStr)] public string pin_name;

然后将P / Invoke声明中的输入/输出缓冲区从byte[]替换为IntPtr。然后,您可以使用此代码转换数据:

pio_desc PA13;
// Set the members of PA13...

IntPtr ptr = IntPtr.Zero;
try {
    var size = Marshal.SizeOf(PA13);
    ptr = Marshal.AllocHGlobal(size);

    Marshal.StructureToPtr(PA13, ptr, false);

    // Your P/Invoke call goes here.
    // size will be the "nInBufferSize" argument
    // ptr will be the "lpInBuffer" argument
} finally {
    if (ptr != IntPtr.Zero) {
        Marshal.DestroyStructure(ptr, typeof(pio_desc));
        Marshal.FreeHGlobal(ptr);
    }
}

答案 1 :(得分:1)

通过对[DllImport]声明的说明,你可以更轻松地做到这一点。只需将lpInBuffer参数声明为结构类型,pinvoke marshaller无论如何都会将其转换为指针。因此:

[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool SetOutputPin(IntPtr hDevice,
                                             int dwIoControlCode,
                                             ref pio_desc lpInBuffer,
                                             int nInBufferSize,
                                             IntPtr lpOutBuffer,
                                             int nOutBufferSize,
                                             out int lpBytesReturned,
                                             IntPtr lpOverlapped);

将IntPtr用于lpOutBuffer,因为驱动程序可能不会返回任何内容。通过IntPtr.Zero。与结构相同的想法。如果未使用该字段,则只需将其声明为IntPtr:

[StructLayout(LayoutKind.Sequential)]  
public struct pio_desc 
{
    public IntPtr pin_name;     // Leave at IntPtr.Zero
    public uint pin_number;     //4 bytes
    public uint default_value;  //4 bytes
    public byte attribute;      //1 byte
    public uint pio_type;       //4 bytes
}

注意Packing属性,因为字节大小的字段,所以它在这里有所不同。你可能需要1但这只是一个猜测而不知道任何关于驱动程序。如果你有C代码,那么测试sizeof(pio_desc)的值并与Marshal.SizeOf()进行比较。将Marshal.SizeOf(typeof(pio_desc))作为nInBufferSize参数传递。如果您已经发布了C声明,那么这将更容易准确回答。

答案 2 :(得分:0)

将lpInBuffer和lpOutBuffer声明为IntPtr。使用Marshal.AllocHGlobal初始化它们(不要忘记最后将它们与Marshal.FreeHGlobal一起发布)。填充这些缓冲区并使用不同的Marshal.Copy重载读取它。