|
在做项目移植的时候,经常会碰到数据类型的转换,而这一次碰到的是C/C++中的结构怎样转换到C#。 C/C++中的结构类型数据在C#下的转换
在做项目移植的时候,经常会碰到数据类型的转换,而我这一次碰到的是C/C++中的结构怎样转换到C#。例如我们在C/C++下的结构数据如下:
typedef struct
{
char sLibName[ 256 ];
char sPathToLibrary[ 256 ];
INT32 iEntries;
INT32 iUsed;
UINT16 iSort;
UINT16 iVersion;
BOOLEAN fContainsSubDirectories;
INT32 iReserved;
} LIBHEADER;
我们想把它转成C#下的结构类型如下:
public struct LIBHEADER
{
public char[] sLibName;
public char[] sPathToLibrary;
public Int32 iEntries;
public Int32 iUsed;
public UInt16 iSort;
public UInt16 iVersion;
public Boolean fContainsSubDirectories;
public Int32 iReserved;
}
看上去好像没问题了,呵呵呵,其实这样是不行的,我们得再给C#编译器一些信息,告诉它一些字符数组的大小。然后它们在C#下面长得样子就变成这样:
[StructLayout(LayoutKind.Sequential)]
public struct LIBHEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] sLibName;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public char[] sPathToLibrary;
public Int32 iEntries;
public Int32 iUsed;
public UInt16 iSort;
public UInt16 iVersion;
public Boolean fContainsSubDirectories;
public Int32 iReserved;
}
然后写一个函数负责转换。
public StructType ConverBytesToStructure<StructType>(byte[] bytesBuffer)
{
// 检查长度。
if (bytesBuffer.Length != Marshal.SizeOf(typeof(StructType)))
{
throw new ArgumentException("bytesBuffer参数和structObject参数字节长度不一致。");
}
IntPtr bufferHandler = Marshal.AllocHGlobal(bytesBuffer.Length);
for (int index = 0; index < bytesBuffer.Length; index++)
{
Marshal.WriteByte(bufferHandler, index, bytesBuffer[index]);
}
StructType structObject = (StructType)Marshal.PtrToStructure(bufferHandler, typeof(StructType));
Marshal.FreeHGlobal(bufferHandler);
return structObject;
}
然后我们的函数用例是这样:
FileStream file = File.OpenRead(@"D:\Jagged Alliance 2 Gold\INSTALL.LOG");
byte[] buffer = new byte[Marshal.SizeOf(typeof(LIBHEADER))];
file.Read(buffer, 0, buffer.Length);
LIBHEADER testValue = CommonTools.ConverBytesToStructure<LIBHEADER>(buffer);
string libName = new string(testValue.sLibName);
string pathToLibrary= new string(testValue.sPathToLibrary);
OK,搞定。
如果想去掉后面两句的char数组的转换哪代码如下
C#中的结构代码
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct LIBHEADER
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string sLibName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string sPathToLibrary;
public Int32 iEntries;
public Int32 iUsed;
public UInt16 iSort;
public UInt16 iVersion;
public Boolean fContainsSubDirectories;
public Int32 iReserved;
}
其它代码不用作修改便可使用。
(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数组:
//// <summary>
/// 结构体转byte数组
/// </summary>
/// <param name="structObj">要转换的结构体</param>
/// <returns>转换后的byte数组</returns>
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数组转结构体:
/// <summary>
/// byte数组转结构体
/// </summary>
/// <param name="bytes">byte数组</param>
/// <param name="type">结构体类型</param>
/// <returns>转换后的结构体</returns>
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;
}
以下为测试的一个例子:
namespace testcomm
{
//注意这个属性不能少
[StructLayoutAttribute(
LayoutKind.Sequential,
CharSet=CharSet.Ansi,Pack=1)]
struct TransData
{
public byte len; //长度
public byte ctrl; //控制字
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] //int数组,SizeConst表示数组的个数,在转换成 byte数组前必须先初始化数组,再使用,初始化的数组长度必须和SizeConst一致,例test = new int[6];
public byte[] REAddr;//unsigned long int meterid; //表编号
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public byte[] DSTAddr; //unsigned long int columnid; //抄表列编号
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public byte[] cumulate; //unsigned long int cumulate; //累积量
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]
public byte[] left; //signed long int left; //剩余气量
public byte csValve1; //控制状态
public byte csValve2;
public byte csLeftClose;
public byte csLeftClue;
public byte csFeeType;
public byte csLeak;
public byte csUnlaw;
public byte csBattery;
public byte ver; //软件版本
public byte verify; //和校验
/*public TransData(int n)
{
REAddr = new byte[n];
DSTAddr = new byte[n];
cumulate = new byte[n];
left = new byte[n];
len = 0;
ctrl = 0;
csBattery = 0;
csFeeType = 0;
csLeak = 0;
csLeftClose = 0;
csLeftClue = 0;
csValve1 = 0;
csValve2 = 0;
csUnlaw = 0;
ver = 0;
verify = 0;
}*/
}; //定义的结构体类型
public partial class Form1 : Form
{
private StringBuilder builder = new StringBuilder();//避免在事件处理方法中反复的创建,定义到外面。
private long received_count = 0;//接收计数
private long send_count = 0;//发送计数
private bool Listening = false; //是否没有执行完invoke相关操作
private bool Closing1 = false;//是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke
public Form1()
{
InitializeComponent();
//txSend.Text = "10120276";
txSend.Text = "00000000";
}
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;
}
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;
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Text ="com1";
comboBox2.Text = "9600";
serialPort1.BaudRate = 9600;
serialPort1.PortName = "com1";
serialPort1.DataBits = 8;
serialPort1.StopBits = StopBits.One;
serialPort1.NewLine="/r/n";
serialPort1.Encoding = Encoding.GetEncoding("Gb2312");//必须的,否则中文显示乱码
}
private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
if (Closing1) return;//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循
try{
Listening = true;//设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
int n = serialPort1.BytesToRead;//先记录下来,避免某种原因,人为的原因,操作几次之间时间长,缓存不一致
// byte[] buf = new byte[n];//声明一个临时数组存储当前来的串口数据
char[] buf= new char[n];//必须的,声明为char 型,而非byte型数组,否则中文显示乱码
received_count += n;//增加接收计数
serialPort1.Read(buf, 0, n);//读取缓冲数据
//builder.Clear();//清除字符串构造器的内容
// builder.Remove(0,n);
builder.Length = 0;//清除字符串构造器的内容
this.Invoke((EventHandler)(delegate
{
if (checkBoxHexView.Checked) //判断是否是显示为16禁止
{
//依次的拼接出16进制字符串
foreach (byte b in buf)
{
builder.Append(b.ToString("X2") + " "); //X代表十六进制,2代表2个为一组长度
}
}
else
{
// builder.Append(System.Text.Encoding.Default.GetString(buf)); //直接按ASCII规则转换成字符串
// builder.Append(Encoding.Unicode.GetString(buf)); //
// builder.Append(Encoding.GetEncoding("gb2312").GetString(buf));
// string str = Convert.ToString(buf);
// builder.Append(buf);
builder.Append(buf);
}
//追加的形式添加到文本框末端,并滚动到最后。
textBox1.AppendText(builder.ToString());
//修改接收计数
labelGetCount.Text = "Get:" + received_count.ToString()+" Bytes";
}));
}
finally
{
Listening = false;//我用完了,ui可以关闭串口了。
}
/* TransData trans1 = new TransData();
int n = serialPort1.BytesToRead;//
byte[] buf = new byte[48];//声明一个临时数组存储当前来的串口数据
received_count += n;//增加接收计数
serialPort1.Read(buf, 0, n);//读取缓冲数据
this.Invoke((EventHandler)(delegate {})
// labelGetCount.Text = "Get:" + received_count.ToString() + " Bytes";
Type t = typeof(TransData);
trans1 = (TransData)Form1.BytesToStuct(buf, t);
string str,t1;
str = "表编号:";
t1 = "";
byte[] dat = new byte[10];
Array.Copy(dat, 0, buf, 2, 8);//原机编号
foreach (byte b in dat) { t1 += b.ToString(); }
str+=t1 ;
textBox1.Text = str;*/
}
private void button3_Click(object sender, EventArgs e)
{
if (button3.Text == "打开串口")
{
try
{
serialPort1.Open();
MessageBox.Show("打开串口成功");
if (serialPort1.IsOpen)
{
//serialPort1.Close();
button3.Text = "关闭串口";
}
else button3.Text = "打开串口";
}
catch
{
MessageBox.Show("串口已经打开");
}
}
else
{
// Closing1 = true;
// while(Listening) Application.DoEvents();
serialPort1.Close();
button3.Text = "打开串口";
MessageBox.Show("串口已经关闭");
}
}
private void button4_Click(object sender, EventArgs e)
{
//this.Close();
// this.Dispose();
Application.Exit();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if(comboBox1 .Text=="com1")
{
serialPort1.PortName = "com1";
// MessageBox.Show("com1 is selected!");
}
else if (comboBox1.Text == "com2")
{
serialPort1.PortName = "com2";
MessageBox.Show("com2 is selected!");
}
else if (comboBox1.Text == "com3")
{
serialPort1.PortName = "com3";
MessageBox.Show("com3 is selected!");
}
else if (comboBox1.Text == "com4")
{
serialPort1.PortName = "com4";
MessageBox.Show("com4 is selected!");
}
else if (comboBox1.Text == "com5")
{
serialPort1.PortName = "com5";
MessageBox.Show("com5 is selected!");
}
else if (comboBox1.Text == "com6")
{
serialPort1.PortName = "com6";
MessageBox.Show("com6 is selected!");
}
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox2.Text == "9600")
{
serialPort1.BaudRate= 9600;
// MessageBox.Show("com1 is selected!");
}
else if (comboBox2.Text == "4800")
{
serialPort1.BaudRate = 4800;
MessageBox.Show("4800 is selected!");
}
else if (comboBox2.Text == "5600")
{
serialPort1.BaudRate = 5600;
MessageBox.Show("5600 is selected!");
}
else if (comboBox2.Text == "19200")
{
serialPort1.BaudRate = 19200;
MessageBox.Show("19200 is selected!");
}
else if (comboBox2.Text == "115200")
{
serialPort1.BaudRate= 115200;
MessageBox.Show("115200 is selected!");
}
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Text = "";
}
private void btnsend_Click(object sender, EventArgs e)
{
int n = 0; //定义一个变量,记录发送了几个字节
//16进制发送
string hexOutput = "";
if (checkBoxHexSend.Checked)
{
/*
//我们不管规则了。如果写错了一些,我们允许的,只用正则得到有效的十六进制数
MatchCollection mc = Regex.Matches(txSend.Text, @"(?i)[/da-f]{2}");
List<byte> buf = new List<byte>();//填充到这个临时列表中
//依次添加到列表中
foreach (Match m in mc)
{
buf.Add(byte.Parse(m.Value, System.Globalization.NumberStyles.HexNumber));
}
//转换列表为数组后发送
serialPort1.Write(buf.ToArray(), 0, buf.Count);
//记录发送的字节数
n = buf.Count;
*/
char[] values = txSend.Text.ToCharArray();
//txSend.Text.ToCharArray(
foreach (char letter in values)
{
int value = Convert.ToInt32(letter);
hexOutput += String.Format("{0:X}", value);
}
serialPort1.Write(hexOutput);
//txSend.Text = hexOutput;
n = txSend.Text.Length;
}
else//ascii编码直接发送
{
serialPort1.Write(txSend.Text);
n = txSend.Text.Length;
}
send_count += n;//累加发送字节数
labelSendCount.Text = "Send:" + send_count.ToString()+" Bytes";//更新界面
}
private void btnclnum_Click(object sender, EventArgs e)
{
//复位接受和发送的字节数计数器并更新界面。
send_count = received_count = 0;
labelGetCount.Text = "Get:0";
labelSendCount.Text = "Send:0";
}
private void btntest_Click(object sender, EventArgs e)
{
try
{
int n = 0; //定义一个变量,记录发送了几个字节
TransData trans = new TransData();
trans.len = 47;
trans.verify = 255;
// trans.DSTAddr[0] =1;//不注释掉为什么会出错?提示未将对象引用设置到对象的实例
byte[] bufsend=new byte[48];
bufsend[0] = 47;
bufsend[1] = 0x18;
bufsend[47]=255;
//txSend.Text = "10120276";
byte[] dat = System.Text.Encoding.ASCII.GetBytes(txSend.Text); //获取文本框中字符串存储到byte数组
//byte[] dat = BitConverter.GetBytes(int.Parse(textBox1.Text,);
// byte[] dat = BitConverter.GetBytes(Convert.ToInt32(textBox1.Text));
// bufsend[2] = dat[0];
// bufsend[3] = dat[1];
// bufsend[4] = dat[2];
// bufsend[5] = dat[3];
Array.Copy(dat,0 ,bufsend ,2,8);//原机编号
Array.Copy(dat, 0, bufsend, 11, 8);//目的机编号
// textBox1.Text.CopyTo(0,bufsend ,2,8);
Type t = typeof(TransData);
trans = (TransData)Form1.BytesToStuct(bufsend, t);
byte[] buf = Form1.StructToBytes(trans);
string t1="";
if (checkBoxHexView.Checked)
{
//依次的拼接出16进制字符串
foreach (byte b in buf)
{
t1 +=b.ToString("X2") + " "; //X代表十六进制,2代表2个为一组长度
}
}
else
foreach (byte b in buf) { t1 += b.ToString("") + " "; }
// textBox1.Text = t1;
serialPort1.Write(buf,0,buf.Length);//发送单抄数据
n = buf.Length;
send_count += n;//累加发送字节数
labelSendCount.Text = "Send:" + send_count.ToString() + " Bytes";//更新界面
}
catch (Exception ex)
{
MessageBox.Show ( ex.ToString());
}
}
}
} |
阿莫论坛20周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|