摆脱不安全的代码,从字节编组uint数组?

时间:2015-02-10 05:23:54

标签: c# struct marshalling

我正在使用其他人编写的项目来接收来自Parrot AR Drone的一些数据。很多数据以字节数组形式出现,这个库我使用一堆结构来解析。一般来说,我对编组很新。

我有一个看起来像这样的结构:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public unsafe struct navdata_vision_detect_t
{
    public ushort tag;
    public ushort size;
    public uint nb_detected;
    public fixed uint type [4]; // <Ctype "c_uint32 * 4">
    public fixed uint xc [4]; // <Ctype "c_uint32 * 4">
    public fixed uint yc [4]; // <Ctype "c_uint32 * 4">
    public fixed uint width [4]; // <Ctype "c_uint32 * 4">
    public fixed uint height [4]; // <Ctype "c_uint32 * 4">
    public fixed uint dist [4]; // <Ctype "c_uint32 * 4">
    public fixed float orientation_angle [4]; // <Ctype "float32_t * 4">
}

但是,如果我尝试访问navdata_vision_detect_t的实例并获得固定的uint值,我必须使用&#34; fixed&#34;关键字,它似乎非常混乱:

unsafe private void drawTagDetection()
{
    int x, y;
    if (_detectData.nb_detected > 0)
    {
         fixed (uint* xc = _detectData.xc)
         {
             x = (int)xc[0];
         }
         fixed (uint* yc = _detectData.yc)
         {
             y = (int)yc[0];
         }
}

我希望能够像普通的c#数组一样访问uint数组。我想我应该可以使用编组,但我无法使用它。我试过像:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public uint[] type; // <Ctype "c_uint32 * 4">

让我删除&#34;不安全&#34;和#34;固定&#34;关键字,但引起了另一个问题,因为在解析字节数据时,有一个大的switch语句,它会对这样的各种结构进行一些转换:

private static unsafe void ProcessOption(navdata_option_t* option, ref NavdataBag navigationData){
        var tag = (navdata_tag_t) option->tag;
        switch (tag)
        {
        //lots of other stuff here
        case navdata_tag_t.NAVDATA_VISION_TAG:
               navigationData.vision = *(navdata_vision_t*) option;
               break;
        }
}

所以我仍然需要在另一个不安全的函数中有一些指向这个结构的指针。如何让这些结构中的数组成为安全的#34;还是允许另一个不安全的函数将我的对象转换为struct?

感谢您提供任何帮助!

1 个答案:

答案 0 :(得分:0)

首先,编组为UnmanagedType.ByValArray不起作用:我们有指针而不是值。 建议的union仿真也将失败:数组是C#中的引用类型,使用[FieldOffset(x)]将无法按预期工作。

我认为你可以这样做:

1)将你的结构转换为像这样的指针结构:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct _navdata_vision_detect_t
{
    public ushort tag;
    public ushort size;
    public uint nb_detected;
    public IntPtr type; // <Ctype "c_uint32 * 4">
    public IntPtr xc; // <Ctype "c_uint32 * 4">
    public IntPtr yc; // <Ctype "c_uint32 * 4">
    public IntPtr width; // <Ctype "c_uint32 * 4
    public IntPtr height; // <Ctype "c_uint32 * 4">
    public IntPtr dist; // <Ctype "c_uint32 * 4">
    public IntPtr orientation_angle; // <Ctype "float32_t * 4">
}

2)围绕这个结构包装一个类。该类应包含上述结构作为成员。它还应该以安全的方式反映所有结构成员&#34;。

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public class navdata_vision_detect_t
{
    public ushort tag;
    public ushort size;
    public uint nb_detected;
    public uint[] type; // <Ctype "c_uint32 * 4">
    public uint[] xc; // <Ctype "c_uint32 * 4">
    public uint[] yc; // <Ctype "c_uint32 * 4">
    public uint[] width; // <Ctype "c_uint32 * 4
    public uint[] height; // <Ctype "c_uint32 * 4">
    public uint[] dist; // <Ctype "c_uint32 * 4">
    public float[] orientation_angle; // <Ctype "float32_t * 4">

    internal _navdata_vision_detect_t data;
}

3)现在您需要一个自定义编组器来手动将数据从指向数据传输到实际数组。请记住(不幸的是)你不能在结构上使用自定义编组,只能在P / Invoke调用上使用。 您的自定义Marshaller可能如下所示:

public class myCustomMarshaler : ICustomMarshaler
{
    [ThreadStatic]
    private navdata_vision_detect_t marshaledObj;

    private static myCustomMarshaler marshaler = null;
    public static ICustomMarshaler GetInstance(string cookie)
    {
        if (marshaler == null)
        {
            marshaler = new myCustomMarshaler();
        }
        return marshaler;
    }

    public int GetNativeDataSize()
    {
        return Marshal.SizeOf(typeof(_navdata_vision_detect_t));
    }

    public System.IntPtr MarshalManagedToNative(object managedObj)
    {
        if (!(managedObj is navdata_vision_detect_t))
        {
            throw new ArgumentException("Specified object is not a navdata_vision_detect_t object.", "managedObj");
        }
        else
        {
            this.marshaledObj = (navdata_vision_detect_t)managedObj;
        }
        IntPtr ptr = Marshal.AllocHGlobal(this.GetNativeDataSize());
        if (ptr == IntPtr.Zero)
        {
            throw new Exception("Unable to allocate memory to.");
        }
        Marshal.StructureToPtr(this.marshaledObj.data, ptr, false);
        return ptr;
    }
    public object MarshalNativeToManaged(System.IntPtr pNativeData)
    {
        marshaledObj.tag = marshaledObj.data.tag;
        marshaledObj.size = marshaledObj.data.size;
        marshaledObj.nb_detected = marshaledObj.data.nb_detected;

        for (int i=0; i<3; i++)
        {
            Int32 _type = Marshal.ReadInt32(this.marshaledObj.data.type, i * sizeof(Int32));
            this.marshaledObj.type[i] = Convert.ToUInt32(_type)
            Int32 _xc = Marshal.ReadInt32(this.marshaledObj.data.xc, i * sizeof(Int32));
            this.marshaledObj.xc[i] = Convert.ToUInt32(_xc)
            Int32 _yc = Marshal.ReadInt32(this.marshaledObj.data.yc, i * sizeof(Int32));
            this.marshaledObj.yc[i] = Convert.ToUInt32(_yc)
            Int32 _width = Marshal.ReadInt32(this.marshaledObj.data.width, i * sizeof(Int32));
            this.marshaledObj.width[i] = Convert.ToUInt32(_width)
            Int32 _height = Marshal.ReadInt32(this.marshaledObj.data.height, i * sizeof(Int32));
            this.marshaledObj.height[i] = Convert.ToUInt32(_height)
            Int32 _dist = Marshal.ReadInt32(this.marshaledObj.data.dist, i * sizeof(Int32));
            this.marshaledObj.dist[i] = Convert.ToUInt32(_dist)
            // Marshal class doesn't have ReadFloat method, so we will read Int32 and convert it to float
            Int32 _orientation_angle = Marshal.ReadInt32(this.marshaledObj.data.orientation_angle, i * sizeof(Int32));
            byte[] tmpBytes = BitConverter.GetBytes(_orientation_angle);
            this.marshaledObj.orientation_angle[i] = BitConverter.ToFloat(tmpBytes);
        }
        // Here is your safe "structure"
        return this.marshaledObj;
    }

    public void CleanUpManagedData(object managedObj)
    {
    }

    public void CleanUpNativeData(System.IntPtr pNativeData)
    {
        Marshal.FreeHGlobal(pNativeData);
    }
}

4)在外部方法中使用您的结构和自定义编组器,如下所示:

[DllImport("legacy.dll", CharSet = CharSet.Ansi)]
public static extern short doLegacyStuff(
    [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(myCustomMarshaler))]
    navdata_vision_detect_t navdata);

5)你可以用一种安全的方式将字节数组转换为结构。使用PtrToStructure():

 GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
 navdata_vision_t data = (navdata_vision_t)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(navdata_vision_t));
 handle.Free();