none
UNION в C# RRS feed

  • Вопрос

  • Не могу разобраться. Как сделать что бы это работало.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace ConsoleApplication2
    {
        class Program
        {
            [StructLayout(LayoutKind.Explicit, Pack = 1)]
            public struct Zagolovok
            {
                [FieldOffset(0)] public UInt16 ID;
                [FieldOffset(2)] public UInt16 Comm;
                [FieldOffset(4)] public UInt16 Size;
            }
            [StructLayout(LayoutKind.Explicit, Pack = 1)]
            public struct Buffer
            {
                [FieldOffset(0)] public Zagolovok zagolovok;
                /// <summary>
                /// ...
                /// ...
                /// ...
                /// </summary>
                [FieldOffset(0)] public byte[] Buf;
            }
            static void Main(string[] args)
            {
                Buffer buffer=new Buffer();
                buffer.zagolovok.ID = 4664;
                Console.WriteLine(buffer.zagolovok.ID);
                Console.ReadLine();
            }
        }
    }

    Это надо для клиент-серверного приложения на основе сокетов. Метод Send принимает только массив байт (byte[]), а мне надо передовать сложные структуры. В С и делфи это делалось элементарно а тут не получается.

    Данный кусок кода при компиляции ошибок не выдает, а при запуске ругается

    Не удалось загрузить тип "Buffer" из сборки "ConsoleApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", так как он содержит поле объекта со смещением 0, которое неверно выровнено или перекрыто полем, не представляющим объект.

    Если удалить строку [FieldOffset(0)] public byte[] Buf;
    то все работает, но мне именно эта строка нужна, что бы передать в метод Send

    Получится ли это сделать простыми способами?

    Спасибо

     

    • Перемещено Tagore Bandlamudi 1 октября 2010 г. 22:04 MSDN Forums consolidation (От:Visual C#)
    6 апреля 2010 г. 9:00

Ответы

  • В свойствах проекта выставить Build/Allow Unsafe Code.

    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    public struct Zagolovok
    {
        [FieldOffset(0)]
        public UInt16 ID;
        [FieldOffset(2)]
        public UInt16 Comm;
        [FieldOffset(4)]
        public UInt16 Size;
    }
    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    unsafe public struct Buffer
    {
        [FieldOffset(0)]
        public Zagolovok zagolovok;
        [FieldOffset(0)]
        public fixed byte Buf[6];
    }
    

    • Помечено в качестве ответа Игорь П 7 апреля 2010 г. 12:27
    7 апреля 2010 г. 12:20
  • Тогда придется руками перегонять данные из byte* в управляемый byte[]

    Buffer buff = new Buffer();
    byte[] arrayToPassToSendMethod = new byte[6];
    Marshal.Copy((IntPtr)buff.Buf, arrayToPassToSendMethod, 0, 6);
    Send(arrayToPassToSendMethod....
    
    Если цель - отправить структуру вида [заголовок, данные после заголовка] извеcтного размера, то можно обойтись вообще без union.

        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        public unsafe struct Buffer
        {
            [FieldOffset(0)]
            public Zagolovok zagolovok;
            [FieldOffset(6)]
            public fixed byte data[20];
        }
    
            static unsafe void Main(string[] args)
            {
                Buffer bufferToSend = new Buffer
                {
                    zagolovok = new Zagolovok { ID = 1, Comm = 2, Size = 3 }
                };
    
                byte[] byteArray = new byte[sizeof(Buffer)];
                fixed (byte* ptr = byteArray)
                {
                    *((Buffer*)ptr) = bufferToSend;
                }
    
                Send(byteArray...
            }
    

    В этом случае можно вообще без unsafe кода и указателей обойтись

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct Zagolovok
        {
            public UInt16 ID;
            public UInt16 Comm;
            public UInt16 Size;
        }
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct Buffer
        {
            public Zagolovok zagolovok;
    
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
            public byte[] data;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Buffer packetToSend = new Buffer
                {
                    zagolovok = new Zagolovok { ID = 1, Comm = 2, Size = 3 },
                    data = new byte[100]
                };
    
                packetToSend.data[5] = 42;
    
                int size = Marshal.SizeOf(packetToSend);
    
                byte[] byteArray = new byte[size];
    
                IntPtr pointer = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(packetToSend, pointer, false);
                Marshal.Copy(pointer, byteArray, 0, size);
                Marshal.FreeHGlobal(pointer);
    
                //Send(byteArray
            }
        }
    

    • Помечено в качестве ответа Игорь П 9 апреля 2010 г. 5:03
    8 апреля 2010 г. 17:40

Все ответы

  • Перед Buf 6 байт заголовка? Поставь offset 6 вместо 0 для Buf.
    • Помечено в качестве ответа I.Vorontsov 7 апреля 2010 г. 8:28
    • Помечено в качестве ответа I.Vorontsov 7 апреля 2010 г. 8:28
    • Снята пометка об ответе Игорь П 7 апреля 2010 г. 12:07
    6 апреля 2010 г. 10:00
  • Спасибо, но не работает даже с 6.

    Хотя мне надо что бы было именно 0. Чтобы в памяти эти переменные хранились по одному адресу.

    Как UNION  в C++

    7 апреля 2010 г. 12:07
  • В свойствах проекта выставить Build/Allow Unsafe Code.

    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    public struct Zagolovok
    {
        [FieldOffset(0)]
        public UInt16 ID;
        [FieldOffset(2)]
        public UInt16 Comm;
        [FieldOffset(4)]
        public UInt16 Size;
    }
    [StructLayout(LayoutKind.Explicit, Pack = 1)]
    unsafe public struct Buffer
    {
        [FieldOffset(0)]
        public Zagolovok zagolovok;
        [FieldOffset(0)]
        public fixed byte Buf[6];
    }
    

    • Помечено в качестве ответа Игорь П 7 апреля 2010 г. 12:27
    7 апреля 2010 г. 12:20
  • Большое спасибо. все клево получилось. Я два дня не мог сделать.

    fixed наверно надо поставить везде, да?

    А чем грозит использование небезопасного кода.

    7 апреля 2010 г. 12:28
  • fixed встраивает массив в структуру. Без него в струкруре хранилась бы просто ссылка на массив в куче. http://msdn.microsoft.com/ru-ru/library/zycewsya.aspx . Там же по ссылкам расписаны возможные последствия. Например:

    static unsafe void Main(string[] args)
    {
        // этот код не вызывает исключения
        Buffer buffer = new Buffer();
        buffer.Buf[7] = 42;
    
        // а этот - вызывает
        byte[] arr = new byte[6];
        arr[7] = 42;
    }

     

    7 апреля 2010 г. 12:44
  • Спасибо. Теперь уперся в другую проблему. Мне надо этот Buf отправить через сокет, но в строке

    Client.Send(buffer.Buf, SIZE, 0);

    выбиват такую ошибку

    Невозможно использовать буферы фиксированного размера в нефиксированных выражениях. Попробуйте использовать оператор fixed. 

    вроде fixed уже использовал, может надо его еще куда воткнуть?

    8 апреля 2010 г. 5:12
  • А как выглядит объявление метода Send? Он первым параметром принимает byte[] или byte*?

    8 апреля 2010 г. 14:38
  • public int Send(
    	byte[] buffer,
    	int size,
    	SocketFlags socketFlags
    )

    принимает byte[]

    8 апреля 2010 г. 15:49
  • Тогда придется руками перегонять данные из byte* в управляемый byte[]

    Buffer buff = new Buffer();
    byte[] arrayToPassToSendMethod = new byte[6];
    Marshal.Copy((IntPtr)buff.Buf, arrayToPassToSendMethod, 0, 6);
    Send(arrayToPassToSendMethod....
    
    Если цель - отправить структуру вида [заголовок, данные после заголовка] извеcтного размера, то можно обойтись вообще без union.

        [StructLayout(LayoutKind.Explicit, Pack = 1)]
        public unsafe struct Buffer
        {
            [FieldOffset(0)]
            public Zagolovok zagolovok;
            [FieldOffset(6)]
            public fixed byte data[20];
        }
    
            static unsafe void Main(string[] args)
            {
                Buffer bufferToSend = new Buffer
                {
                    zagolovok = new Zagolovok { ID = 1, Comm = 2, Size = 3 }
                };
    
                byte[] byteArray = new byte[sizeof(Buffer)];
                fixed (byte* ptr = byteArray)
                {
                    *((Buffer*)ptr) = bufferToSend;
                }
    
                Send(byteArray...
            }
    

    В этом случае можно вообще без unsafe кода и указателей обойтись

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct Zagolovok
        {
            public UInt16 ID;
            public UInt16 Comm;
            public UInt16 Size;
        }
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct Buffer
        {
            public Zagolovok zagolovok;
    
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
            public byte[] data;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                Buffer packetToSend = new Buffer
                {
                    zagolovok = new Zagolovok { ID = 1, Comm = 2, Size = 3 },
                    data = new byte[100]
                };
    
                packetToSend.data[5] = 42;
    
                int size = Marshal.SizeOf(packetToSend);
    
                byte[] byteArray = new byte[size];
    
                IntPtr pointer = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(packetToSend, pointer, false);
                Marshal.Copy(pointer, byteArray, 0, size);
                Marshal.FreeHGlobal(pointer);
    
                //Send(byteArray
            }
        }
    

    • Помечено в качестве ответа Игорь П 9 апреля 2010 г. 5:03
    8 апреля 2010 г. 17:40
  • Еще раз спасибо. Все подробно и понятно.

    Думаю выбрать из первых двух вариантов. Они понятнее. Просто пересылать данные после заголовка надо разных размеров. А в заголовке передается информация о типе этих данных и их размер.

    Будем пробовать.

    9 апреля 2010 г. 5:08