[.net] Marshall.StructureToPtr - correct usage?

Started by
4 comments, last by Afr0m@n 17 years, 9 months ago
I've made a couple of methods to receive and send a class in C#. Problem is that to use the sending one, I have to supply it with an int pointer as a parameter, representing a pointer to the object I'm sending. To do this, I'm trying to use the StructureToPtr method, but it's giving me a hard time. :( When I run the code, I get an error message saying "The specified structure must be blittable or have layout information". What does that mean? How do I resolve this issue? I've already looked in MSDN, the documentation is useless. Here's the code:
        public unsafe Program()
        {
            Socket = new NetFXSocket(false, "127.0.0.1");
            StateObject ThisState = new StateObject(0, Socket.Listener, 1024); 

            if (Socket.Connected == true)
                Console.WriteLine("Successfully connected to server!");
            
            User NewUser = new User(ThisState);
            IntPtr Ptr = Marshal.AllocCoTaskMem(1024);
            Marshal.StructureToPtr(NewUser, Ptr, false);
            NewUser.Name = "Mats";
            NewUser.Password = "Prins";
            NewUser.SendObject(Ptr);
        }

Oh and here's the code for the User class:
using System;
using System.Collections.Generic;
using System.Text;
using NetFX;
using System.Net.Sockets;
using System.Runtime.InteropServices;

namespace NimdalinNetwork
{
    public class User : NetworkObject
    {
        public StateObject ThisState;
        public string Name;
        public string Password;

        public User(StateObject ParamState)
        {
            ThisState = ParamState;
        }

        public override unsafe void SendObject(IntPtr Ptr)
        {
            object Obj = new object();
            Marshal.PtrToStructure(Ptr, Obj);
            
            byte[] buf = (byte[])Obj;
            ThisState.StateSocket.Send(buf);
        }

        public override void ReceiveObject()
        {
            byte[] buf = new byte[1024];

            ThisState.StateSocket.BeginReceive(ThisState.buf, 0, 1024, SocketFlags.None,
                                                new AsyncCallback(HandleReceive), ThisState);
        }

        private void HandleReceive(IAsyncResult ar)
        {
            StateObject State = (StateObject)ar.AsyncState;
            Socket Client = State.StateSocket;
            int Read = Client.EndReceive(ar);

            if (Read > 0)
            {
                //Not all the data was read, so read more...
                Client.BeginReceive(State.buf, 0, 1024, SocketFlags.None,
                                                new AsyncCallback(HandleReceive), State);
            }
            else if (State.buf.Length > 1)
            {
                //Store the new user item in the RecvdObj object.
                State.RecvdObj = (object)State.buf;
            }

            Client.Close();
        }
    }
}


Thanks in advance for any help! Edit; This line is the one that gives me the error - "Marshal.StructureToPtr(NewUser, Ptr, false);"
_______________________Afr0Games
Advertisement
You need to make your User type a struct in order to make it "blittable". There is a very fundamental difference in how structs and classes are handled in .NET. While a variable of a class type represents a pointer to an instance of the class on the heap, a variable of a value type (of which structs are a subset) actually *is* the sequence of bytes that comprises the information in the struct.

The word "blittable" means that an instance of the type can be copied with just a simple memcpy. For this to be true, it can only contain primitives or other value types.

An instance of a .NET class contains more than just the bytes representing its members, it also has some space reserved for pointers to metadata about the type (this is used to support virtual dispatch of method calls and reflection). See here for some more information about the runtime layout of objects and value types in the .NET runtime.

In other words, only structs can be blittable. Furthermore, for a struct to be blittable it can only contain other value types or primitives (int, float etc).

I think you will need to reconsider your entire architecture.
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
Yep, Arild is right. Only structs, each element of which is also a blittable struct or other value type, can be memcpy'd in this way. For a general purpose transmission of class data, you need to look into serialisation (either that provided by .Net, or by specifying a protocol by which your classes can serialise themselves).
Thanks, guys! I redesigned my system with the help of a friend. The problem now is, I can't send or receive any data! :( The number of bytes sent seems to always be 0 for some reason, and the same goes for the number of bytes received, obviously. What I'm doing is I'm copying the datamembers of the User class to a stream and then getting the buffer of the stream into the array of bytes that are going to be sent. Could anyone please take a look? The class is completely standalone, except for a StateObject class that I'll also post. Thanks in advance!

using System;using System.Collections.Generic;using System.Text;using NetFX;using System.Net.Sockets;using System.IO;namespace NimdalinNetwork{    public class User    {        public StateObject ThisState;        public string Name = "";        public string Password = "";        public User(StateObject ParamState)        {            ThisState = ParamState;        }        private bool WriteTo(Stream S)        {            using(BinaryWriter Writer = new BinaryWriter(S))            {                Writer.Write(Name);                Writer.Write(Password);            }            return true;        }        private bool ReadFrom(Stream S)        {            using (BinaryReader Reader = new BinaryReader(S))            {                Name = Reader.ReadString();                Password = Reader.ReadString();            }            return true;        }        public void SendObject()        {            using (MemoryStream MemStream = new MemoryStream(1024))            {                WriteTo(MemStream);                byte[] buf = MemStream.GetBuffer();                ThisState.StateSocket.BeginSend(buf, 0, buf.Length, SocketFlags.None, HandleSend, ThisState);             }        }        public void ReceiveObject()        {            try            {                ThisState.StateSocket.BeginReceive(ThisState.buf, 0, ThisState.buf.Length, SocketFlags.None,                                                    new AsyncCallback(HandleReceive), ThisState);            }            catch (Exception Ex)            {            }        }        private void HandleSend(IAsyncResult ar)        {            StateObject State = (StateObject)ar.AsyncState;            Socket Client = State.StateSocket;            int Sent = Client.EndSend(ar);            ThisState.Sent = Sent;        }        private void HandleReceive(IAsyncResult ar)        {            StateObject State = (StateObject)ar.AsyncState;            Socket Client = State.StateSocket;            int Read = Client.EndReceive(ar);            if (Read > 0)            {                ThisState.Read = Read;                MemoryStream MemStream = new MemoryStream();                BinaryWriter Writer = new BinaryWriter(MemStream);                Writer.Write(State.buf);                ReadFrom(MemStream);            }            else            {                ThisState.StateSocket.BeginReceive(State.buf, Read, State.buf.Length, SocketFlags.None,                                                    new AsyncCallback(HandleReceive), State);            }            Client.Close();        }    }}


using System;using System.Collections.Generic;using System.Text;using System.Net.Sockets;namespace NetFX{    /// <summary>    /// Generic state object that's supposed to be used to determine what state in any given protocol    /// any given socket is in.    /// </summary>    public class StateObject    {        public int State;        public Socket StateSocket;        public byte[] buf;        public int BufferSize;        public int Read;        public int Sent;                public StateObject(int ParamState, Socket ParamSocket, int BSize)        {            State = ParamState;            StateSocket = ParamSocket;            buf = new byte[BSize];            BufferSize = BSize;        }    }}
_______________________Afr0Games
Dumb question no. 1: have you assigned values to User and Password? It may be that the representation of an empty string is zero bytes, though I doubt it.

Put some sort of logging code in before you attempt the send to find out if you've successfully populated the MemoryStream and the byte array. Then you'll know which half of your code isn't working.

It looks okay at a glance but I'm not used to working with streams so I may not have a very good eye!
Yep values are assigned in the client code. Actually, you can see that in the code in my original post, if I'm not mistaken. And I also wrote a quick logger. For some reason I've now started getting an error message from my server program saying that it's reading past the end of the memory stream. It also seems, according to my logger class, that I'm getting 1024 bytes of data regardless of the size of the memorystream on the clientside when it's created. I think that must mean that I'm sending data, and that the memorystream must be resizing itself automatically based on the size of the array of bytes that's written to it. I still haven't been able to figure out why the receiving code is reading past the end of the stream though... :\
_______________________Afr0Games

This topic is closed to new replies.

Advertisement