搜索
bottom↓
回复: 2

C/C++中的结构体转换到C#

[复制链接]

出0入0汤圆

发表于 2011-8-17 13:09:29 | 显示全部楼层 |阅读模式
在做项目移植的时候,经常会碰到数据类型的转换,而这一次碰到的是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周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2011-8-19 17:44:35 | 显示全部楼层
        /// &lt;summary&gt;
        /// 结构体转Byte数组
        /// &lt;/summary&gt;
        /// &lt;param name="StructObj"&gt;&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public static Byte[] StructToByteA( object StructObj )
        {

            IntPtr structPtr;
            //得到结构体的大小
            int size = Marshal.SizeOf(StructObj);
            Byte[] bytes = new byte[size];
            //分配结构体大小的内存空间
            structPtr = Marshal.AllocHGlobal(size);
            //将结构体拷到分配好的内存空间
            Marshal.StructureToPtr(StructObj, structPtr, false);
            Marshal.Copy(structPtr, bytes, 0, size);
            Marshal.FreeHGlobal(structPtr);
            return
bytes;
        }


        public static
void ByteToStruct&lt;T&gt;( Byte[] bytes, out T ReObj ) where T : struct
        {
            
            Int32 size = Marshal.SizeOf(typeof(T));
            Int32 CopyLenght = size;
            IntPtr structPtr = Marshal.AllocHGlobal(size);
            if
(size &gt; bytes.Length)
            {

                /*return null;*/

                for
(Int32 i = 0; i &lt; (size - bytes.Length); i++)
                {

                    Marshal.WriteByte(structPtr, (bytes.Length + i), 0);
                }

                CopyLenght = bytes.Length;
            }

            Marshal.Copy(bytes, 0, structPtr, CopyLenght);
            //将内存空间转换为目标结构体
            T Sobj = (T)Marshal.PtrToStructure(structPtr, typeof(T));
            ReObj = Sobj<font color="#000000">;
        }

我两个函数怎么样,加入了泛型的特性

出0入0汤圆

 楼主| 发表于 2011-8-19 18:05:03 | 显示全部楼层
呵呵,不错啊。我就用的这个实现的一个上位机抄表小软件。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-22 23:09

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表