6

私は次のことをしたいと思います:

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

コンパクト フレームワークでは Pack がサポートされていないため、代替手段はありますか?

更新: 構造体を明示的に設定し、それぞれに FieldOffset を指定しても、構造体のパック方法には影響しないため、機能しません。

Update2: 次のことを試してみると、構造がどのようにパックされているかにより、CF プログラムは実行されません。

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

信じられないかもしれませんが、試してみればわかります。これを CF プロジェクトに追加して実行しようとすると、TypeLoadException が発生します。オフセットをそれぞれ 0,4,8,10 に変更すると機能します (ただし、サイズは最終的に 12 になります)。

おそらく誰かがリフレクションを使用して、各フィールド型のサイズを個別にマーシャリングすることを望んでいました(構造体または型の配列内の構造体を処理するための再帰を伴うもの)。

4

7 に答える 7

6

これはおそらくあなたが探しているタイプの回答ではありませんが、とにかく投稿します。

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

基本的に、APIStruct プロパティでパッキング/アンパッキング自体を行います。

于 2009-08-27T14:12:53.160 に答える
4

この種の問題に対処する最も簡単な方法は、ビット フィールドの場合と同じ方法で、データを適切なデータ型のプライベート メンバー (サイズが大きい場合はメンバー) にパックし、パブリック プロパティを提示することです。あなたのためにデータを解凍します。アンパック操作は非常に高速で、パフォーマンスへの影響はほとんどありません。特定のタイプの場合、おそらく次のものが必要です。

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

一部の構造では、構造の定義方法が不運なため、この方法でも実行できません。そのような場合、通常、要素をアンパックできるデータの塊としてバイト配列を使用する必要があります。

編集:この単純な方法を使用して処理できない構造体について私が意味することを拡張します。このような単純なパッキング/アンパッキングを行うことができない場合は、不規則な struct を手動でマーシャリングする必要があります。これは、pInvoked API を呼び出す時点で手動メソッドを使用するか、カスタム マーシャラーを使用して行うことができます。以下は、その場での手動マーシャリングに簡単に適応できるカスタム マーサラーの例です。

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
}

これらの構造体の配列の場合、配列サイズが固定されていない限りカスタム マーシャリングを使用できませんが、同じ手法を使用して配列データ全体を手動でマーシャリングするのは比較的簡単です。

于 2009-08-25T13:09:43.723 に答える
2

その特定のレイアウトが絶対に必要ですか、それとも単純にサイズ 8 にすることは許容されますか?

レイアウトは次のようになっているので、私はこれを尋ねます

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

問題の原因となっている可能性のある、単語が整列されていないフィールドがあります。

物事を「再配置」できる場合、これはうまくいくかもしれません:

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

エミュレータでこれをテストすると、正常に動作します。

明らかに、再配置を許可しない限り、あなたにできることは何もありません。

この回答この古い記事は、少なくともサイズの倍数で構造体を整列する必要があることを強く示しています(オフセット2に整列されたintで試しましたが、これもエラーを引き起こしました)

外部で定義されたデータと相互運用する必要がある場合、次の方法がおそらく最も簡単な解決策です。

[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; }
    }
}
于 2009-08-27T15:11:42.660 に答える
1

Stephen Martin の回答を参考にして、T を受け入れ、リフレクションを使用して MarshalManagedToNative および MarshalNativeToManaged メソッドを一般的に実装する必要があると思います。次に、任意のタイプの構造体で機能するカスタムのパック構造体マーシャラーを作成します。

コードは次のとおりです。

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
    }
}
于 2010-01-27T01:07:43.330 に答える
0

LayoutKind.Explicit と FieldOffsetAttribute を使用すると、Pack プロパティで実行できることはすべて実行できます。これらの明示的なレイアウト属性を使用すると、構造体の各フィールドの正確なバイト位置を (構造体のメモリ範囲の先頭を基準にして) 指定できます。Pack プロパティは、連続レイアウトを使用するときに各フィールドの正確な位置を決定するために、ランタイムによって使用されます。pack プロパティには他に影響がないため、明示的なレイアウトを使用すると、少し冗長ではありますが、まったく同じ動作をエミュレートできます。これで問題が解決しないと思われる場合は、何をしようとしているのか、または Pack プロパティを使用する必要があると思われる理由について、もう少し情報を投稿できます。

編集: 構造体全体のサイズを 8 バイトにしようとしているという追加のコメントに気付きました。StructLayoutAttribute.Size プロパティを使用してみましたか? Pack とは異なり、Compact Framework で使用できます。

于 2009-08-26T21:44:47.163 に答える
0

LayoutKind.Explicit特定のメモリ レイアウトを定義するには、これが最善の方法です。ただし、真のポインター、オペレーティング システムのハンドル、sなどのポインター サイズの値を含む構造体には使用しないでください。LayoutKind.ExplicitIntPtrこれは、ランダムなプラットフォームでの実行時に不可解な問題を要求しているだけです。

特に、LayoutKind.Explicit匿名共用体の代用としては不十分です。ターゲット構造に無名共用体が含まれている場合は、それを名前付き共用体に変換します。名前付き共用体を構造体として安全に表現できLayoutKind.Explicitすべてのオフセットは0.

于 2009-07-14T22:01:02.520 に答える