Er det et alternativ for StructLayout "Pack" attributt i Compact Framework?

stemmer
6

Jeg ønsker å gjøre følgende:

  [StructLayout(LayoutKind.Sequential, Pack = 1)]
  public struct SomeStruct
  {
     public byte  SomeByte;
     public int   SomeInt;
     public short SomeShort;
     public byte  SomeByte2;
  }

Er det et alternativ siden Pack støttes ikke i den kompakte rammen?

Oppdatering: Eksplisitt sette opp strukturen og gir FieldOffset for hver ikke fungerer enten som det ikke påvirker hvordan struct er pakket

Update2: Hvis du prøver følgende, CF-programmet vil ikke engang kjøre på grunn av hvordan strukturen er pakket:

[StructLayout(LayoutKind.Explicit, Size=8)]
public struct SomeStruct
{
   [FieldOffset(0)]
   public byte SomeByte;
   [FieldOffset(1)]
   public int SomeInt;
   [FieldOffset(5)]
   public short SomeShort;
   [FieldOffset(7)]
   public byte SomeByte2;
}

Jeg vet det virker vanskelig å tro, men hvis du prøver det vil du se. Legg det til på et CF-prosjektet og prøver å kjøre den, og du vil få en TypeLoadException. Endring av forskyvninger til 0,4,8,10 henholdsvis, og det vil fungere (men størrelsen ender opp med å bli 12).

Jeg håpet kanskje noen hadde en løsning ved hjelp av refleksjon kanskje å skaffe til veie størrelsen på hver av de felttyper individuelt (noe som innebærer rekursjon til å håndtere structs innenfor structs eller matriser av typer).

Publisert på 14/07/2009 klokken 19:54
kilden bruker
På andre språk...                            


7 svar

stemmer
6

Dette er sannsynligvis ikke den type svaret du leter etter, men jeg skal legge det likevel for helvete av det:

public struct SomeStruct
{
    public byte SomeByte;
    public int SomeInt;
    public short SomeShort;
    public byte SomeByte2;


    public byte[] APIStruct
    {
        get
        {
            byte[] output = new byte[8];
            output[0] = this.SomeByte;
            Array.Copy(BitConverter.GetBytes(this.SomeInt), 0,
                output, 1, 4);
            Array.Copy(BitConverter.GetBytes(this.SomeShort), 0,
                output, 5, 2);
            output[7] = this.SomeByte2;
            return output;
        }
        set
        {
            byte[] input = value;
            this.SomeByte = input[0];
            this.SomeInt = BitConverter.ToInt32(input, 1);
            this.SomeShort = BitConverter.ToInt16(input, 5);
            this.SomeByte2 = input[7];
        }
    }
}

I utgangspunktet det gjør pakking / utpakking seg i APIStruct eiendom.

Svarte 27/08/2009 kl. 14:12
kilden bruker

stemmer
4

Den enkleste metoden for å håndtere denne typen problemer er langs de samme linjer som du kanskje for litt felt, bare pakke dine data til en privat medlem (eller medlemmer hvis det er stor) av den aktuelle datatypen og deretter presentere offentlige eiendommer som pakke ut data for deg. De utpakking operasjoner er ekstremt rask og vil ha liten innvirkning på ytelsen. For din bestemt type følgende er trolig hva du vil:

public struct SomeStruct
{
    private long data;

    public byte SomeByte { get { return (byte)(data & 0x0FF); } }
    public int SomeInt { get { return (int)((data & 0xFFFFFFFF00) << 8); } }
    public short SomeShort { get { return (short)((data & 0xFFFF0000000000) << 40); } }
    public byte SomeByte2 { get { return (byte)((data & unchecked((long)0xFF00000000000000)) << 56); } }
}

For noen strukturer selv denne metode ikke er gjennomførbar på grunn av uheldig måte en struktur er blitt definert. I slike tilfeller vil du vanligvis må bruke en byte array som en blob av data som elementer kan pakkes ut.

EDIT: For å utdype hva jeg mener om structs som ikke kan håndteres ved hjelp av denne enkle metoden. Når du ikke kan gjøre enkle pakking / utpakking som dette du trenger å skaffe til veie den uregelmessige struct manuelt. Dette kan gjøres ved hjelp av manuelle metoder på det punktet du ringe pInvoked API eller ved å bruke en tilpasset marshaler. Følgende er et eksempel på en tilpasset marhsaler som enkelt kan tilpasses på stedet manuell marshaling.

using System.Runtime.InteropServices;
using System.Threading;

public class Sample
{
    [DllImport("sample.dll")]
    public static extern void TestDataMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(TestDataMarshaler))] TestDataStruct pData);
}

public class TestDataStruct
{
    public byte data1;
    public int data2;
    public byte[] data3 = new byte[7];
    public long data4;
    public byte data5;
}

public class TestDataMarshaler : ICustomMarshaler
{
    //thread static since this could be called on 
    //multiple threads at the same time.
    [ThreadStatic()]
    private static TestDataStruct m_MarshaledInstance;

    private static ICustomMarshaler m_Instance = new TestDataMarshaler();

    public static ICustomFormatter GetInstance(string cookie)
    {
        return m_Instance;
    }

    #region ICustomMarshaler Members

    public void CleanUpManagedData(object ManagedObj)
    {
        //nothing to do.
    }

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

    public int GetNativeDataSize()
    {
        return 21;
    }

    public IntPtr MarshalManagedToNative(object ManagedObj)
    {
        m_MarshaledInstance = (TestDataStruct)ManagedObj;
        IntPtr nativeData = Marshal.AllocHGlobal(GetNativeDataSize());

        if (m_MarshaledInstance != null)
        {
            unsafe //unsafe is simpler but can easily be done without unsafe if necessary
            {
                byte* pData = (byte*)nativeData;
                *pData = m_MarshaledInstance.data1;
                *(int*)(pData + 1) = m_MarshaledInstance.data2;
                Marshal.Copy(m_MarshaledInstance.data3, 0, (IntPtr)(pData + 5), 7);
                *(long*)(pData + 12) = m_MarshaledInstance.data4;
                *(pData + 20) = m_MarshaledInstance.data5;
            }
        }
        return nativeData;
    }

    public object MarshalNativeToManaged(IntPtr pNativeData)
    {
        TestDataStruct data = m_MarshaledInstance;
        m_MarshaledInstance = null; //clear out TLS for next call.

        if (data == null) data = new TestDataStruct(); //if no in object then return a new one

        unsafe //unsafe is simpler but can easily be done without unsafe if necessary
        {
            byte* pData = (byte*)pNativeData;
            data.data1 = *pData;
            data.data2 = *(int*)(pData + 1);
            Marshal.Copy((IntPtr)(pData + 5), data.data3, 0, 7);
            data.data4 = *(long*)(pData + 12);
            data.data5 = *(pData + 20);
        }
        return data;
    }

    #endregion
}

I tilfelle av matriser av disse structs du ikke kan bruke tilpassede Marshaling mindre array størrelse er fast, men det er relativt enkelt å skaffe til veie array data som helhet ved hjelp av de samme teknikkene manuelt.

Svarte 25/08/2009 kl. 13:09
kilden bruker

stemmer
2

Har du absolutt kreve at bestemt oppsett eller er det akseptabelt å bare gjøre størrelsen 8?

Jeg spør dette fordi lå ut som følger

[StructLayout(LayoutKind.Explicit, Size=8)]
public struct SomeStruct
{
   [FieldOffset(0)]
   public byte SomeByte;
   [FieldOffset(1)]
   public int SomeInt;
   [FieldOffset(5)]
   public short SomeShort;
   [FieldOffset(7)]
   public byte SomeByte2;
}

Har ikke ord justert felt som kan være hva som forårsaker problemet.

Hvis du kan 'ordne' ting så dette kan fungere for deg:

[StructLayout(LayoutKind.Explicit, Size=8)]
public struct SomeStruct
{
   [FieldOffset(0)]
   public byte SomeByte;
   [FieldOffset(1)]
   public byte SomeByte2;
   [FieldOffset(2)]
   public short SomeShort;
   [FieldOffset(4)]
   public int SomeInt;
}

Når jeg tester med dette på emulator det fungerer fint.

Selvfølgelig med mindre du er villig til å la omorganisering det er ingenting du kan gjøre.

Dette svaret og denne gamle artikkelen vil sterkt indikere at du må på et minimum justere dine structs på multipler av deres størrelse (jeg prøvde med en int innrettet på offset 2 og dette også utløste feil)

Gitt behovet for å fungere sammen med eksternt definerte data deretter følgende er sannsynlig at enkleste løsning:

[StructLayout(LayoutKind.Explicit, Size=8)]
public struct SomeStruct
{ 
   [FieldOffset(0)] private byte b0;
   [FieldOffset(1)] private byte b1;
   [FieldOffset(2)] private byte b2;
   [FieldOffset(3)] private byte b3;
   [FieldOffset(4)] private byte b4;
   [FieldOffset(5)] private byte b5;
   [FieldOffset(6)] private byte b6;
   [FieldOffset(7)] private byte b7;

   // not thread safe - alter accordingly if that is a requirement
   private readonly static byte[] scratch = new byte[4];       

   public byte SomeByte 
   { 
       get { return b0; }
       set { b0 = value; }
   }

   public int SomeInt
   {
       get 
       { 
           // get the right endianess for your system this is just an example!
           scratch[0] = b1;
           scratch[1] = b2;
           scratch[2] = b3;
           scratch[3] = b4;
           return BitConverter.ToInt32(scratch, 0);
       }
   }

   public short SomeShort
   {
        get 
        { 
            // get the right endianess for your system this is just an example!
            scratch[0] = b5;
            scratch[1] = b6;
            return BitConverter.ToInt16(scratch, 0);
        }
    }

    public byte SomeByte2 
    { 
        get { return b7; }
        set { b7 = value; }
    }
}
Svarte 27/08/2009 kl. 15:11
kilden bruker

stemmer
1

Jeg tror man bør ta Stephen Martin svar, gjør det akseptere en T, og bruke refleksjon til generelt å gjennomføre MarshalManagedToNative og MarshalNativeToManaged metoder. Deretter vil du ha en tilpasset pakket struct marshaler som vil fungere for alle typer struct.

Her er koden:

using System;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;

namespace System.Runtime.InteropServices
{
    public class PinnedObject : IDisposable
    {
        private GCHandle gcHandle = new GCHandle();
        public PinnedObject(object o)
        {
            gcHandle = GCHandle.Alloc(o, GCHandleType.Pinned);
        }

        public unsafe static implicit operator byte*(PinnedObject po)
        {
            return (byte*)po.gcHandle.AddrOfPinnedObject();
        }

        #region IDisposable Members
        public void Dispose()
        {
            if (gcHandle.IsAllocated)
            {
                gcHandle.Free();
            }
        }
        #endregion
    }

    public class PackedStructMarshaler<T> : ICustomMarshaler where T : struct
    {
        private static ICustomMarshaler m_instance = new PackedStructMarshaler<T>();

        public static ICustomMarshaler GetInstance()
        {
            return m_instance;
        }

        private void ForEachField(Action<FieldInfo> action)
        {
            foreach (var fi in typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic))
            {
                // System.Diagnostics.Debug.Assert(fi.IsValueType);
                action(fi);
            }
        }

        private unsafe void MemCpy(byte* dst, byte* src, int numBytes)
        {
            for (int i = 0; i < numBytes; i++)
            {
                dst[i] = src[i];
            }
        }

        #region ICustomMarshaler Members
        public void CleanUpManagedData(object ManagedObj)
        {
        }

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

        public int GetNativeDataSize()
        {
            unsafe
            {
                int ret = 0;
                ForEachField(
                    (FieldInfo fi) =>
                    {
                        Type ft = fi.FieldType;
                        ret += Marshal.SizeOf(ft);
                    });
                return ret;
            }
        }

        private object m_marshaledObj = null;

        public unsafe IntPtr MarshalManagedToNative(object obj)
        {
            IntPtr nativeData = (IntPtr)0;

            if (obj != null)
            {
                if (m_marshaledObj != null)
                    throw new ApplicationException("This instance has already marshaled a managed type");

                m_marshaledObj = obj;

                nativeData = Marshal.AllocHGlobal(GetNativeDataSize());
                byte* pData = (byte*)nativeData;
                int offset = 0;

                ForEachField(
                    (FieldInfo fi) =>
                    {
                        int size = Marshal.SizeOf(fi.FieldType);
                        using (PinnedObject po = new PinnedObject(fi.GetValue(obj)))
                        {
                            MemCpy(pData + offset, po, size);
                        }
                        offset += size;
                    });
            }

            return nativeData;
        }

        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            if (m_marshaledObj != null)
                m_marshaledObj = null;

            unsafe
            {
                byte* pData = (byte*)pNativeData;
                int offset = 0;

                object res = new T();

                ForEachField(
                    (FieldInfo fi) =>
                    {
                        int size = Marshal.SizeOf(fi.FieldType);
                        fi.SetValue(res, (object)(*((byte*)(pData + offset))));
                        offset += size;
                    });

                return res;
            }
        }

        #endregion
    }
}
Svarte 27/01/2010 kl. 01:07
kilden bruker

stemmer
1

Du må legge inn en mer relevant eksempel. Innstilling pakking på at struct ville ha noen effekt uansett.

Min innsats er at du må bruke LaoutKind.Explicit og deretter gi forskyvninger for hvert medlem. Det er mye bedre enn å rote med pakking uansett, fordi det er mye mer opplagt for noen å se på koden som den opprinnelige utvikleren eksplisitt ment for ting å være unaligned.

Noe langs disse linjene:

[StructLayout(LayoutKind.Explicit)]
struct Foo
{
    [FieldOffset(0)]
    byte a;
    [FieldOffset(1)]
    uint b;
}
Svarte 14/07/2009 kl. 21:50
kilden bruker

stemmer
0

LayoutKind.Explicit og FieldOffsetAttribute vil tillate deg å gjøre alt du kan gjøre med det Pack eiendom. Disse eksplisitte layout attributtene kan du angi nøyaktig byte posisjon for hvert felt i struct (i forhold til begynnelsen av struct utvalg av minne). Pack egenskapen brukes av runtime for å fastslå den nøyaktige plasseringen av hvert felt når du bruker en sekvensiell layout. Pakken Eiendommen har ingen annen effekt, så bruker eksplisitt layout gjør at du kan etterligne nøyaktig samme atferd, om enn litt mer verbosely. Hvis du ikke tror dette løser problemet, kanskje du kunne legge litt mer informasjon om hva du prøver å gjøre eller hvorfor du tror du trenger å bruke Pack eiendom.

Edit: Jeg la merke til den ekstra kommentar om å prøve å få hele strukturen størrelse 8 byte. Har du prøvd å bruke StructLayoutAttribute.Size eiendom? I motsetning Pack, er den tilgjengelig i Compact Framework.

Svarte 26/08/2009 kl. 21:44
kilden bruker

stemmer
0

LayoutKind.Explicitville være det beste alternativet for å definere en bestemt minne layout. Imidlertid ikke bruk LayoutKind.Explicitfor konstruksjoner som inneholder peker størrelse verdier som sanne pekere, operativsystem håndtak eller IntPtrs; Dette er bare å be om mystiske problemer under kjøring på tilfeldige plattformer.

Spesielt LayoutKind.Expliciter en dårlig erstatning for anonyme fagforeninger . Hvis ønsket strukturen inneholder en anonym union, konvertere den til en navngitt union; du kan trygt representere en navngitt union som en struct med LayoutKind.Explicithvor alle forskyvninger 0.

Svarte 14/07/2009 kl. 22:01
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more