c#结构体中的结构体数组应该如何定义与初始化赋值

时间:2022-08-30 19:09:00
结构体定义如下

    //报文头
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TMsgHeadInfo
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public char[] MsgCode;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
        public char[] MsgType;
        public byte SenderType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public char[] Sender;
        public byte ReceiverType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
        public char[] Receiver;
        public int TotalLength;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public char[] VerifyCode;
    }
//03 报文
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TLabelContent
    {
        public int PosX;
        public int PosY;
        public int Flag;
        public int Flv;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public char[] FileName;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public char[] strContent;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public char[] strColor;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)]
        public char[] strBkColor;
        public int Space;
        public int Font;
        public int FontHeight;
        public int FontWidth;
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TPlayList
    {
        public int ActTime;
        public int Action;
        public int Speed;
        public int ListNum;
        public TLabelContent[] Content;
    }
    //下发节目单
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TPlayListInfo
    {
        public byte DevType;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
        public char[] DevCode;
        public int Page;
        public TPlayList[] PlayList;
    }
    //下发报文
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    struct TPlayListMsg
    {
        public TMsgHeadInfo MsgHead;
        public TPlayListInfo PlayListInfo;
    }


调用代码如下

private void button3_Click(object sender, EventArgs e)
        {
            //初始化方法是否正确,如果不正确请告知其他方法
            TPlayListMsg playlistmsg;
            playlistmsg.PlayListInfo.PlayList = new TPlayList[10];

            for (int i = 0; i < 10; i++)
                playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5];

            playlistmsg.PlayListInfo.DevCode = new char[10];
            playlistmsg.PlayListInfo.DevCode = "2803000001".ToCharArray();
            playlistmsg.PlayListInfo.DevType = 1;
            playlistmsg.PlayListInfo.Page = 10;

            playlistmsg.MsgHead.MsgCode = new char[2];
            playlistmsg.MsgHead.MsgCode = "03".ToCharArray();

            playlistmsg.MsgHead.MsgType = new char[2];
            playlistmsg.MsgHead.MsgType = "01".ToCharArray();
            playlistmsg.MsgHead.SenderType = 1;

            playlistmsg.MsgHead.Sender = new char[6];
            playlistmsg.MsgHead.Sender = "280100".ToCharArray();

            playlistmsg.MsgHead.Receiver = new char[6];
            playlistmsg.MsgHead.Receiver = "280300".ToCharArray();
            playlistmsg.MsgHead.ReceiverType = 4;
            //注意此处
            playlistmsg.MsgHead.TotalLength = 2600;

            playlistmsg.MsgHead.VerifyCode = new char[32];
            //注意此处
            playlistmsg.MsgHead.VerifyCode = "235A3ECDB49A612AFDE4F4C3D6735D5D".ToCharArray();
            
            for (int i = 0; i < 10; i++)
            {
                playlistmsg.PlayListInfo.PlayList[i].ListNum = 5;
                playlistmsg.PlayListInfo.PlayList[i].Action = 1;
                playlistmsg.PlayListInfo.PlayList[i].ActTime = 5;
                playlistmsg.PlayListInfo.PlayList[i].Speed = 10;
                for (int j = 0; j < 5; j++)
                {
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].PosX = 0;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].PosY = 0;

                    playlistmsg.PlayListInfo.PlayList[i].Content[j].FileName = new char[255];
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].FileName = "".ToCharArray();
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].Flag = 0;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].Flv = 0;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].Font = 0;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].FontHeight = 32;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].FontWidth = 32;
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].Space = 0;

                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strBkColor = new char[12];
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strBkColor = "255000000000".ToCharArray();

                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strColor = new char[12];
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strColor = "000000000000".ToCharArray();

                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strContent = new char[255];
                    playlistmsg.PlayListInfo.PlayList[i].Content[j].strContent = "祝您一路平安".ToCharArray();
                }
            }
            
            Tools tool = new Tools();
            byte[] by = tool.StructToBytes(playlistmsg.PlayListInfo);
            playlistmsg.MsgHead.VerifyCode = tool.MD5Byte(by);

            byte[] bytes = tool.StructToBytes(playlistmsg.MsgHead);

            playlistmsg.MsgHead.TotalLength = Marshal.SizeOf(playlistmsg);

            TCPClient tcp = new TCPClient();
            int ret;
            ret = tcp.ConnectServer("192.168.26.118", 13334);
            if (ret == 0)
            {
                MessageBox.Show("连接服务器失败");
            }
            else
            {
                MessageBox.Show("连接服务器成功");

                if (tcp.SendMsg(bytes) > 0)
                {
                    MessageBox.Show("发送成功");
                }
                else
                {
                    MessageBox.Show("发送失败");
                }
            }
        }

里面用到的结构体转byte数组的代码如下
        public byte[] StructToBytes(object obj)
        {
            int size = Marshal.SizeOf(obj);
            byte[] bytes = new byte[size];
            IntPtr structPtr = Marshal.AllocHGlobal(size); //分配结构体大小的内存空间
            Marshal.StructureToPtr(obj, structPtr, false); //将结构体拷到分配好的内存空间
            Marshal.Copy(structPtr, bytes, 0, size);       //从内存空间拷到byte数组
            Marshal.FreeHGlobal(structPtr);                //释放内存空间
            return bytes;
        }

我上面的初始化方法是否正确,另外上面标处两个注意的地方,一个是要得到这处结构体的MD5(只要报文体的,即TPlaylistInfo的),一个是要得到这个结构体的长度,但是因为如果结构体中有未赋值 参数,所以就先随便赋了个值,然后再修改的这两个值,挺别扭,请问有其他方法吗


近来问的问题回答的人不多,而且大多没回答到点子上,望大家认真对待,解决问题后,尽快结帖,如果分不够,可以再开帖散分,谢谢

21 个解决方案

#1


这句提示错误,
byte[] by = tool.StructToBytes(playlistmsg.PlayListInfo);
            playlistmsg.MsgHead.VerifyCode = tool.MD5Byte(by);


未处理的“System.ArgumentException”类型的异常出现在 MTWS.Net.exe 中。

其他信息: 参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))

另外TCPCLIENT类的定义如下
 class TCPClient
    {
        //客户端socket
        Socket ClientSocket;
        IPEndPoint sSvrIP;
        public TCPClient()
        {

        }

        ~TCPClient()
        {

        }

        public int ConnectServer(string IP, int Port)
        {
            int ret = 0;
            sSvrIP = new IPEndPoint(IPAddress.Parse(IP), Port);
            ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                ClientSocket.Connect(sSvrIP);
                ret = 1;
            }
            catch (SocketException ex)
            {
                ret = 0;
                //throw new Exception(ex.Message);
            }
            return ret;
        }

        public bool IsConnected()
        {
            return ClientSocket.Connected;
        }

        public int SendMsg(byte[] Msg)
        {
            if (IsConnected())
                return ClientSocket.Send(Msg, Msg.Length, SocketFlags.None);
            else
                return -1;
        }

        public int RecvMsg(byte[] Msg)
        {
            int bufLen = 0;
            bufLen = Msg.Length;
            if (IsConnected())
                return ClientSocket.Receive(Msg, bufLen, SocketFlags.None);
            else
                return -1;
        }
    }

#2


public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

#3


引用 2 楼 soaringbird 的回复:
public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 

#4


引用 3 楼 shuihan20e 的回复:
Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

#5


引用 4 楼 soaringbird 的回复:
Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

#6


TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

#7


引用 5 楼 shuihan20e 的回复:
Quote: 引用 4 楼 soaringbird 的回复:

Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

类型定义你这么写也可以,但是封送和解析的地方,你还是按照报文的定义(类型、顺序、长度、字节序)来处理吧

#8


引用 6 楼 gomoku 的回复:
TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

如果加上[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]这样能行吗?

#9


c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

#10


引用 9 楼 wyd1520 的回复:
c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了

#11


引用 10 楼 shuihan20e 的回复:
Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

#12


引用 11 楼 wyd1520 的回复:
Quote: 引用 10 楼 shuihan20e 的回复:

Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

那个问题已解决了,这是新问题,结构体套结构体数组的问题

#13


引用 7 楼 soaringbird 的回复:
Quote: 引用 5 楼 shuihan20e 的回复:

Quote: 引用 4 楼 soaringbird 的回复:

Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

类型定义你这么写也可以,但是封送和解析的地方,你还是按照报文的定义(类型、顺序、长度、字节序)来处理吧

但是提示错误啊
未处理的“System.ArgumentException”类型的异常出现在 MTWS.Net.exe 中。

其他信息: 未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配。

#14


引用 6 楼 gomoku 的回复:
TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

另外可否这样,声明lablecontent数组,赋值 ,然后把他们移动到一个连续内在里再发送

#15


引用 12 楼 shuihan20e 的回复:
Quote: 引用 11 楼 wyd1520 的回复:

Quote: 引用 10 楼 shuihan20e 的回复:

Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

那个问题已解决了,这是新问题,结构体套结构体数组的问题


我的建议还是一样的,要不然你会发现C#强行实现C++的结构,到了后边会是个坑,而这个坑会越挖越深。。

C++多以指针操作,他可以容易实现结构的大小对应内存里的数据,C#这方面不怎么行。
就像内存共享一样。当然你要非得这样实现,只能慢慢去挖觉。我也会关注一下。可能以后会用到。

#16


记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用

#17


引用 16 楼 shuihan20e 的回复:
记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。


#18


引用 17 楼 wyd1520 的回复:
Quote: 引用 16 楼 shuihan20e 的回复:

记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。



感谢,问题已解决,还是用原来的方法,提示那个错误是因为位数不够,又没有自动补齐,您所说的那种访求,我也想尝试一下,可以加我QQ 78647557,晚上聊聊,C#方面还要多向你学习,

#19


C#想管理内存数据确实要命... 

#20


引用 18 楼 shuihan20e 的回复:
Quote: 引用 17 楼 wyd1520 的回复:

Quote: 引用 16 楼 shuihan20e 的回复:

记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。



感谢,问题已解决,还是用原来的方法,提示那个错误是因为位数不够,又没有自动补齐,您所说的那种访求,我也想尝试一下,可以加我QQ 78647557,晚上聊聊,C#方面还要多向你学习,


贴出结果呀。。哪里补齐

#21


所有的char数组都要补齐

#1


这句提示错误,
byte[] by = tool.StructToBytes(playlistmsg.PlayListInfo);
            playlistmsg.MsgHead.VerifyCode = tool.MD5Byte(by);


未处理的“System.ArgumentException”类型的异常出现在 MTWS.Net.exe 中。

其他信息: 参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))

另外TCPCLIENT类的定义如下
 class TCPClient
    {
        //客户端socket
        Socket ClientSocket;
        IPEndPoint sSvrIP;
        public TCPClient()
        {

        }

        ~TCPClient()
        {

        }

        public int ConnectServer(string IP, int Port)
        {
            int ret = 0;
            sSvrIP = new IPEndPoint(IPAddress.Parse(IP), Port);
            ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                ClientSocket.Connect(sSvrIP);
                ret = 1;
            }
            catch (SocketException ex)
            {
                ret = 0;
                //throw new Exception(ex.Message);
            }
            return ret;
        }

        public bool IsConnected()
        {
            return ClientSocket.Connected;
        }

        public int SendMsg(byte[] Msg)
        {
            if (IsConnected())
                return ClientSocket.Send(Msg, Msg.Length, SocketFlags.None);
            else
                return -1;
        }

        public int RecvMsg(byte[] Msg)
        {
            int bufLen = 0;
            bufLen = Msg.Length;
            if (IsConnected())
                return ClientSocket.Receive(Msg, bufLen, SocketFlags.None);
            else
                return -1;
        }
    }

#2


public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

#3


引用 2 楼 soaringbird 的回复:
public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 

#4


引用 3 楼 shuihan20e 的回复:
Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

#5


引用 4 楼 soaringbird 的回复:
Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

#6


TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

#7


引用 5 楼 shuihan20e 的回复:
Quote: 引用 4 楼 soaringbird 的回复:

Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

类型定义你这么写也可以,但是封送和解析的地方,你还是按照报文的定义(类型、顺序、长度、字节序)来处理吧

#8


引用 6 楼 gomoku 的回复:
TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

如果加上[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]这样能行吗?

#9


c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

#10


引用 9 楼 wyd1520 的回复:
c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了

#11


引用 10 楼 shuihan20e 的回复:
Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

#12


引用 11 楼 wyd1520 的回复:
Quote: 引用 10 楼 shuihan20e 的回复:

Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

那个问题已解决了,这是新问题,结构体套结构体数组的问题

#13


引用 7 楼 soaringbird 的回复:
Quote: 引用 5 楼 shuihan20e 的回复:

Quote: 引用 4 楼 soaringbird 的回复:

Quote: 引用 3 楼 shuihan20e 的回复:

Quote: 引用 2 楼 soaringbird 的回复:

public TLabelContent[] Content;
你确定这个地方不用再加修饰或者指明空间大小码?

这个地方已经指出了
for (int i = 0; i < 10; i++)                 playlistmsg.PlayListInfo.PlayList[i].Content = new TLabelContent[5]; 


c#结构里的数组仍然是引用类型,在内存里仍然是一个4字节的引用,而不是所有元素的所有空间

应该如何写呢?

类型定义你这么写也可以,但是封送和解析的地方,你还是按照报文的定义(类型、顺序、长度、字节序)来处理吧

但是提示错误啊
未处理的“System.ArgumentException”类型的异常出现在 MTWS.Net.exe 中。

其他信息: 未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配。

#14


引用 6 楼 gomoku 的回复:
TPlayList是不能封送(Marshal)的,原因是,缺少了SizeConst定义,CLR不知道TLabelContent[]到底要封送出几个,或封收几个。

一种比较简单直接做法是,自己做系列化,而不依赖于系统封送:
interface IPersistable
{
    void WriteTo(Stream stream);
    void ReadFrom(Stream stream);
}

class TLabelContent : IPersistable
{
   //...
}

class TPlayList : IPersistable
{
    public int ActTime;
    public int Action;
    public int Speed;
    public int ListNum;
    public TLabelContent[] Content;

    public void WriteToStream(Stream s)
    {
        if (Content == null || Content.Length != ListNum) throw new Exception("");

        foreach (int i in new int[] { ActTime, Action, Speed, ListNum })
        {
            byte[] bytes = BitConverter.GetBytes(i);
            s.Write(bytes, 0, bytes.Length);
        }
        foreach (IPersistable c in Content)
        {
            c.WriteTo(s);
        }
    }
    public void ReadFromStream(Stream s)
    {
        //...
        Content = new TLabelContent[ListNum];
        for (int i = 0; i < ListNum; i++)
        {
            Content[i] = new TLabelContent();
            Content[i].ReadFrom(s);
        }
    }
}

另外可否这样,声明lablecontent数组,赋值 ,然后把他们移动到一个连续内在里再发送

#15


引用 12 楼 shuihan20e 的回复:
Quote: 引用 11 楼 wyd1520 的回复:

Quote: 引用 10 楼 shuihan20e 的回复:

Quote: 引用 9 楼 wyd1520 的回复:

c#结构体中的结构体数组应该如何定义与初始化赋值

楼主过于依赖C++的结构体了。C#要实现C++的模式还真有点难度。

是啊,这C#我是半瓶子醋的水平,只好这样了


记得你有发过类拟的贴子。不是有跟你提过么。
你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。
然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

那个问题已解决了,这是新问题,结构体套结构体数组的问题


我的建议还是一样的,要不然你会发现C#强行实现C++的结构,到了后边会是个坑,而这个坑会越挖越深。。

C++多以指针操作,他可以容易实现结构的大小对应内存里的数据,C#这方面不怎么行。
就像内存共享一样。当然你要非得这样实现,只能慢慢去挖觉。我也会关注一下。可能以后会用到。

#16


记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用

#17


引用 16 楼 shuihan20e 的回复:
记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。


#18


引用 17 楼 wyd1520 的回复:
Quote: 引用 16 楼 shuihan20e 的回复:

记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。



感谢,问题已解决,还是用原来的方法,提示那个错误是因为位数不够,又没有自动补齐,您所说的那种访求,我也想尝试一下,可以加我QQ 78647557,晚上聊聊,C#方面还要多向你学习,

#19


C#想管理内存数据确实要命... 

#20


引用 18 楼 shuihan20e 的回复:
Quote: 引用 17 楼 wyd1520 的回复:

Quote: 引用 16 楼 shuihan20e 的回复:

记得你有发过类拟的贴子。不是有跟你提过么。 你把C++封装后的结构体转成byte[] 后打印出来, 看里面的字节是怎么组成的。 然后用C#的BinaryWrite 去组成C++转成byte[]后的字节就是了。这种方法是最可行的。

这种方式我考虑过,但是如果把他们连续的放在一起里,前面有人说过用范型,也是类似,可惜水平不行,不知道如何使用


你可以打印出C++的结构体
我打个比方
C++结构体

 public int X
 public long Y
 public String xxx
 public bool aa

这样的结构 在内存中
他是 
int 对应的是4个byte
long 对应的是8个byte
string 两种可能(字符长度+ string->Byte,另一种是 string->byte + \0 这是常见的一种)
bool 1个byte

所以结构变Byte[]就是 4+8+((string->Byte).Length +1) + 1
然后就把这个Byte直接发给C++服务端,就会认的出来了

所以我之前跟你提的先看看C++的结构体转成Byte是什么样的打印出来。,int long bool 都是一样的。
然后由C#自己用Byte组合,结构套结构 在C++里他们内存里的数据都是连继的会放在一起,C#是引用类型,
所以你得出的数据就不是你要的结果了。



感谢,问题已解决,还是用原来的方法,提示那个错误是因为位数不够,又没有自动补齐,您所说的那种访求,我也想尝试一下,可以加我QQ 78647557,晚上聊聊,C#方面还要多向你学习,


贴出结果呀。。哪里补齐

#21


所有的char数组都要补齐