2015-02-10 85 views
3

我正在使用其他人编写的项目从Parrot AR Drone接收一些数据。很多数据以字节数组的形式出现,我使用的这个库使用一堆结构进行分析。一般来说,我对编组真的很陌生。摆脱不安全的代码,从字节编组uint数组?

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

[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值获得,我必须使用“固定”关键字,它似乎真的很乱:

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]; 
     } 
} 

我希望能够只访问UINT阵列就像我会正常的C#阵列。我认为我应该能够使用编组,但我无法使它工作。我想是这样的:

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

这让我删除“不安全”和“固定”的关键字,但引起了另一个问题,因为解析字节的数据时,有一个大的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; 
     } 
} 

所以我仍然有一些指向这个结构在另一个不安全的函数。我怎样才能让这些结构中的数组保持“安全”,同时又允许另一个不安全的函数将我的对象作为结构体来施放?

感谢您给予任何帮助!

+0

不幸的是,您的问题和代码示例并不完全清楚。为什么你的'ProcessOption()'方法将一个指针作为参数?什么是'navdata_option_t'类型?这与“navdata_vision_detect_t”类型有什么关系?'navdata_vision_detect_t'类型有一个'size'字段;这个大小实际上是可变的?您使用的图书馆是否强迫您使用不安全的结构,或者您是否拥有对图书馆代码的控制权?在我看来,仅仅使用'BitConverter'来解析数组到实际的结构将是最好的。 – 2015-02-10 07:58:35

+0

请注意,通过在类型中使用显式布局并将多个字段(本身可以是结构体)的偏移量设置为结构中的相同位置,您可以在C#中有效地创建联合。这个问题对你是否有用尚不清楚,因为问题本身不是很清楚。 – 2015-02-10 07:59:35

+0

'* _detectData.xc'不工作?编辑:不,你正在使用它作为一个领域。作为一个本地人,你不需要那个。所以修复它在入门或使用参数等 – leppie 2015-02-10 18:25:35

回答

0

首先,编号为UnmanagedType.ByValArray不会起作用:我们有指针而不是值。 建议的联合仿真也会失败:数组是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)总结解决这个结构的类。该类应该包含上述结构作为成员。它也应该以“安全的方式”镜像所有结构成员。

[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(); 
+0

Plz评论你认为什么是错的,亲爱的downvoter? – Eugene 2015-02-10 18:49:02