Gorgon_Meducer 发表于 2010-3-21 11:44:54

[C#][学习笔记]一个可以存/取HEX文件的类HexFileStream

[说在前面的话]
    最近要开发一个类似programmer的软件模块,自然要牵涉到Hex文件的读取和写入,
于是我习惯性的先搜索各大网站,想寻找前人的肩膀,无奈,搜索的结果很出乎我意料,
难道C#程序员都不需要读取Hex文件么?这应该是微软System.IO的一部分吧?
    然而,想当然归想想当然,后来仔细一想,天下文件格式多了去了,微软才不管你
死活呢,要写,估计也是Intel写吧。求人不如求己,于是自己写了一个。
    受水猫启发,单单发一个东西给大家用或者等着喊牛是要挨打的。于是,我决定将
自己的思维过程和开发过程简单的拆解开来,分阶段的接受群众的监督和检查。但不管
怎么说,先放一个能用的HexFileStream类才是王道。

<font color=red>[使用方法]
    1、通过Add reference方法,加入对HexFileStream.dll的引用。

    2、在代码中加入对命名空间的引用:
       using Utilities.IO;

    3、在代码中建立一个HexFileStream
                HexFileStream tStream = null;
                try
                {
                  tStream = new HexFileStream(openFileDialog1.FileName, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite);
                }
                catch (Exception Err)
                {
                  Err.ToString();
                  return;
                }
    4、设置存储器空间的大小——比如你用M128,你就要设置存储器空间大小
       为128 * 1024,当然,如果空间大小是可变的,那么就设置0
       tStream.SetLength(0);

    5、读取所需的数据,并显示出来
       Byte[] tBuffer = new Byte;
      
       //! 和Stream的用法相同
       tStream.Read(tBuffer, 0, tBuffer.Length);

       //! m_bvExample是一个ByteViewer,ByteViewer是微软提供的一个类,用以将二进制
       //! 数组以Hex字符串的方式显示在窗体上。
       m_bvExample.SetBytes(tBuffer);

[版本说明]
   
    v0.50 由于偷懒,目前版本仅仅支持各类Hex文件的读取,暂不支持写入,也不支持异步读取。
          构造函数目前仅允许使用文件路径来创建HexFileStream,暂不支持使用文件指针。
          构造函数必须指定操作方法为Read或者ReadWrite,否则,任何Read操作都将导致异常。

[相关下载]
    1、点击此处下载HexFileStream.dll ourdev_539960.rar(文件大小:11K) (原文件名:HexFileStream.rar)
    2、点击此处下载测试工程ourdev_539962.rar(文件大小:61K) (原文件名:HexFileStreamTester.rar)

Gorgon_Meducer 发表于 2010-3-21 11:45:07

“情况不明的时候,先从必须要做的东西开始入手”
    开发一个较大的项目,由于最终实现的功能可能较为复杂,则在项目的一开始就要求
较为精确的规划,对很多初学者来说,是相当有难度得,也不现实。比较现实的做法是:
<font color=blue>从必须要做的事情入手,然后将遇到的新问题分类,再次找到必须要做的事情,最终完成
大部分工作。
    这种开发方式有其局限性,不过很容易通过经验的积累来弥补这种局限。至于是什么
局限,开发的经验会告诉你。

    废话少说,一个HexFileStream最重要且最先要做的,当然是能够从Hex文件中解码出
我们需要的数据啦。所以,做一个Record类不用犹豫。

Record.h
using System;
using System.Collections.Generic;
using System.Text;

namespace Utilities.IO
{

    //! \name record structure in Intel-Hexadecimal file
    //! @{
    internal abstract class Record
    {
      protected Boolean m_bAvailable = false;

      //! \brief property for check whether this record is available or not.
      public Boolean Available
      {
            get { return m_bAvailable; }
      }


      //! \name record type
      //! @{
      internal enum Type : byte
      {
            DATA_RECORD                     = 0,            //!< data record
            END_OF_FILE_RECORD            = 1,            //!< end of file record
            EXTEND_SEGMENT_ADDRESS_RECORD   = 2,            //!< extend segment address record
            START_SEGMENT_ADDRESS_RECORD    = 3,            //!< start segment address record
            EXTEND_LINEAR_ADDRESS_RECORD    = 4,            //!< extend linear address record
            START_LINEAR_ADDRESS_RECORD   = 5             //!< start linear address record
      }
      //! @}

      //! \brief property for getting record type
      /*! \note Each record has a RECTYP field which specifies the record type of this record.
         *      The RECTYP field is used to interpret the remaining information within the
         *      record. The encoding for all the current record types are:
         *          ’00’ Data Record
         *          ’01’ End of File Record
         *          ’02’ Extended Segment Address Record
         *          ’03’ Start Segment Address Record
         *          ’04’ Extended Linear Address Record
         *          ’05’ Start Linear Address Record
         */
      public abstract Type RecordType
      {
            get;
      }

      /*! \note Each record has a RECLEN field which specifies the number of bytes of
         *      information or data which follows the RECTYP field of the record. Note that
         *      one data byte is represented by two ASCII characters. The maximum value of
         *      the RECLEN field is hexadecimal ’FF’ or 255.
         */
      public abstract Int32 RecordLength
      {
            get;
      }

      /*! \note Each record has a LOAD OFFSET field which specifies the 16-bit starting load
         *      offset of the data bytes, therefore this field is only used for Data Records.
         *      In other records where this field is not used, it should be coded as four
         *      ASCII zero characters (’0000’ or 03030303OH).
         */
      public virtual UInt16 LoadOffset
      {
            get { return 0; }
      }

      /*! \note Each record has a variable length INFO/DATA field, it consists of zero or
         *      more bytes encoded as pairs of hexadecimal digits. The interpretation of
         *      this field depends on the RECTYP field.
         */
      public abstract Byte[] Data
      {
            get;
      }


      //! \brief method to parse record string
      //! \param tHexRecord a record string
      //! \return a reference to a new record object
      public static Record Parse(String tHexRecord)
      {
            //! check input
            if (null == tHexRecord)
            {
                return null;
            }
            
            tHexRecord = tHexRecord.Trim().ToUpper();
            if ("" == tHexRecord)
            {
                return null;
            }

            //! \brief check record head
            if (':' != tHexRecord)
            {
                return null;
            }
            if (!IsHexNumber(tHexRecord.Substring(1)))
            {
                return null;
            }

            Int32 tRecordLength = 0;
            Int32 tCheckSUM = 0;
            Int32 tLoadOffSet = 0;
            Byte tRecordType = 0;
            Byte[] tData = null;

            //! try to get record length
            try
            {
                tRecordLength = Int32.Parse(tHexRecord.Substring(1, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return null;
            }
            tCheckSUM += tRecordLength;

            //! check record string length
            if (tHexRecord.Length < (11 + tRecordLength * 2))
            {
                //! incomplete record
                return null;
            }

            //! try to get LoadOffset
            try
            {
                tLoadOffSet = UInt16.Parse(tHexRecord.Substring(3,4), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return null;
            }
            tCheckSUM += tLoadOffSet >> 8;
            tCheckSUM += tLoadOffSet & 0xFF;

            //! try to get record type
            try
            {
                tRecordType = Byte.Parse(tHexRecord.Substring(7, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return null;
            }

            //! check record type value
            if (tRecordType > 5)
            {
                return null;
            }
            else if (
                        ((Type)tRecordType != Type.DATA_RECORD)
                  &&(tLoadOffSet != 0)
                  )
            {
                //! illegal record loadoffset
                return null;
            }

            tCheckSUM += tRecordType;
            
            //! get data byte
            tData = new Byte;
            if (null == tData)
            {
                //! failed to allocated memory
                return null;
            }

            //! read all data bytes in a record
            for (Int32 n = 0; n < tData.Length; n++)
            {
                try
                {
                  tData = Byte.Parse(tHexRecord.Substring(9 + n * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
                }
                catch (Exception Err)
                {
                  Err.ToString();
                  return null;
                }
                tCheckSUM += tData;
            }

            //! get check sum
            try
            {
                tCheckSUM += Byte.Parse(tHexRecord.Substring(9 + tData.Length * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return null;
            }
            if (0 != (tCheckSUM & 0xFF))
            {
                //! check sum error
                return null;
            }

            //! \brief check record type
            switch ((Type)tRecordType)
            {
                case Type.DATA_RECORD:                              //!< data record
                  return new DataRecord((UInt16)tLoadOffSet, tData);

                case Type.END_OF_FILE_RECORD:                     //!< EOF record
                  return new EndOfFileRecord();

                case Type.EXTEND_LINEAR_ADDRESS_RECORD:             //!< extend linear address record
                  return new ExtendLinearAddressRecord(tData);   

                case Type.EXTEND_SEGMENT_ADDRESS_RECORD:            //!< extend segment address
                  return new ExtendSegmentAddressRecord(tData);   

                case Type.START_LINEAR_ADDRESS_RECORD:            //!< start linear address record
                  return new StartLinearAddressRecord(tData);   

                case Type.START_SEGMENT_ADDRESS_RECORD:
                  return new StartSegmentAddressRecord(tData);      //!< start segment address

                default:
                  //! brief no such type
                  return null;
            }
            
            return null;
      }

      //! \brief get HEX record String
      public override String ToString()
      {
            StringBuilder tsbHexRecord = new StringBuilder();
            if (m_bAvailable)
            {
                return null;
            }

            UInt32 tCheckSUM = 0;

            //! record head
            tsbHexRecord.Append(':');

            //! record length
            tsbHexRecord.Append(this.RecordLength.ToString("X2"));
            tCheckSUM += (UInt32)RecordLength;

            //! record load offset
            tsbHexRecord.Append(this.LoadOffset.ToString("X4"));
            tCheckSUM += (UInt32)(LoadOffset & 0x00FF);
            tCheckSUM += (UInt32)(LoadOffset >> 8);

            //! record type
            tsbHexRecord.Append(((Byte)this.RecordType).ToString("X2"));
            tCheckSUM += (Byte)RecordType;

            //! record data
            foreach (Byte tItem in Data)
            {
                tsbHexRecord.Append(tItem.ToString("X2"));
                tCheckSUM += tItem;
            }

            //! record check sum
            tsbHexRecord.Append(((Byte)((UInt16)0x100 - (UInt16)(tCheckSUM & 0xFF))).ToString("X2"));

            return tsbHexRecord.ToString();
      }


      //! \brief method for check whecher string only consist of hex value
      static public Boolean IsHexNumber(String tHexString)
      {
            if (null == tHexString)
            {
                return false;
            }

            tHexString = tHexString.Trim().ToUpper();
            if ("" == tHexString)
            {
                return false;
            }

            foreach (Char tSymble in tHexString.ToCharArray())
            {
                if (!Char.IsNumber(tSymble))
                {
                  if ((tSymble < 'A') || (tSymble > 'F'))
                  {
                        return false;
                  }
                }
            }

            return true;
      }

    }
    //! @}

    /*! \note The End of File Record specifies the end of the hexadecimal object
   *      file.
   */
    //! \name the End of File record
    //! @{
    internal class EndOfFileRecord : Record
    {
      //! \brief length always is zero.
      public override int RecordLength
      {
            get { return 0; }
      }


      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.END_OF_FILE_RECORD; }
      }

      //! \brief no data
      public override byte[] Data
      {
            get
            {
                return new Byte;
            }
      }
    }
    //! @}

    /*! \note The 32-bit Extended Linear Address Record is used to specify bits
   *      16-31 of the Linear Base Address (LBA), where bits 0-15 of the LBA
   *      are zero. Bits 16-31 of the LBA are referred to as the Upper Linear
   *      Base Address (ULBA). The absolute memory address of a content byte
   *      in a subsequent Data Record is obtained by adding the LBA to an
   *      offset calculated by adding the LOAD OFFSET field of the containing
   *      Data Record to the index of the byte in the Data Record (0, 1, 2,
   *      ... n). This offset addition is done modulo 4G (i.e., 32-bits),
   *      ignoring any carry, so that offset wrap-around loading (from
   *      OFFFFFFFFH to OOOOOOOOOH) results in wrapping around from the end
   *      to the beginning of the 4G linear address defined by the LBA. The
   *      linear address at which a particular byte is loaded is calculated
   *      as:
   *      
   *      (LBA + DRLO + DRI) MOD 4G
   *      
   *      where:
   *      DRLO is the LOAD OFFSET field of a Data Record.
   *      DRI is the data byte index within the Data Record.
   *      
   *      When an Extended Linear Address Record defines the value of LBA,
   *      it may appear anywhere within a 32-bit hexadecimal object file.
   *      This value remains in effect until another Extended Linear Address
   *      Record is encountered. The LBA defaults to zero until an Extended
   *      Linear Address Record is encountered.
   */
    //! \name the Extend Linear Address Record
    //! @{
    internal class ExtendLinearAddressRecord : Record
    {
      private UInt16 m_UpperLinearBaseAddress = 0;
      private Byte[] m_Data = null;

      public ExtendLinearAddressRecord(Byte[] tData)
      {
            if (null == tData)
            {
                return;
            }
            else if (2 != tData.Length)
            {
                return ;
            }

            m_Data = tData;

            try
            {
                Byte[] tTemp = new Byte;
                tTemp = tData;
                tTemp = tData;

                m_UpperLinearBaseAddress = BitConverter.ToUInt16(tTemp, 0);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return;
            }

            m_bAvailable = true;
      }

      //! \brief record data length should always be 2
      public override Int32 RecordLength
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }
                return 2;
            }
      }


      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.EXTEND_LINEAR_ADDRESS_RECORD; }
      }

      //! \brief get data
      public override Byte[] Data
      {
            get
            {
                if (!m_bAvailable)
                {
                  return null;
                }

                return m_Data;
            }
      }

      //! \brief get the Upper Linear Base Address
      public UInt32 UpperLinearBaseAddress
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return ((UInt32)m_UpperLinearBaseAddress) << 16;
            }
      }

    }
    //! @}

    //! \name the Start Segment Address Record
    //! @{
    internal class StartSegmentAddressRecord : Record
    {
      private UInt32 m_StartSegmentAddress = 0;
      private Byte[] m_Data = null;

      public StartSegmentAddressRecord(Byte[] tData)
      {
            if (null == tData)
            {
                return;
            }
            else if (4 != tData.Length)
            {
                return;
            }

            m_Data = tData;

            try
            {
                Byte[] tTemp = new Byte;
                tTemp = tData;
                tTemp = tData;
                tTemp = tData;
                tTemp = tData;

                m_StartSegmentAddress = BitConverter.ToUInt32(tTemp, 0);
            }
            catch (Exception Err)
            {
                Err.ToString();
            }

            m_bAvailable = true;
      }

      //! \brief always return 4
      public override int RecordLength
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return 4;
            }
      }


      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.START_SEGMENT_ADDRESS_RECORD; }
      }

      //! \brief get data
      public override Byte[] Data
      {
            get
            {
                if (!m_bAvailable)
                {
                  return null;
                }

                return m_Data;
            }
      }

      //! \brief get start segment address
      public UInt32 StartSegmentAddress
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return m_StartSegmentAddress & 0x000FFFFF;
            }
      }

      public UInt16 CS
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return (UInt16)(m_StartSegmentAddress & 0x0000FFFF);
            }
      }

      public UInt16 IP
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return (UInt16)((m_StartSegmentAddress >> 16) & 0x000F);
            }
      }

    }
    //! @}

    /*! \note The Data Record provides a set of hexadecimal digits that represent
   *      the ASCII code for data bytes that make up a portion of a memory image.
   *      The method for calculating the absolute address (linear in the 8-bit
   *      and 32-bit case and segmented in the 16-bit case) for each byte of data
   *      is described in the discussions of the Extended Linear Address Record
   *      and the Extended Segment Address Record.
   */
    //! \name the Data Record
    //! @{
    internal class DataRecord : Record
    {
      private Byte[] m_Data = null;
      private UInt16 m_LoadOffset = 0;

      //! \brief constructor for the Data Record
      public DataRecord(UInt16 tLoadOffset, Byte[] tData)
      {
            if (null == tData)
            {
                return;
            }
            else if (tData.Length > 255)
            {
                return;
            }

            m_Data = tData;
            m_LoadOffset = tLoadOffset;

            m_bAvailable = true;
      }

      //! \brief get record data length
      public override int RecordLength
      {
            get
            {
                if (null == m_Data)
                {
                  return 0;
                }

                return m_Data.Length;
            }
      }

      //! \brief get load offset
      public override UInt16 LoadOffset
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return m_LoadOffset;
            }
      }

      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.DATA_RECORD; }
      }

      //! \brief get record data
      public override Byte[] Data
      {
            get
            {
                if (!m_bAvailable)
                {
                  return null;
                }

                return m_Data;
            }
      }

    }
    //! @}

    /*! \note The 16-bit Extended Segment Address Record is used to specify bits
   *      4-19 of the Segment Base Address (SBA), where bits 0-3 of the SBA
   *      are zero. Bits 4-19 of the SBA are referred to as the Upper Segment
   *      Base Address (USBA). The absolute memory address of a content byte
   *      in a subsequent Data Record is obtained by adding the SBA to an
   *      offset calculated by adding the LOAD OFFSET field of the containing
   *      Data Record to the index of the byte in the Data Record (0, 1, 2,
   *      ... n). This offset addition is done modulo 64K (i.e., 16-bits),
   *      ignoring any carry, so that offset wrap-around loading (from OFFFFH
   *      to OOOOOH) results in wrapping around from the end to the beginning
   *      of the 64K segment defined by the SBA. The address at which a
   *      particular byte is loaded is calculated as:
   *      
   *      SBA + ( MOD 64K)
   *      
   *      where:
   *      DRLO is the LOAD OFFSET field of a Data Record.
   *      DRI is the data byte index within the Data Record.
   *      
   *      When an Extended Segment Address Record defines the value of SBA,
   *      it may appear anywhere within a 16-bit hexadecimal object file. This
   *      value remains in effect until another Extended Segment Address Record
   *      is encountered. The SBA defaults to zero until an Extended Segment
   *      Address Record is encountered.
   */
    //! \name the Extend Segment Address Record
    //! @{
    internal class ExtendSegmentAddressRecord : Record
    {
      private UInt16 m_ExtendSegmentUpperBaseAddress = 0;
      private Byte[] m_Data = null;

      public ExtendSegmentAddressRecord(Byte[] tData)
      {
            if (null == tData)
            {
                return;
            }
            else if (2 != tData.Length)
            {
                return;
            }

            m_Data = tData;

            try
            {
                Byte[] tTemp = new Byte;
                tTemp = tData;
                tTemp = tData;

                m_ExtendSegmentUpperBaseAddress = BitConverter.ToUInt16(tTemp, 0);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return;
            }

            m_bAvailable = true;
      }

      //! \brief always return 2
      public override int RecordLength
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return 2;
            }
      }


      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.EXTEND_SEGMENT_ADDRESS_RECORD; }
      }

      //! \brief get data
      public override Byte[] Data
      {
            get
            {
                if (!m_bAvailable)
                {
                  return null;
                }

                return m_Data;
            }
      }

      //! \brief get extend segment base address
      public UInt32 ExtendSegmentBaseAddress
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return ((UInt32)m_ExtendSegmentUpperBaseAddress) << 4;
            }
      }

    }
    //! @}
   
    /*! \note The Start Linear Address Record is used to specify the execution start
   *      address for the object file. The value given is the 32-bit linear address
   *      for the EIP register. Note that this record only specifies the code
   *      address within the 32-bit linear address space of the 80386. If the code
   *      is to start execution in the real mode of the 80386, then the Start
   *      Segment Address Record should be used instead, since that record specifies
   *      both the CS and IP register contents necessary for real mode.
   */
    //! \name the Start Linear Address Record
    //! @{
    internal class StartLinearAddressRecord : Record
    {
      private UInt32 m_StartLinearAddress = 0;
      private Byte[] m_Data = null;

      public StartLinearAddressRecord(Byte[] tData)
      {
            if (null == tData)
            {
                return;
            }
            else if (4 != tData.Length)
            {
                return;
            }

            m_Data = tData;

            try
            {
                Byte[] tTemp = new Byte;
                tTemp = tData;
                tTemp = tData;
                tTemp = tData;
                tTemp = tData;

                m_StartLinearAddress = BitConverter.ToUInt32(tTemp, 0);
            }
            catch (Exception Err)
            {
                Err.ToString();
                return ;
            }

            m_bAvailable = true;
      }

      //! \brief always return 4
      public override Int32 RecordLength
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return 4;
            }
      }

      //! \brief get record type
      public override Record.Type RecordType
      {
            get { return Type.START_LINEAR_ADDRESS_RECORD; }
      }

      //! \brief get data
      public override Byte[] Data
      {
            get
            {
                if (!m_bAvailable)
                {
                  return null;
                }

                return m_Data;
            }
      }

      //! \brief get start linear address (EIP)
      public UInt32 StartLinearAddress
      {
            get
            {
                if (!m_bAvailable)
                {
                  return 0;
                }

                return m_StartLinearAddress;
            }
      }

    }
    //! @}
}

Gorgon_Meducer 发表于 2010-3-21 11:45:19

To be continue...

Gorgon_Meducer 发表于 2010-3-21 11:45:30

To be continue...

elecfun 发表于 2010-3-21 12:28:46

呵呵 C#正在学习中   

代码风格很好啊只是还不习惯全E文./emotion/em005.gif

12fen 发表于 2010-3-21 13:25:57

mark

dzyong 发表于 2010-3-21 13:38:07

C#~~~~~

lixupeng 发表于 2011-3-22 18:59:26

mark!!!!!

HadesHe 发表于 2011-5-6 17:28:11

mark

wuguoyan 发表于 2011-5-9 19:58:36

mark

myhonour 发表于 2011-9-5 14:44:58

mark

summarize 发表于 2011-9-5 23:40:13

学习!

thisjoy 发表于 2011-9-11 15:24:50

mark

tclandmei 发表于 2011-11-10 09:38:18

谢谢!
正在找这个!

cheungman 发表于 2011-11-10 09:45:42

写过vc 6.0下的hex类, 傻孩子的这个支持大于64K的hex文件, 不错, 网上的代码基本只支持小于64K的.

shandian 发表于 2011-11-10 10:06:26

前段时间我也在网上找C#的Hex控件,最后找到一个,还比较好用的。名字叫Be.Windows.Forms.HexBox,功能很全,开源的。网上搜一下就能下载到。
我已经应用到了我的程序中了。如下:
http://cache.amobbs.com/bbs_upload782111/files_47/ourdev_693721ND6817.jpg
(原文件名:未命名.jpg)
http://cache.amobbs.com/bbs_upload782111/files_47/ourdev_693723GJY5GJ.jpg
(原文件名:未命名1.jpg)

skynet 发表于 2011-11-10 10:09:55

好东西

farmer 发表于 2011-11-10 16:50:23

好东西

wenfaxiang 发表于 2012-1-17 21:32:17

学习了

hygs 发表于 2012-1-18 10:15:26

谢谢

armku 发表于 2012-3-6 01:35:41

mark

qinshi1208 发表于 2012-5-4 10:31:42

shandian 发表于 2011-11-10 10:06 static/image/common/back.gif
前段时间我也在网上找C#的Hex控件,最后找到一个,还比较好用的。名字叫Be.Windows.Forms.HexBox,功能很全 ...

试用了一下,这个控件确实不错,应该做电子的很多地方都可以用。我上传到这里吧

kiema 发表于 2012-5-14 00:06:39

mark
學習了

jacobson 发表于 2012-5-14 21:17:47

學習{:smile:}{:smile:}

liubinghui 发表于 2012-11-3 21:03:23

有没MFC的呀。

jacktau 发表于 2012-11-18 15:21:22

mark~~~~~~~~~~~~~~~~~~

电子小生 发表于 2012-11-25 10:09:50

学习了!!

airwolf09921 发表于 2012-11-25 16:25:10

谢谢傻孩子大侠,希望您能多写一些关于C#的文章!

mwt168 发表于 2013-6-28 15:37:13

学习了,好东西{:victory:}

Tliang 发表于 2013-7-10 09:30:43

mark                  

mcuprogram 发表于 2013-7-10 10:20:14

傻孩子的东西不错 .

leahcim89 发表于 2013-7-20 23:47:43

这帖太有用报。

cy8051 发表于 2013-8-20 14:00:12

文件已经下载不了,楼主能不能再上传一下啊?

lh5566 发表于 2013-10-7 18:38:39

不错 16楼的先试试

电子小生 发表于 2013-10-7 19:04:38

学习了!

skyxjh 发表于 2013-10-7 21:58:33

C# HEX文件读写,标记一下。

ablightstar 发表于 2015-4-9 17:43:03

用附件中的HexFileStreamTester.rar,打开一个数据大小为65684字节(非hex文件本身的大小)的hex文件,显示全部为FF啊
是不是不能显示超过64K的文件?

star_tale 发表于 2017-8-29 13:38:39

感谢,让我柳岸花明又一村!

kevinchen026 发表于 2018-12-25 12:07:22

C# hex /HexBox
页: [1]
查看完整版本: [C#][学习笔记]一个可以存/取HEX文件的类HexFileStream