• Delphi Hex and Bin 转换


    Hex formats
    
    Intel
    =====
    
    Hexadecimal values are always in uppercase. Each line is a record.
    The sum of all the bytes in each record should be 00 (modulo 256).
    
    Record types:
    
    00: data records
    01: end-of-file record
    02: extended address record
    
    Data record
    -----------
    
        :0D011C0000000000C3E0FF0000000000C30F
    
    : 0D 011C 00 00000000C3E0FF0000000000C3 0F
    |  |   |   | -------------+------------  |
    |  |   |   |              |              +--- Checksum
    |  |   |   |              +------------------ Data bytes
    |  |   |   +--------------------------------- Record type
    |  |   +------------------------------------- Address
    |  +----------------------------------------- Number of data bytes
    +-------------------------------------------- Start of record
    
    
    End of file record
    ------------------
    
        :00000001FE
    
    : 00 0000 01 FE
    |  |   |   |  |
    |  |   |   |  +--- Checksum
    |  |   |   +------ Record type
    |  |   +---------- Address
    |  +-------------- Number of data bytes
    +----------------- Start of record
    
    
    
    Extended address record
    -----------------------
    
        :02010002E0001B
    
    : 02 0100 02 E000 1B
    |  |   |   |  |    |
    |  |   |   |  |    +--- Checksum
    |  |   |   |  +-------- Segment address
    |  |   |   +----------- Record type
    |  |   +--------------- Address
    |  +------------------- Number of data bytes
    +---------------------- Start of record
    
    Following data records will start at E000:0100 or E0100
    (* -----------------------------------------------------------------------------
    
      http://www.keil.com/support/docs/1584/
    
      What is the Intel HEX file format?
    
      The Intel HEX file is an ASCII text file with lines of text that follow
      the Intel HEX file format.
      Each line in an Intel HEX file contains one HEX record.
      These records are made up of hexadecimal numbers that represent machine
      language code and/or constant data.
      Intel HEX files are often used to transfer the program and data that
      would be stored in a ROM or EPROM.
      Most EPROM programmers or emulators can use Intel HEX files.
    
      ------------------------------------------------------------------------------
      Record Format
      ------------------------------------------------------------------------------
      An Intel HEX file is composed of any number of HEX records.
      Each record is made up of five fields that are arranged in the following format:
    
      :llaaaatt[dd...]cc
    
      Each group of letters corresponds to a different field,
      and each letter represents a single hexadecimal digit.
      Each field is composed of at least two hexadecimal digits-which
      make up a byte-as described below:
    
      ------------------------------------------------------------------------------
      : is the colon that starts every Intel HEX record.
    
      ------------------------------------------------------------------------------
      ll is the record-length field that
      represents the number of data bytes (dd) in the record.
    
      ------------------------------------------------------------------------------
      aaaa is the address field that represents the starting address for
      subsequent data in the record.
    
      ------------------------------------------------------------------------------
      tt is the field that represents the HEX record type,
      which may be one of the following:
      00 - data record
    
      01 - end-of-file record :00000001FF ( :00AB2F0125 : Jump 0xAB2F )
    
      02 - extended segment address record :02-0000-02-FFFF-FC : 0x000FFFF0
    
      03 - start segment address record :04-0000-03-0000-00CD-2A : CS-IP
    
      04 - extended linear address record :02-0000-04-FFFF-FC : 0xFFFF0000
    
      05 - start linear address record :04-0000-05-000000CD-2A : EIP
    
      ------------------------------------------------------------------------------
      dd is a data field that represents one byte of data.
      A record may have multiple data bytes. The number of data bytes in the record
      must match the number specified by the ll field.
    
      ------------------------------------------------------------------------------
      cc is the checksum field that represents the checksum of the record.
      The checksum is calculated by summing the values of
      all hexadecimal digit pairs in the record modulo 256
      and taking the two's complement.
    
      --------------------------------------------------------------------------------
      Data Records
      --------------------------------------------------------------------------------
    
      The Intel HEX file is made up of any number of data records that are terminated
      with a carriage return and a linefeed. Data records appear as follows:
    
      :10246200464C5549442050524F46494C4500464C33
    
      This record is decoded as follows:
    
      : 10 2462 00 464C5549442050524F46494C4500464C 33
    
      where:
    
      10 is the number of data bytes in the record.
      2462 is the address where the data are to be located in memory.
      00 is the record type 00 (a data record).
      464C...464C is the data.
      33 is the checksum of the record.
    
    
      ------------------------------------------------------------------------------
      Extended Linear Address Records (HEX386)
      ------------------------------------------------------------------------------
      Extended linear address records are also known as 32-bit address records
      and HEX386 records. These records contain the upper 16 bits (bits 16-31)
      of the data address. The extended linear address record always
      has two data bytes and appears as follows:
    
      :02000004FFFFFC
    
      where:
    
      02 is the number of data bytes in the record.
    
      0000 is the address field.
      For the extended linear address record, this field is always 0000.
    
      04 is the record type 04 (an extended linear address record).
    
      FFFF is the upper 16 bits of the address.
    
      FC is the checksum of the record and is calculated as
      01h + NOT(02h + 00h + 00h + 04h + FFh + FFh).
    
      When an extended linear address record is read,
      the extended linear address stored in the data field is saved
      and is applied to subsequent records read from the Intel HEX file.
    
      The linear address remains effective until changed
      by another extended address record.
    
      The absolute-memory address of a data record is obtained
      by adding the address field in the record to the shifted address data
      from the extended linear address record.
    
      The following example illustrates this process..
    
      Address from the data record's address field      2462
      Extended linear address record data field     FFFF
      ------------------------------------------------------------------------------
      Absolute-memory address                       FFFF2462
    
    
      ------------------------------------------------------------------------------
      Extended Segment Address Records (HEX86)
      ------------------------------------------------------------------------------
    
      Extended segment address records-also known as HEX86 records-contain bits 4-19
      of the data address segment.
    
      The extended segment address record always
      has two data bytes and appears as follows:
    
      :020000021200EA
    
      where:
    
      02 is the number of data bytes in the record.
    
      0000 is the address field.
      For the extended segment address record, this field is always 0000.
    
      02 is the record type 02 (an extended segment address record).
    
      1200 is the segment of the address.
    
      EA is the checksum of the record and is calculated as
      01h + NOT(02h + 00h + 00h + 02h + 12h + 00h).
    
      When an extended segment address record is read,
      the extended segment address stored in the data field
      is saved and is applied to subsequent records read from the Intel HEX file.
    
      The segment address remains effective until changed
      by another extended address record.
    
      The absolute-memory address of a data record is obtained
      by adding the address field in the record to the shifted-address data
      from the extended segment address record.
    
      The following example illustrates this process.
    
      Address from the data record's address field     2462
      Extended segment address record data field      1200
      --------
      Absolute memory address                      00014462
    
      ------------------------------------------------------------------------------
      End-of-File (EOF) Records
      ------------------------------------------------------------------------------
      An Intel HEX file must end with an end-of-file (EOF) record.
      This record must have the value 01 in the record type field.
      An EOF record always appears as follows:
    
      :00000001FF
    
      where:
    
      00 is the number of data bytes in the record.
      0000 is the address where the data are to be located in memory.
      The address in end-of-file records is meaningless and is ignored.
      An address of 0000h is typical.
      01 is the record type 01 (an end-of-file record).
      FF is the checksum of the record and is calculated as
      01h + NOT(00h + 00h + 00h + 01h).
    
    
      ------------------------------------------------------------------------------
      Example Intel HEX File
      ------------------------------------------------------------------------------
      Following is an example of a complete Intel HEX file:
    
      :10001300AC12AD13AE10AF1112002F8E0E8F0F2244
      :10000300E50B250DF509E50A350CF5081200132259
      :03000000020023D8
      :0C002300787FE4F6D8FD7581130200031D
      :10002F00EFF88DF0A4FFEDC5F0CEA42EFEEC88F016
      :04003F00A42EFE22CB
      :00000001FF
    
      : 02 0000 04 2000             DA        : 2000 is the upper 16 bits of address.
      : 08 1264 00 0000A0E31EFF2FE1 D2        : 1264 is the lower 16 bits of address.
      : 00 0000 01                  FF        : Enf of File
    
      ’00’ Data Record <=We use this record
      ’01’ End of File Record <=We use this record
    
      ’02’ Extended Segment Address Record  : BaseAddr = ( SegAddr<<4 )
      ’03’ Start Segment Address Record     : Value of the CS:IP ( >>80286 )
      ’04’ Extended Linear Address Record   : BaseAddr = ( LinearAddr<<16 )
      ’05’ Start Linear Address Record      : Value of the EIP ( 80386>> )
    
      Sometimes the terms I8HEX, I16HEX, I32HEX, resp. INTEL 8/16/32 are used,
      usually in the context of x86 CPUs.
      The format of the files are all the same, but the terms imply using a particular
      subset of the possible record types:
    
      I8HEX uses only types 00/01 (16 bit addresses),
      I16HEX adds types 02/03 (20 bit addresses), and
      I32HEX adds 04/05 (32 bit addresses).
    
      --------------------------------------------------------------------------- *)
    unit uIntelHex;
    
    interface
    
    uses
      System.SysUtils, System.Classes, Windows;
    
    const
      HEX_ERROR_MARKER = 1;
      HEX_ERROR_ADDRESS = 2;
      HEX_ERROR_REC_TYPE = 3;
      HEX_ERROR_SECTION_SIZE = 4;
      HEX_ERROR_DATA = 5;
      HEX_ERROR_CHECK_SUM = 6;
      HEX_ERROR_SECTION_COUNT = 7;
    
    type
      EHex2Bin = class( Exception )
      private
        FCode : integer;
      public
        constructor Create( ACode : integer );
        property Code : integer read FCode write FCode;
      end;
    
    type
      TXxx2Bin = procedure( TxtStringList : TStringList; BinStream : TMemoryStream;
        var StartAddress : int64 );
    
    procedure Txt2Bin( TxtStringList : TStringList; BinStream : TMemoryStream;
      var StartAddress : int64 );
    procedure Hex2Bin( HexStringList : TStringList; BinStream : TMemoryStream;
      var StartAddress : int64 );
    procedure Bin2Hex( BinStream : TMemoryStream; HexStringList : TStringList;
      StartAddress : int64 );
    
    implementation
    
    const
      ONE_RECORD_SIZE = 16;
      ONE_SECTION_SIZE = 64 * 1024;
      MAX_SECTION_COUNT = 16;
      MAX_BUFFER_SIZE = MAX_SECTION_COUNT * ONE_SECTION_SIZE;
    
    type
      // Different possible records for Intel .hex files.
      TRecType = ( rtData = 0, // data
        rtEof = 1, // End Of File
        rtEsa = 2, // Extended Segment Address
        rtSsa = 3, // Start Segment Address
        rtEla = 4, // Extended Linear Address
        rtSla = 5 ); // Start Linear Address
    
      THexRec = record
        Marker : BYTE; // : Valid, other Invalid
        DataSize : BYTE;
        Addr : Word;
        RecType : TRecType;
        DataBuf : array [ 0 .. 255 ] of BYTE;
        CheckSum : BYTE;
      end;
    
      THexSection = record
        LinearAddress : DWORD;
        UsedOffset : DWORD;
        UnusedOffset : DWORD;
        DataBuffer : array [ 0 .. ONE_SECTION_SIZE - 1 ] of BYTE;
      end;
    
    var
      HexSections : array [ 0 .. MAX_SECTION_COUNT - 1 ] of THexSection;
      Hex2BinErrorMessage : array [ HEX_ERROR_MARKER .. HEX_ERROR_SECTION_COUNT ]
        of string; // error messages
    
    constructor EHex2Bin.Create( ACode : integer );
    begin
      FCode := ACode;
      inherited Create( Hex2BinErrorMessage[ ACode ] );
    end;
    
    // : 10 0013 00 AC12AD13AE10AF1112002F8E0E8F0F22 44
    // \_________________________________________/ CS
    //
    // The checksum is calculated by summing the values of all hexadecimal digit
    // pairs in the record modulo 256  and taking the two's complement
    //
    function HexCalcCheckSum( HexRec : THexRec ) : BYTE;
    var
      i : integer;
    begin
      Result := HexRec.DataSize + HexRec.Addr + ( HexRec.Addr shr 8 ) +
        BYTE( HexRec.RecType );
      for i := 0 to HexRec.DataSize - 1 do
        Inc( Result, HexRec.DataBuf[ i ] );
    
      // Result := -Integer(Result);
      Result := ( not Result ) + 1;
    end;
    
    function HexRec2Str( HexRec : THexRec ) : string;
    var
      i : integer;
    begin
      Result := ':' + IntToHex( HexRec.DataSize, 2 ) + IntToHex( HexRec.Addr, 4 ) +
        IntToHex( Ord( HexRec.RecType ), 2 );
      for i := 0 to HexRec.DataSize - 1 do
        Result := Result + IntToHex( HexRec.DataBuf[ i ], 2 );
      Result := Result + IntToHex( HexCalcCheckSum( HexRec ), 2 );
    end;
    
    // 1 23 4567 89 ABCDEF.............................
    // : 10 0013 00 AC12AD13AE10AF1112002F8E0E8F0F22 44
    //
    function HexStr2Rec( HexStr : string ) : THexRec;
    var
      i : integer;
    begin
      Result.Marker := Ord( HexStr[ 1 ] );
      if Result.Marker <> Ord( ':' ) then
        raise EHex2Bin.Create( HEX_ERROR_MARKER );
    
      try
        Result.DataSize := StrToInt( '$' + Copy( HexStr, 2, 2 ) );
        Result.Addr := StrToInt( '$' + Copy( HexStr, 4, 4 ) );
        Result.RecType := TRecType( StrToInt( '$' + Copy( HexStr, 8, 2 ) ) );
        for i := 0 to Result.DataSize - 1 do
          Result.DataBuf[ i ] := StrToInt( '$' + Copy( HexStr, 10 + i * 2, 2 ) );
    
        Result.CheckSum :=
          StrToInt( '$' + Copy( HexStr, 10 + Result.DataSize * 2, 2 ) );
      except
        raise EHex2Bin.Create( HEX_ERROR_DATA );
      end;
    
      if Result.CheckSum <> HexCalcCheckSum( Result ) then
        raise EHex2Bin.Create( HEX_ERROR_CHECK_SUM );
    end;
    
    procedure Bin2Hex( BinStream : TMemoryStream; HexStringList : TStringList;
      StartAddress : int64 );
    var
      HexRec : THexRec;
      BufferSize : DWORD;
      SectionSize : DWORD;
      RecordSize : DWORD;
      SectionAddr : DWORD;
      LinearAddr : DWORD;
    begin
      SectionAddr := 0;
      LinearAddr := 0;
      BufferSize := BinStream.Size;
      SectionSize := BufferSize;
      BinStream.Seek( 0, soBeginning );
    
      while BufferSize > 0 do
      begin
        // Write Linear Address
        if ( StartAddress <> 0 ) or ( SectionSize = 0 ) then
        begin
          if ( StartAddress <> 0 ) then // first section
          begin
            SectionAddr := StartAddress and ( ONE_SECTION_SIZE - 1 );
            SectionSize := ONE_SECTION_SIZE - SectionAddr;
            LinearAddr := StartAddress shr 16;
            StartAddress := 0;
          end
          else // if ( SectionSize = 0 ) then
          begin
            SectionAddr := 0;
            SectionSize := BufferSize;
            LinearAddr := LinearAddr + 1;
          end;
    
          HexRec.DataSize := 2;
          HexRec.Addr := 0;
          HexRec.RecType := rtEla;
          HexRec.DataBuf[ 0 ] := LinearAddr shr 8;
          HexRec.DataBuf[ 1 ] := LinearAddr and $FF;
          HexStringList.Add( HexRec2Str( HexRec ) );
    
        end
        else // Write Data Record
        begin
          RecordSize := SectionSize;
          if RecordSize > ONE_RECORD_SIZE then
            RecordSize := ONE_RECORD_SIZE;
    
          HexRec.DataSize := RecordSize;
          HexRec.Addr := SectionAddr;
          HexRec.RecType := rtData;
          BinStream.Read( HexRec.DataBuf[ 0 ], RecordSize );
          HexStringList.Add( HexRec2Str( HexRec ) );
    
          SectionAddr := SectionAddr + RecordSize;
          SectionSize := SectionSize - RecordSize;
          BufferSize := BufferSize - RecordSize;
        end;
      end;
    
      // Write EOF :00000001FF
      HexRec.DataSize := 0;
      HexRec.Addr := 0;
      HexRec.RecType := rtEof;
      HexStringList.Add( HexRec2Str( HexRec ) );
    end;
    
    procedure Hex2Bin( HexStringList : TStringList; BinStream : TMemoryStream;
      var StartAddress : int64 );
    var
      i : integer;
      LastAddress : int64;
      HexRec : THexRec;
      SectionFreeAddr : DWORD;
      SectionIndex : DWORD;
      SizeToWrite : DWORD;
      BufferToWrite : Pointer;
      LinearAddress : DWORD;
      FirstLinearAddr : DWORD;
      LastLinearAddr : DWORD;
      FirstUsedDataOffset : DWORD; // First Section : $0000
      LastUnusedDataOffset : DWORD; // Last Section : $10000
    begin
      for i := 0 to MAX_SECTION_COUNT - 1 do // Mark as Unused
      begin
        HexSections[ i ].LinearAddress := $0000;
        HexSections[ i ].UnusedOffset := $0000;
        HexSections[ i ].UsedOffset := ONE_SECTION_SIZE;
        FillChar( HexSections[ i ].DataBuffer[ 0 ], ONE_SECTION_SIZE, $FF );
      end;
    
      SectionIndex := 0;
      for i := 0 to HexStringList.Count - 1 do
      begin
        HexRec := HexStr2Rec( HexStringList[ i ] );
        case HexRec.RecType of
          rtEof :
            break;
          rtSsa, rtEsa, rtSla :
            continue;
          rtEla :
            begin
              LinearAddress := HexRec.DataBuf[ 0 ] * 256 + HexRec.DataBuf[ 1 ];
              if HexSections[ SectionIndex ].LinearAddress <> LinearAddress then
              begin
                if ( i <> 0 ) then
                  SectionIndex := SectionIndex + 1;
                if ( SectionIndex = MAX_SECTION_COUNT ) then
                  raise EHex2Bin.Create( HEX_ERROR_SECTION_COUNT );
    
                HexSections[ SectionIndex ].LinearAddress := LinearAddress;
              end;
            end;
    
          rtData :
            begin
              SectionFreeAddr := HexRec.Addr + HexRec.DataSize; // ONE_SECTION_SIZE
              if SectionFreeAddr > ONE_SECTION_SIZE then
                raise EHex2Bin.Create( HEX_ERROR_SECTION_SIZE );
              if HexSections[ SectionIndex ].UnusedOffset < SectionFreeAddr then
                HexSections[ SectionIndex ].UnusedOffset := SectionFreeAddr;
              if HexSections[ SectionIndex ].UsedOffset > HexRec.Addr then
                HexSections[ SectionIndex ].UsedOffset := HexRec.Addr;
              CopyMemory( @HexSections[ SectionIndex ].DataBuffer[ HexRec.Addr ],
                @HexRec.DataBuf[ 0 ], HexRec.DataSize );
            end;
        end;
      end;
    
      FirstLinearAddr := $10000;
      LastLinearAddr := 0;
      FirstUsedDataOffset := 0;
      LastUnusedDataOffset := ONE_SECTION_SIZE;
    
      for i := 0 to SectionIndex do
      begin
        if HexSections[ i ].LinearAddress > LastLinearAddr then
        begin
          LastLinearAddr := HexSections[ i ].LinearAddress;
          LastUnusedDataOffset := HexSections[ i ].UnusedOffset;
        end;
        if HexSections[ i ].LinearAddress < FirstLinearAddr then
        begin
          FirstLinearAddr := HexSections[ i ].LinearAddress;
          FirstUsedDataOffset := HexSections[ i ].UsedOffset;
        end;
      end;
    
      StartAddress := DWORD( FirstLinearAddr ) shl 16;
      StartAddress := StartAddress + FirstUsedDataOffset;
    
      LastAddress := DWORD( LastLinearAddr ) shl 16;
      LastAddress := LastAddress + LastUnusedDataOffset;
    
      BinStream.Clear;
      BinStream.SetSize( LastAddress - StartAddress );
    
      // Write Every Section ( include unused sections : FF .. FF )
      for i := 0 to SectionIndex do
      begin
        if HexSections[ i ].LinearAddress = FirstLinearAddr then
        begin
          SizeToWrite := ONE_SECTION_SIZE - HexSections[ i ].UsedOffset;
          if SizeToWrite > BinStream.Size then
            SizeToWrite := BinStream.Size;
    
          BufferToWrite := @HexSections[ i ].DataBuffer
            [ HexSections[ i ].UsedOffset ];
        end
        else if HexSections[ i ].LinearAddress = LastLinearAddr then
        begin
          SizeToWrite := HexSections[ i ].UnusedOffset;
          BufferToWrite := @HexSections[ i ].DataBuffer[ 0 ];
        end
        else
        begin
          SizeToWrite := ONE_SECTION_SIZE;
          BufferToWrite := @HexSections[ i ].DataBuffer[ 0 ];
        end;
        BinStream.Write( BufferToWrite^, SizeToWrite );
      end;
    
    end;
    
    function HexStr2Int( HexStr : PChar; var AByte : BYTE ) : boolean;
    begin
      Result := FALSE;
      if ( HexStr[ 0 ] = '0' ) then
        if ( ( HexStr[ 1 ] = 'x' ) or ( HexStr[ 1 ] = 'X' ) ) then
          Exit;
    
      if CharInSet( HexStr[ 0 ], [ '0' .. '9', 'A' .. 'F', 'a' .. 'f' ] ) then
      begin
        if CharInSet( HexStr[ 1 ], [ '0' .. '9', 'A' .. 'F', 'a' .. 'f' ] ) then
        begin
          AByte := StrToInt( '$' + HexStr[ 0 ] + HexStr[ 1 ] );
          Result := TRUE;
        end;
      end;
    end;
    
    procedure Txt2Bin( TxtStringList : TStringList; BinStream : TMemoryStream;
      var StartAddress : int64 ); // dont care StartAddress
    var
      CharIndex : DWORD;
      SectionIndex : DWORD;
      SectionOffset : DWORD;
      TextStr : string;
      BinSize : DWORD;
      AByte : BYTE;
      SizeToWrite : DWORD;
    begin
      TextStr := '';
      for SectionOffset := 0 to TxtStringList.Count - 1 do
        TextStr := TextStr + TxtStringList[ SectionOffset ];
    
      SectionIndex := 0;
      SectionOffset := 0;
      CharIndex := 1;
      BinSize := 0;
    
      while CharIndex < Length( TextStr ) do
      begin
        if not HexStr2Int( @TextStr[CharIndex], AByte ) then
        begin
          Inc( CharIndex, 1 );
          continue;
        end;
    
        HexSections[ SectionIndex ].DataBuffer[ SectionOffset ] := AByte;
        Inc( BinSize, 1 );
        Inc( SectionOffset, 1 );
        if SectionOffset = ONE_SECTION_SIZE then
          Inc( SectionIndex, 1 );
        if SectionIndex = MAX_SECTION_COUNT then
          break;
    
        Inc( CharIndex, 2 );
      end;
    
      BinStream.SetSize( BinSize );
      while BinSize > 0 do
      begin
        SizeToWrite := BinSize;
        if SizeToWrite > ONE_SECTION_SIZE then
          SizeToWrite := ONE_SECTION_SIZE;
        BinStream.Write( HexSections[ SectionIndex ].DataBuffer[ 0 ], SizeToWrite );
        Inc( SectionIndex );
        BinSize := BinSize - SizeToWrite;
      end;
    end;
    
    initialization
    
    Hex2BinErrorMessage[ HEX_ERROR_MARKER ] := 'Error Marker';
    Hex2BinErrorMessage[ HEX_ERROR_ADDRESS ] := 'Error Address';
    Hex2BinErrorMessage[ HEX_ERROR_REC_TYPE ] := 'Error Type';
    Hex2BinErrorMessage[ HEX_ERROR_SECTION_SIZE ] := 'Error Section Size';
    Hex2BinErrorMessage[ HEX_ERROR_DATA ] := 'Error Data';
    Hex2BinErrorMessage[ HEX_ERROR_CHECK_SUM ] := 'Error CheckSum';
    Hex2BinErrorMessage[ HEX_ERROR_SECTION_COUNT ] := 'Error Section Count';
    
    end.

  • 相关阅读:
    C#通过模板导出Word(文字,表格,图片) 转载
    转载:mysql新手入门安装配置:mysql 8.0.13 zip安装,初始配置,修改密码(经测试管用)
    转载:MySQL 8.0.19安装教程(windows 64位)
    VS2010上winform打包发布、打包安装程序
    asp.net core mvc权限控制:分配权限
    asp.net core mvc权限控制:权限控制介绍
    实测可用-免费屏幕录制软件下载地址
    Win10激活工具-Win7激活工具-Office激活工具-KMS激活工具汉化版x64下载-实测可用
    C#-WPF实现抽屉式风格主题框架源码-使用MaterialDesignThemes实现WPF炫酷漂亮的效果-提供Demo下载
    C#实现图片暗通道去雾算法-Demo-提供代码实例下载地址
  • 原文地址:https://www.cnblogs.com/shangdawei/p/3050286.html
Copyright © 2020-2023  润新知