2015-02-10 66 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