您好,欢迎来到化拓教育网。
搜索
您的当前位置:首页C#结构体和字符数组转换

C#结构体和字符数组转换

来源:化拓教育网
C#结构体和字节数组的转换

在写C#TCP通信程序时,发送数据时,只能发送byte数组,处理起来比较麻烦不说,如果是和VC6.0等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把一个char[]数组转换为一个结构体,而在C#却不能直接把byte数组转换为结构体,要在C#中发送结构体,可以按以下方法实现: (1)定义结构体: //命名空间

using System.Runtime.InteropServices; //注意这个属性不能少

[StructLayoutAttribute(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)] struct TestStruct {

public int c;

//字符串,SizeConst为字符串的最大长度

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string str;

//int数组,SizeConst表示数组的个数,在转换成 //byte数组前必须先初始化数组,再使用,初始化

//的数组长度必须和SizeConst一致,例test = new int[6]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public int[] test; }

(2)结构体转byte数组: ////

/// 结构体转byte数组 ///

/// 要转换的结构体 /// 转换后的byte数组

public static byte[] StructToBytes(object structObj) {

//得到结构体的大小

int size = Marshal.SizeOf(structObj); //创建byte数组

byte[] bytes = new byte[size]; //分配结构体大小的内存空间

IntPtr structPtr = Marshal.AllocHGlobal(size); //将结构体拷到分配好的内存空间

Marshal.StructureToPtr(structObj, structPtr, false); //从内存空间拷到byte数组

Marshal.Copy(structPtr, bytes, 0, size); //释放内存空间

Marshal.FreeHGlobal(structPtr); //返回byte数组 return bytes;

}

(3)byte数组转结构体: ///

/// byte数组转结构体 ///

/// byte数组 /// 结构体类型 /// 转换后的结构体

public static object BytesToStuct(byte[] bytes,Type type) {

//得到结构体的大小

int size = Marshal.SizeOf(type); //byte数组长度小于结构体的大小 if (size > bytes.Length) {

//返回空 return null; }

//分配结构体大小的内存空间

IntPtr structPtr = Marshal.AllocHGlobal(size); //将byte数组拷到分配好的内存空间 Marshal.Copy(bytes,0,structPtr,size); //将内存空间转换为目标结构体

object obj = Marshal.PtrToStructure(structPtr, type); //释放内存空间

Marshal.FreeHGlobal(structPtr); //返回结构体 return obj; }

C#.NET和C++结构体Socket通信与数据转换

最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结 构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。 1、仿照C++结构体写出C#的结构来

1 using System.Runtime.InteropServices; 2

3 [Serializable] // 指示可序列化

4 [StructLayout(LayoutKind.Sequential, Pack = 1 )] // 按1字节对齐

5 public struct Operator 6 7 { 8 public ushort id; 9 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11 )] // 声明一个字符数组,大小为11 10 public char [] name; 11 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9 )] 12 public char [] pass; 13 14 public Operator( string user, string pass) // 初始化 15 { 16 this .id = 10000 ; 17 this .name = user.PadRight( 11 , ' /0 ' ).ToCharArray(); 18 this .pass = pass.PadRight( 9 , ' /0 ' ).ToCharArray(); 19 } 20 } 21 22 2、注意C#与C++数据类型的对应关系 C++与C#的数据类型对应关系表 API数据类型 类型描述 WORD LONG DWORD HANDLE UINT BOOL LPSTR LPCSTR BYTE 16位无符号整数 32位无符号整数 32位无符号整数 句柄,32位整数 32位无符号整数 32位布尔型整数 指向字符的32位指针 指向常字符的32位指针 字节 C#类型 API数据类型 ushort CHAR int uint int uint bool string String byte 类型描述 字符 C#类型 char long DWORDLONG 位长整数 HDC HGDIOBJ HINSTANCE HWM HPARAM LPARAM WPARAM 设备描述表句柄 int GDI对象句柄 实例句柄 窗口句柄 32位消息参数 32位消息参数 32位消息参数 int int int int int int 整个结构的字节数是22bytes。 对应的C++结构体是:

1 typedef struct 2 {

3 WORD id; 4 CHAR name[ 11 ]; 5 CHAR password[ 9 ]; 6 } Operator; 7 8

3、发送的时候先要把结构转换成字节数组

1 using System.Runtime.InteropServices; 2

3 ///

4 /// 将结构转换为字节数组 5 ///

6 /// 结构对象 7 /// 字节数组 8 public byte [] StructToBytes( object obj) 9 {

10 // 得到结构体的大小

11 int size = Marshal.SizeOf(obj); 12 // 创建byte数组

13 byte [] bytes = new byte [size]; 14 // 分配结构体大小的内存空间

15 IntPtr structPtr = Marshal.AllocHGlobal(size); 16 // 将结构体拷到分配好的内存空间

17 Marshal.StructureToPtr(obj, structPtr, false ); 18 // 从内存空间拷到byte数组

19 Marshal.Copy(structPtr, bytes, 0 , size); 20 // 释放内存空间

21 Marshal.FreeHGlobal(structPtr);

22 // 返回byte数组 23 return bytes; 24 25 } 26 27

接收的时候需要把字节数组转换成结构

1 ///

2 /// byte数组转结构 3 ///

4 /// byte数组 5 /// 结构类型 6 /// 转换后的结构

7 public object BytesToStruct( byte [] bytes, Type type) 8 {

9 // 得到结构的大小

10 int size = Marshal.SizeOf(type); 11 Log(size.ToString(), 1 ); 12 // byte数组长度小于结构的大小 13 if (size > bytes.Length) 14 {

15 // 返回空 16 return null ; 17 }

18 // 分配结构大小的内存空间

19 IntPtr structPtr = Marshal.AllocHGlobal(size); 20 // 将byte数组拷到分配好的内存空间 21 Marshal.Copy(bytes, 0 , structPtr, size); 22 // 将内存空间转换为目标结构

23 object obj = Marshal.PtrToStructure(structPtr, type); 24 // 释放内存空间

25 Marshal.FreeHGlobal(structPtr); 26 // 返回结构

27 return obj; 28 } 29

4、实际操作:

1 using System.Collections; 2 using System.Collections.Generic; 3 using System.Net; 4 using System.Net.Sockets; 5

6 byte [] Message = StructToBytes( new Operator( \" user \" , \" pass \" )); // 将结构转换成字节数组 7

8 TcpClient socket = new TcpClient(); 9

10 socket.Connect(ip,port); 11

12 NetworkStream ns = Socket.GetStream(); 13

14 ns.Write(Message, 0 ,Message.Length); // 发送 15

16 byte [] Recv = new byte [ 1024 ]; // 缓冲 17

18 int NumberOfRecv = 0 ; 19

20 IList < byte > newRecv = new List < byte > (); 21 ns.ReadTimeout = 3000 ; 22 try 23 { 24 do 25 {

26 // 接收响应

27 NumberOfRecv = ns.Read(Recv, 0 , Recv.Length); 28 for ( int i = 0 ; i < NumberOfRecv; i ++ )

29 newRecv.Add(Recv[i]); 30 }

31 while (ns.DataAvailable);

32 byte [] resultRecv = new byte [newRecv.Count]; 33 newRecv.CopyTo(resultRecv, 0 ); 34

35 Operator MyOper = new Operator(); 36

37 MyOper = (Operator)BytesToStruct(resultRecv, MyOper.GetType()); // 将字节数组转换成结构 38

在这里取值的时候可能会出现只能取到一个字段,剩余的取不到的问题,怎么回事我也搞不懂,反正我的解决办法就是按照字节的顺序从 resultRecv里分别取出对应的字段的字节数组,然后解码,例如:

Operator.name是11个字节,最后一位是0,Operator.id是2个字节,那么从第3位到第12位的字节就是 Operator.name的内容,取出另存为一个数组 MyOperName,Encoding.Default.GetString(MyOperName)就是MyOper.name的内容。

1 socket.Close(); 2

3 ns.Close(); 4

在C++中,套接字发送和接收的类型为字符数组char[],而char[]与结构体struct可以直接进行显式转换就可以;

在C#中,Sockets类和NetworkStream类发送和接收的类型为字节数组byte[],而它与结构体的转换并不是非常方便,因此也就有了下文 知识储备:

(1) 需要用到Marshal类,该类提供了一个方法集,这些方法用于分配非托管内存、复制非托管内存块、将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法。

(2) 代码的输入和输出都是托管类型(疑问:结构是值类型,也是托管类型??) (3) 中间需要用非托管类型作为内容的中转; 下面是代码:

从结构体转到字节数组:

[csharp] view plaincopy

1. //byte[]是托管的,structObj是托管的,structPtr是非托管的 2. //从struct转换到byte[]

3. public static byte[] StructToBytes(object structObj) 4. {

5. //返回类的非托管大小(以字节为单位) 6. int size = Marshal.SizeOf(structObj); 7.

8. //分配大小

9. byte[] bytes = new byte[size]; 10.

11. //从进程的非托管堆中分配内存给structPtr

12. IntPtr structPtr = Marshal.AllocHGlobal(size); 13.

14. //将数据从托管对象structObj封送到非托管内存块structPtr 15. Marshal.StructureToPtr(structObj, structPtr, false); 16.

17. //Marshal.StructureToPtr(structObj, structPtr, true); 18. //将数据从非托管内存指针复制到托管 8 位无符号整数数组 19. Marshal.Copy(structPtr, bytes, 0, size); 20.

21. //释放以前使用 AllocHGlobal 从进程的非托管内存中分配的内存 22. Marshal.FreeHGlobal(structPtr); 23. return bytes; 24. }

从字节数组转到结构体(strType指明了所要转换的结构体类型):

[csharp] view plaincopy

1. //返回类型其实没什么用,从bytes转为strType类型的结构体 2. //从byte[]转换为struct

3. public static object BytesToStruct(byte[] bytes, Type strType) 4. {

5. //获取结构体的大小(以字节为单位)

6. int size = Marshal.SizeOf(strType); 7. //简单的判断(可以去掉) 8. if (size > bytes.Length) 9. {

10. return null; 11. } 12.

13. //从进程的非托管堆中分配内存给structPtr 14. IntPtr strPtr = Marshal.AllocHGlobal(size); 15.

16. //将数据从一维托管数组bytes复制到非托管内存指针strPtr 17. Marshal.Copy(bytes, 0, strPtr, size); 18.

19. //将数据从非托管内存块封送到新分配的指定类型的托管对象 20. //将内存空间转换为目标结构体

21. object obj = Marshal.PtrToStructure(strPtr, strType); 22.

23. //释放以前使用 AllocHGlobal 从进程的非托管内存中分配的内存 24. Marshal.FreeHGlobal(strPtr); 25. return obj; 26. }

从字节数组类型转为结构体类型(返回值从object直接转为了structType类型的对象)

[csharp] view plaincopy

1. //从字节数组转化为结构体

2. public StructType ConverBytesToStructure(byte[] bytesBuffer) 3. {

4. // 检查长度

5. if (bytesBuffer.Length != Marshal.SizeOf(typeof(StructType))) 6. {

7. throw new ArgumentException(\"bytesBuffer参数和structObject参数字节长

度不一致。\"); 8. } 9.

10. //分配一个未托管类型变量

11. IntPtr bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length); 12.

13. //逐个复制,也可以直接用copy()方法

14. for (int index = 0; index < bytesBuffer.Length; index++) 15. {

16. Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]); 17. }

18.

19. //从非托管类型转化为托管类型变量

20. StructType structObject = (StructType)Marshal.PtrToStructure(bufferHandl

er, typeof(StructType)); 21.

22. //释放非托管类型变量

23. Marshal.FreeHGlobal(bufferHandler); 24.

25. return structObject; 26. }

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo9.cn 版权所有 赣ICP备2023008801号-1

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务