读取图像中的 EXIF 信息(不全,能够读取部分)

 

   需要读取EXIF信息,自己阅读了一些资料,现在共享一下
需要注意的是,由于EXIF是一种可交换的文件格式,所以可以用在Intel系列和Motorola系列的CPU上(至于两者CPU的区别,大家可以到网上找找,这里不做说明)。在文件中有一个标志,如果是“MM”表示Motorola的CPU,否则为“II”表示Intel的CPU。
  这个代码是使用C写的,读取文件用的API。本来我想写成一个VC使用的类的,但是自己还是太懒了。需要的自己去转换吧。

  贴在这里主要就是这方面的资料国内太少了,希望本文能够帮助某部分人 :)
  同样,下面贴出 .h 文件与 .c 文件,需要的人拷贝下来


//      .h 文件
 

/********************************************************************
        Copyright@ 版权所有@ 1998-2005hengai。保留所有权利。
********************************************************************/
 
 
/********************************************************************
  文件说明: 能够读取 JPG 图像文件中的 EXIF 信息
  文件名称: exif.h
  版本号 : 1.0.0
  作    者: hengai
  修改纪录:
  使用方法: 包含此头文件,然后调用函数
              int EXIF_Read(LPCTSTR pszJpgFileName, EXIFINFO* pExifInfo)
              即可获取 EXIF 信息。EXIF信息包含在参数 pExifInfo 中
            在定义了 #define EXIF_OUTPUT_ERRMSG (默认下已经定义)后可以使用
              LPCTSTR EXIF_GetErrorString(); 获取出错信息
*********************************************************************
 
//
#ifndef STATIC
#define STATIC     static
#endif
#define EXIF_OUTPUT_ERRMSG       // 定义是否输出出错信息
//
//              定义常量
#define MAX_COMMENT    1000       // 最大的注释字符串长度
 
//
// 写入注释时,表明注释的类型,如 ASCII, UNICODE 等
typedef enum ECT {
  CT_ASCII = 0 ,
  CT_UNICODE ,
  CT_JIS ,
  CT_UNDEFINE
} COMMENT_TYPE ;
//
//              定义需要的结构体
#define ET_NOT_CLOSE_FILE        0x00000001   // 最后不关闭打开的文件句柄
#define ET_MALLOC_THUMBNAIL      0x00000002   // 拷贝缩略图的数据,调用者需要使用 free()
#define ET_MALLOC_USERCOM        0x00000004   // 是否拷贝用户注释,调用者需要使用 free()
#define ET_MALLOC_MAKERCOM       0x00000008   // 是否拷贝厂商注释,调用者需要使用 free()
//JPG 文件中的读入后的 EXIFF 信息保存到这个结构体中
typedef struct tag_ExifInfo {
  DWORD dwExifType ;             // 取值为 ET_NOT_CLOSE_FILE|ET_MALLOC_THUMBNAIL, ....
  DWORD dwExifType2 ;
  char   Version       [ 5 ];       //EXIF 信息版本
  char   CameraMake    [ 32 ];      //DC 制造商
  char   CameraModel  [ 40 ];      //DC 型号
  char   DateTime      [ 20 ];      //JPG 文件日期
  char   DateTimeDigitized [ 20 ];  //JPG 文件被其它软件修改日期
  int    Height , Width ;          // 图像高度、宽度
  int    Orientation ;            // 拍摄方向,例如相机向左手方向旋转后拍摄的
  int    IsColor ;                //
  int    Process ;                // 被处理
  int    FlashUsed ;              // 是否使用闪光灯
  float FocalLength ;            // 焦距
  float ExposureTime ;           // 曝光时间(快门速度)
  float ApertureFNumber ;        // 光圈数
  float Distance ;               // 拍摄物体距离
  float CCDWidth ;                //CCD 大小
  float ExposureBias ;           // 曝光补偿
  int    Whitebalance ;           // 白平衡
  int    MeteringMode ;           // 测光模式
  int    ExposureProgram ;        // 曝光
  int    ISOequivalent ;          //ISO
  int    CompressionLevel ;       // 压缩
  float FocalplaneXRes ;         // 焦平面X轴分辨率
  float FocalplaneYRes ;         // 焦平面Y轴分辨率
  float FocalplaneUnits ;        // 焦平面分辨率单位
  float Xresolution ;            //X 轴分辨率
  float Yresolution ;            //Y 轴分辨率
  float ResolutionUnit ;         // 分辨率单位
  float Brightness ;             // 亮度
  char   Comments [ MAX_COMMENT ];  // 注释
  DWORD UserCOMLength ;          // 用户注释长度。如果==0表示没有用户注释
  char  * UserCOM ;               // 用户注释
                                //if(dwExifType&ET_MALLOC_USERCOM == TRUE) 这个数值保存了用户注释数据,调用者需要使用 free()
                                 // 否则为用户注释的偏移量(相对于文件起始0处)
  DWORD MakerCOMLength ;         // 厂商注释长度。如果==0表示没有厂商注释
  char  * MakerCOM ;              // 厂商注释
                                //if(dwExifType&ET_MALLOC_MAKERCOM == TRUE) 这个数值保存了厂商注释数据,调用者需要使用 free()
                                 // 否则为厂商注释的偏移量(注意:是在当前SECTION中的偏移量,不是相对整个文件的)
 
 
  UCHAR * ThumbnailPointer ;     // 缩略图数据。
                                //if(dwExifType&ET_MALLOC_THUMBNAIL == TRUE) 这个数值保存了缩略图的数据
                                // 否则为一个 DWORD(需要强制转换) 表示缩略图在JPG文件中的偏移值(相对于文件起始0处)
  DWORD ThumbnailSize ;          // 缩略图的大小(字节流 ThumbnailPointer 的长度)
                                // 如果<=0表示该 JPG 文件没有缩略图
  HFILE hJpgFileHandle ;         // 返回打开的 JPG 文件句柄。必须 dwExifType&ET_NOT_CLOSE_FILE == TRUE 才是有效句柄
                                // 用户需要使用 CloseHandle(hJpgFileHandle)来关闭这个句柄
  BOOL   IsExif ;                 // 是否存在 EXIF 信息
} EXIFINFO ;
//
//              接口函数
int EXIF_Read ( LPCTSTR pszJpgFileName , EXIFINFO * pExifInfo );
LPCTSTR EXIF_GetErrorString ();
int EXIF_AddUserComments ( LPCTSTR pszJpgFileName , LPCTSTR pszUserComments , DWORD dwCommentLength , COMMENT_TYPE nCommentType );


//   .c 文件

/********************************************************************
        Copyright@ 版权所有@ 1998-2005 HENGAI。保留所有权利。
********************************************************************/
 
 
/********************************************************************
  文件说明:
  文件名称: exif.c
  版本号 : 1.0.0
  作    者: hengai
  修改纪录:
*********************************************************************/
#include "exif.h"
//
// 读取 EXIF 过程中需要的结构体
typedef struct tag_Section_t {
    UCHAR      * Data ;
    int        Type ;
    unsigned   Size ;
} Section_t ;
//
#ifdef   EXIF_OUTPUT_ERRMSG
STATIC TCHAR m_szLastError [ 256 ];  // 这里保存了出错的信息
#define EXIF_ERR_OUT ( str_err ) strcpy ( m_szLastError , str_err );
 
LPCTSTR EXIF_GetErrorString ()
{
  return ( LPCTSTR ) m_szLastError ;
}
#else
#define EXIF_ERR_OUT
LPCTSTR EXIF_GetErrrorString ()
{
  return "Plese #define EXIT_ERR_OUT in exif.h" ;
}
#endif
 
STATIC EXIFINFO * m_pExifInfo = 0 ;   //
STATIC     int m_MotorolaOrder    = 0 //
STATIC   int m_ExifImageWidth  = 0 //
 
//
 
/* Describes format descriptor */
static const int m_BytesPerFormat [] = { 0 , 1 , 1 , 2 , 4 , 8 , 1 , 1 , 2 , 4 , 8 , 4 , 8 };
#define NUM_FORMATS      12
 
#define FMT_BYTE         1            //Format Byte
#define FMT_STRING       2
#define FMT_USHORT       3
#define FMT_ULONG        4
#define FMT_URATIONAL    5
#define FMT_SBYTE        6
#define FMT_UNDEFINED    7
#define FMT_SSHORT       8
#define FMT_SLONG        9
#define FMT_SRATIONAL   10
#define FMT_SINGLE      11
#define FMT_DOUBLE      12
//
#define MAX_SECTIONS    20        //JPG 文件中能够允许的最多 SECTION 个数
 
#ifndef M_SOI
#define M_SOF0   0xC0             // Start Of Frame N
#define M_SOF1   0xC1             // N indicates which compression process
#define M_SOF2   0xC2              // Only SOF0-SOF2 are now in common use
#define M_SOF3   0xC3
#define M_SOF5   0xC5             // NB: codes C4 and CC are NOT SOF markers
#define M_SOF6   0xC6
#define M_SOF7   0xC7
#define M_SOF9   0xC9
#define M_SOF10 0xCA
#define M_SOF11 0xCB
#define M_SOF13 0xCD
#define M_SOF14 0xCE
#define M_SOF15 0xCF
#define M_SOI    0xD8             // Start Of Image (beginning of datastream)
#define M_EOI    0xD9             // End Of Image (end of datastream)
#define M_SOS    0xDA             // Start Of Scan (begins compressed data)
#define M_JFIF   0xE0             // Jfif marker
#define M_EXIF   0xE1             // Exif marker
#define M_COM    0xFE             // COMment
 
// 定义 APP 标识(SECTION)
#define M_APP0   0xE0
#define M_APP1   0xE1
#define M_APP2   0xE2
#define M_APP3   0xE3
#define M_APP4   0xE4
#define M_APP5   0xE5
#define M_APP6   0xE6
//...
#endif
 
// Describes tag values
// 注意: 下面的定义是按照 Intel CPU 来定义的,也就是说所有的都是高位在后,
// 这样的定义可能与 EXIF 白皮书上的定义不一致。例如白皮书上把 TAG_MAKE 定义为 0F01
// 下面是主要信息
#define TAG_MAKE               0x010F     // 相机DC 制造商
#define TAG_MODEL              0x0110     //DC 型号
#define TAG_ORIENTATION        0x0112     // 拍摄时方向,例如向左手旋转DC 90度拍摄照片
#define TAG_XRESOLUTION        0x011A     //X 轴分辨率
#define TAG_YRESOLUTION        0x011B     //Y 轴分辨率
#define TAG_RESOLUTIONUNIT     0x0128     // 分辨率单位,例如 inch, cm
#define TAG_DATATIME           0x0132     // 日期时间
#define TAG_YBCR_POSITION      0x0213     //YCbCr 位置控制,例如 居中
#define TAG_COPYRIGHT          0x8298     // 版权
#define TAG_EXIF_OFFSET        0x8769     //EXIF 偏移,这时候相当于处理一个新的 EXIF 信息
 
//
#define TAG_IMAGEWIDTH         0x0001     // 图像宽度
#define TAG_IMAGEHEIGHT        0x0101     // 图像高度
//BOOKMARK
// 辅助信息
#define TAG_EXPOSURETIME       0x829A     // 曝光时间,例如 1/30 秒
#define TAG_FNUMBER            0x829D     // 光圈,例如 F2.8
#define TAG_EXIF_VERSION       0x9000     //EXIF 信息版本
#define TAG_DATETIME_ORIGINAL 0x9003     // 照片拍摄时间,例如 2005-10-13 11:09:35
#define TAG_DATATIME_DIGITIZED   0x9004   // 相片被其它图像修改软件修改后的时间,例如 2005-10-13 11:36:35
#define TAG_COMPONCONFIG       0x9101     //ComponentsConfiguration 色彩空间配置
#define TAG_COMPRESS_BIT       0x9202     // 每像素压缩位数
#define TAG_SHUTTERSPEED       0x9201     // 快门速度,例如 1/30 秒
#define TAG_APERTURE           0x9202     // 光圈值,例如 F2.8
#define TAG_BRIGHTNESS         0x9203     // 亮度
#define TAG_EXPOSURE_BIAS      0x9204     // 曝光补偿,例如 EV0.0
#define TAG_MAXAPERTURE        0x9205     // 最大光圈值,例如 F2.8
#define TAG_SUBJECT_DISTANCE   0x9206     // 拍摄物距离,例如 3.11 米
#define TAG_METERING_MODE      0x9207     // 测光模式,例如矩阵
#define TAG_WHITEBALANCE       0x9208     //LightSource 白平衡
#define TAG_FLASH              0x9209     // 是否使用闪光灯
#define TAG_FOCALLENGTH        0x920A     // 焦距,例如 7.09mm
#define TAG_USERCOMMENT        0x9286     // 用户注释
#define TAG_MAKE_COMMENT       0x927C     // 厂商注释。这个版本不提供(2005-10-13)
#define TAG_SUBSECTIME         0x9290     //SubSecTime
#define TAG_SUBTIME_ORIGINAL   0x9291     //SubSecTimeOriginal
#define TAG_SUBTIME_DIGITIZED 0x9292     //SubSecTimeDigitized
#define TAG_FLASHPIXVERSION    0x00A0     //Flash Pix 版本
#define TAG_COLORSPACE         0x01A0     // 色彩空间,例如 sRGB
 
#define TAG_PIXEL_XDIMENSION   0x02A0     //
#define TAG_PIXEL_YDIMENSION   0x03A0     //
#define TAG_
//EXIFR98
 
// 缩略图
#define TAG_INTEROP_OFFSET     0xa005     // 偏移
 
#define TAG_FOCALPLANEXRES     0xA20E     // 焦平面X轴分辨率,例如 1024000/278
#define TAG_FOCALPLANEYRES     0xA20F     // 焦平面X轴分辨率,例如 768000/209
#define TAG_FOCALPLANEUNITS    0xA210     // 焦平面分辨率单位
#define TAG_EXIF_IMAGEWIDTH    0xA002     //EXIF 图像宽度(就是这张 JPG 图像)
#define TAG_EXIF_IMAGELENGTH   0xA003     //EXIF 图像高度
 
#define TAG_EXPOSURE_PROGRAM   0x8822     //
#define TAG_ISO_EQUIVALENT     0x8827     //
#define TAG_COMPRESSION_LEVEL 0x9102     //
 
#define TAG_THUMBNAIL_OFFSET   0x0201     // 缩略图偏移
#define TAG_THUMBNAIL_LENGTH   0x0202     // 缩略图大小
 
 
#define TAG_GPS_VERSIONID        0x0000   //GPS 版本
#define TAG_GPS_LATITUDEREF      0x0001   // 纬度参考,例如南纬
#define TAG_GPS_LATITUDE         0x0002   // 纬度值
#define TAG_GPS_LONGITUDEREF     0x0003   // 经度参考,例如东经
#define TAG_GPS_LONGITUDE        0x0004   // 经度值
#define TAG_GPS_ALTITUDEREF      0x0005   // 海拔高度参考
#define TAG_GPS_ALTITUDE         0x0006   // 海拔
#define TAG_GPS_TIMESTAMP        0x0007   // 时间戳
#define TAG_GPS_SATELLITES       0x0008   // 卫星
#define TAG_GPS_STATUS           0x0009   // 状态
#define TAG_GPS_MEASUREMODE      0x000A   //
#define TAG_GPS_DOP              0x000B   //
#define TAG_GPS_SPEEDREF         0x000C   //
#define TAG_GPS_SPEED            0x000D   //
#define TAG_GPS_TRACKREF         0x000E   //
#define TAG_GPS_TRACK            0x000F   //
#define TAG_GPS_IMGDIRECTIONREF 0x0010   //
#define TAG_GPS_IMGDIRECTION     0x0011   //
#define TAG_GPS_MAPDATUM         0x0012   //
#define TAG_GPS_DESTLATITUDEREF 0x0013   //
#define TAG_GPS_DESTLATITUDE     0x0014   //
#define TAG_GPS_DESTLONGITUDEREF   0x0015 //
#define TAG_GPS_DESTLONGITUDE    0x0016   //
#define TAG_GPS_DESTBEARINGREF   0x0017   //
#define TAG_GPS_DESTBEARING      0x0018   //
#define TAG_GPS_DESTDISTANCEREF 0x0019   //
#define TAG_GPS_DESTDISTANCE     0x001A   //
//
/*--------------------------------------------------------------------------
Get 16 bits motorola order (always) for jpeg header stuff.
--------------------------------------------------------------------------*/
STATIC int EXIF_Get16m ( void * Short )
{
  return ((( unsigned char *) Short )[ 0 ] << 8 ) | (( unsigned char *) Short )[ 1 ];
}
 
/*--------------------------------------------------------------------------
Convert a 16 bit unsigned value from file's native unsigned char order
--------------------------------------------------------------------------*/
STATIC int EXIF_Get16u ( void * Short )
{
  if ( m_MotorolaOrder )
 {
    return ((( unsigned char *) Short )[ 0 ] << 8 ) | (( unsigned char *) Short )[ 1 ];
 }
  else
 {
    return ((( unsigned char *) Short )[ 1 ] << 8 ) | (( unsigned char *) Short )[ 0 ];
 }
}
 
/*--------------------------------------------------------------------------
Convert a 32 bit signed value from file's native unsigned char order
--------------------------------------------------------------------------*/
STATIC long EXIF_Get32s ( void * Long )
{
  if ( m_MotorolaOrder )
 {
    return  ((( char *) Long )[ 0 ] << 24 ) | ((( unsigned char *) Long )[ 1 ] << 16 )
      | ((( unsigned char *) Long )[ 2 ] << 8 ) | ((( unsigned char *) Long )[ 3 ] << 0 );
 }
  else
 {
    return  ((( char *) Long )[ 3 ] << 24 ) | ((( unsigned char *) Long )[ 2 ] << 16 )
      | ((( unsigned char *) Long )[ 1 ] << 8 ) | ((( unsigned char *) Long )[ 0 ] << 0 );
 }
}
 
/*--------------------------------------------------------------------------
Convert a 32 bit unsigned value from file's native unsigned char order
--------------------------------------------------------------------------*/
STATIC ULONG EXIF_Get32u ( void * Long )
{
  return ( unsigned long ) EXIF_Get32s ( Long ) & 0XFFFFFFFF ;
}
 
/*--------------------------------------------------------------------------
Evaluate number, be it int, rational, or float from directory.
--------------------------------------------------------------------------*/
STATIC double EXIF_ConvertAnyFormat ( void * ValuePtr , int Format )
{
  double Value ;
  Value = 0 ;
 
  switch ( Format )
 {
  case FMT_SBYTE :     Value = *( signed char *) ValuePtr ;   break ;
  case FMT_BYTE :      Value = *( unsigned char *) ValuePtr ; break ;
   
  case FMT_USHORT :    Value = EXIF_Get16u ( ValuePtr );      break ;
  case FMT_ULONG :     Value = EXIF_Get32u ( ValuePtr );      break ;
   
  case FMT_URATIONAL :
  case FMT_SRATIONAL :
    {
      int Num , Den ;
      Num = EXIF_Get32s ( ValuePtr );
      Den = EXIF_Get32s ( 4 +( char *) ValuePtr );
      if ( Den == 0 )
      {
        Value = 0 ;
      }
      else
      {
        Value = ( double ) Num/Den ;
      }
      break ;
    }
   
  case FMT_SSHORT :    Value = ( signed short ) EXIF_Get16u ( ValuePtr ); break ;
  case FMT_SLONG :     Value = EXIF_Get32s ( ValuePtr );               break ;
   
  /* Not sure if this is correct (never seen float used in Exif format)
    */
  case FMT_SINGLE :    Value = ( double )*( float *) ValuePtr ;     break ;
  case FMT_DOUBLE :    Value = *( double *) ValuePtr ;            break ;
 }
  return Value ;
}
 
//
 
/*********************************************************************
  函数声明:
  参    数:
          IN:
         OUT:
         I/O:
    返回值:
  功能描述: 处理 JPG 文件中的注释信息
  引    用:
*********************************************************************/
STATIC void EXIF_Process_COM ( CONST UCHAR * Data , int length )
{
  int ch ;
  char Comment [ MAX_COMMENT + 1 ];
  int nch ;
  int a ;
 
  nch = 0 ;
 
  if ( length > MAX_COMMENT ) length = MAX_COMMENT ; // Truncate if it won't fit in our structure.
 
  for ( a = 2 ; a < length ; a ++)
 {
    ch = Data [ a ];
   
    if ( ch == '/r' && Data [ a + 1 ] == '/n' ) continue ; // Remove cr followed by lf.
   
    if (( ch >= 0x20 ) || ch == '/n' || ch == '/t' )
    {
      Comment [ nch ++] = ( char ) ch ;
    }
    else
    {
      Comment [ nch ++] = '?' ;
    }
 }
 
  Comment [ nch ] = '/0' ; // Null terminate
 
  //if (ShowTags) printf("COM marker comment: %s/n",Comment);
 
  strcpy ( m_pExifInfo -> Comments , Comment );
}
 
/*********************************************************************
  函数声明: STATIC BOOL EXIF_ProcessExifDir(...)
  参    数:
          IN: CONST UCHAR* DataStart: 数据流的起始位置。这个数值仅仅在函数 EXIF_Decode 中能够改变
              CONST DWORD dwFilePointerBeforeReadData: 在读取数据流之前的文件指针位置
              UCHAR *DirStart: SECTION 中数据流,去除了前面的 EXIF/0/0(6)+II(2)+2A00(2)+08000000(6)=14
              UCHAR *OffsetBase: 仅仅去除了 EXIFF/0/0(6)=6字节
              UINT ExifLength: 整个 SECTION 数据流的长度去除 EXIF/0/0后的长度==All Length - 6
              EXIFINFO * const m_exifinfo: 
         OUT:
         I/O:
              UCHAR **const LastExifRefdP: 偏移过后的位置
    返回值:
  功能描述:
  引    用:
*********************************************************************/
STATIC BOOL EXIF_ProcessExifDir ( CONST UCHAR * DataStart , CONST DWORD dwFilePointerBeforeReadData ,
                                UCHAR * DirStart , UCHAR * OffsetBase , CONST UINT ExifLength ,
                                EXIFINFO * const m_exifinfo , UCHAR ** const LastExifRefdP )
{
  int de = 0 ;                 //
  int a = 0 ;                  //
  int NumTagEntries = 0 ;      // 包含的 TAG 的个数
  UINT ThumbnailOffset = 0 ;   // 缩略图偏移量
  UINT ThumbnailSize = 0 ;     // 缩略图的大小
  int   BytesCount = 0 ;        //
  UCHAR * TagEntry = 0 ;       // 每个 TAG 的入口
  int Tag , Format , Components ;
  UCHAR * ValuePtr = 0 ;       // 偏移后的位置。因为 TAG 与内容很多时候都不是连续的,而是中间有个偏移量
  DWORD OffsetVal = 0 ;        // 偏移量
 
  // 读取文件中存在 TAG 个数
  NumTagEntries = EXIF_Get16u ( DirStart );
 
  // 判断 EXIF 信息的长度是否正确
  // 下面 DirStart+2 指再去除了 NumTagEntries 所占的 2 个字节
  if (( DirStart + 2 + NumTagEntries * 12 ) > ( OffsetBase + ExifLength ))
 {
    EXIF_ERR_OUT ( "Illegally sized directory" );
    return 0 ;
 }
  for ( de = 0 ; de < NumTagEntries ; de ++)
 {
    // 在下面的操作中,所有的数据通通使用 UCHAR* 来表示
    TagEntry = DirStart + 2 + 12 * de ;    //TagEntry 的入口点
    Tag = EXIF_Get16u ( TagEntry );
    Format = EXIF_Get16u ( TagEntry + 2 );
    Components = EXIF_Get32u ( TagEntry + 4 );
   
    if (( Format - 1 ) >= NUM_FORMATS )
    {
      //(-1) catches illegal zero case as unsigned underflows to positive large
      EXIF_ERR_OUT ( "Illegal format code in EXIF dir" );
      return 0 ;
    }   
    BytesCount = Components * m_BytesPerFormat [ Format ];
    if ( BytesCount > 4 )
    {
      OffsetVal = EXIF_Get32u ( TagEntry + 8 );
      //If its bigger than 4 unsigned chars, the dir entry contains an offset.
      if ( OffsetVal + BytesCount > ExifLength )
      {
        //JPG 文件内容遭到破坏
        EXIF_ERR_OUT ( "Illegal pointer offset value in EXIF." );
        return 0 ;
      }
      ValuePtr = OffsetBase + OffsetVal ;
    }
    else
    {
      //4 unsigned chars or less and value is in the dir entry itself
      ValuePtr = TagEntry + 8 ;
    }
    if (* LastExifRefdP < ValuePtr + BytesCount )
    {
      // 当前已经处理的进度
      // 这样可以再次的检测 JPG 文件的合法性
      * LastExifRefdP = ValuePtr + BytesCount ;
    }
 
    // Extract useful components of tag
    switch ( Tag )
    {
    case TAG_MAKE :
      strncpy ( m_exifinfo -> CameraMake , ( char *) ValuePtr , 31 );
      break ;
    case TAG_MODEL :
      strncpy ( m_exifinfo -> CameraModel , ( char *) ValuePtr , 39 );
      break ;
     
    case TAG_EXIF_VERSION :
      strncpy ( m_exifinfo -> Version ,( char *) ValuePtr , 4 );
      break ;
    // 日期和时间 
    case TAG_DATETIME_ORIGINAL :
      strncpy ( m_exifinfo -> DateTime , ( char *) ValuePtr , 19 );
      break ;
    case TAG_DATATIME_DIGITIZED :
      strncpy ( m_exifinfo -> DateTimeDigitized , ( char *) ValuePtr , 19 );
      break ;
    // 用户注释
    case TAG_USERCOMMENT :
      m_exifinfo -> UserCOMLength = BytesCount ;
      if ( m_exifinfo -> dwExifType & ET_MALLOC_USERCOM )
      {
        m_exifinfo -> UserCOM = malloc ( BytesCount );
        memcpy ( m_exifinfo -> UserCOM , ValuePtr , BytesCount );
        /*/ /Olympus 誠cf9 誠cf9 蟎cf9 郳cf9 籠cf9 鶿cf9 籠cf9 醆cf9 診cf9 赲cf9 譢cf9 cf9 蔦cf9 蚛cf9 礬cf9 腬cf9 譢cf9 頫cf9 篭cf9 骪cf9 肻cf9 鎈cf9 蘚cf9 頫cf9 砛cf9 鋅cf9 縗cf9 誠cf9 竆cf9 馶cf8 0x20禱cf9 鳿cf9 瞈cf9 籠cf9 蔦cf9 荺cf7 '/0',
        // 下面先将后面的空格替换成 '/0' 然后再拷贝注释Comment
        for ( a = BytesCount ; a > 0 ;)
        {
          a --;
          if ((( char *) ValuePtr )[ a ] == ' ' )
          {
            (( char *) ValuePtr )[ a ] = '/0' ;
          }
          else
          {
            break ;
          }
        }
        // 将用户注释拷贝到 exifinfo.Comments 中
         // 首先判断是否是 ASCII 模式(就是注释的前面 5 个字符是 ASCII)
        // 如果是,则取消拷贝最前面的 ASCII 五个字符
        if ( memcmp ( ValuePtr , "ASCII" , 5 ) == 0 )
        {
          for ( a = 5 ; a < 10 ; a ++)
          {
            char c ;
            c = (( char *) ValuePtr )[ a ];
            if ( c != '/0' && c != ' ' )
            {
              strncpy ( m_exifinfo -> Comments , ( char *) ValuePtr + a , MAX_COMMENT - 1 );
              break ;
            }
          }       
        }
        else
        {
          strncpy ( m_exifinfo -> Comments , ( char *) ValuePtr , MAX_COMMENT - 1 );
        }*/
      }
      else
      {
        // 记录用户注释相对于整个文件起始处的偏移量
        m_exifinfo -> UserCOM = ( CHAR *)( OffsetBase + OffsetVal - DataStart + dwFilePointerBeforeReadData );
        //m_exifinfo->UserCOM = (char*)OffsetVal; // 偏移
      }
      break ;
    // 厂商注释
    case TAG_MAKE_COMMENT :
      m_exifinfo -> MakerCOMLength = BytesCount ;
      if ( m_exifinfo -> dwExifType & ET_MALLOC_MAKERCOM )
      {
        m_exifinfo -> MakerCOM = malloc ( BytesCount );
        memcpy ( m_exifinfo -> MakerCOM , ValuePtr , BytesCount );
      }
      else
      {
        m_exifinfo -> MakerCOM = ( char *) OffsetVal ; // 偏移
      }
      break ;
    // 光圈
    case TAG_FNUMBER :
      m_exifinfo -> ApertureFNumber = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
 
    case TAG_APERTURE :    // 光圈值
    case TAG_MAXAPERTURE : // 最大光圈值
    //More relevant info always comes earlier, so only
    //use this field if we don't have appropriate aperture
    //information yet.
    /*- if (m_exifinfo->ApertureFNumber == 0)
      {
        m_exifinfo->ApertureFNumber = (float)exp(EXIF_ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);//ATTENTION
        m_exifinfo->ApertureFNumber = (float)(EXIF_ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
      }-*/
      break ;
 
    //Brightness
    case TAG_BRIGHTNESS :
      m_exifinfo -> Brightness = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
 
    // 焦距信息(例如 7.09mm)     
    case TAG_FOCALLENGTH :
    //Nice digital cameras actually save the focal length
    //as a function of how farthey are zoomed in.
     
      m_exifinfo -> FocalLength = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
 
    // 目标距离(例如 1.11米)
    case TAG_SUBJECT_DISTANCE :
    //Inidcates the distacne the autofocus camera is focused to.
    //Tends to be less accurate as distance increases.
      m_exifinfo -> Distance = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
 
    // 曝光时间(例如 1/30 秒)
    case TAG_EXPOSURETIME :
    //Simplest way of expressing exposure time, so I
    //trust it most. (overwrite previously computd value
    //if there is one)
      m_exifinfo -> ExposureTime =
        ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
 
    //SHUTTERSPEED 快门速度不需要   
    case TAG_SHUTTERSPEED :
    //More complicated way of expressing exposure time,
    //so only use this value if we don't already have it
    //from somewhere else. 
    /*- if (m_exifinfo->ExposureTime == 0)
      {
        m_exifinfo->ExposureTime = (float)
          (1/exp(EXIF_ConvertAnyFormat(ValuePtr, Format)*log(2)));
      }-*/
      break ;
 
  //FLASH 闪光灯信息不需要     
    case TAG_FLASH :
      if (( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format ) & 7 )
      {
        m_exifinfo -> FlashUsed = 1 ;
      }
      else
      {
        m_exifinfo -> FlashUsed = 0 ;
      }
      break ;
     
    case TAG_ORIENTATION :
      m_exifinfo -> Orientation = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      if ( m_exifinfo -> Orientation < 1 || m_exifinfo -> Orientation > 8 )
      {
        EXIF_ERR_OUT ( "Undefined rotation value" );
        m_exifinfo -> Orientation = 0 ;
      }
      break ;
    //EXIF 图像高度与宽度(例如 1024*768)
    case TAG_EXIF_IMAGELENGTH :
    case TAG_EXIF_IMAGEWIDTH :
      a = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      if ( m_ExifImageWidth < a ) m_ExifImageWidth = a ;
      break ;
  // 焦平面 X 轴分辨率(例如 1024000/278),理论上与 Y 一致
    case TAG_FOCALPLANEXRES :
      m_exifinfo -> FocalplaneXRes = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
  // 焦平面 Y 轴分辨率(例如 768000/209),理论上与 X 一致
    case TAG_FOCALPLANEYRES :
      m_exifinfo -> FocalplaneYRes = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    case TAG_RESOLUTIONUNIT :
      switch (( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format ))
      {
      case 1 : m_exifinfo -> ResolutionUnit = 1 . 0f ;             break ;   // 1 inch
      case 2 : m_exifinfo -> ResolutionUnit = 1 . 0f ;             break ;   //
      case 3 : m_exifinfo -> ResolutionUnit = 0 . 3937007874f ;    break ;   // 1 centimeter
      case 4 : m_exifinfo -> ResolutionUnit = 0 . 03937007874f ;   break ;   // 1 millimeter
      case 5 : m_exifinfo -> ResolutionUnit = 0 . 00003937007874f ;         // 1 micrometer
      }
      break ;
    // 焦平面分辨率单位(例如米)
    case TAG_FOCALPLANEUNITS :
      switch (( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format ))
      {
      case 1 : m_exifinfo -> FocalplaneUnits = 1 . 0f ;             break // 1 inch
      case 2 : m_exifinfo -> FocalplaneUnits = 1 . 0f ;             break //
      case 3 : m_exifinfo -> FocalplaneUnits = 0 . 3937007874f ;    break // 1 centimeter
      case 4 : m_exifinfo -> FocalplaneUnits = 0 . 03937007874f ;   break // 1 millimeter
      case 5 : m_exifinfo -> FocalplaneUnits = 0 . 00003937007874f ; break // 1 micrometer//
      }
      break ;
 
    // 曝光补偿信息
    case TAG_EXPOSURE_BIAS :
      m_exifinfo -> ExposureBias = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    // 白平衡
    case TAG_WHITEBALANCE :
      m_exifinfo -> Whitebalance = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    case TAG_METERING_MODE :
      m_exifinfo -> MeteringMode = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    case TAG_EXPOSURE_PROGRAM :
      m_exifinfo -> ExposureProgram = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    case TAG_ISO_EQUIVALENT :
      m_exifinfo -> ISOequivalent = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      if ( m_exifinfo -> ISOequivalent < 50 ) m_exifinfo -> ISOequivalent *= 200 ;
      break ;
    case TAG_COMPRESSION_LEVEL :
      m_exifinfo -> CompressionLevel = ( int ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    //X 轴分辨率
    case TAG_XRESOLUTION :
      m_exifinfo -> Xresolution = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    //Y 轴分辨率
    case TAG_YRESOLUTION :
      m_exifinfo -> Yresolution = ( float ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    // 缩略图 偏移量 
    case TAG_THUMBNAIL_OFFSET :
      ThumbnailOffset = ( unsigned ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
      break ;
    // 缩略图的大小
    case TAG_THUMBNAIL_LENGTH :
        ThumbnailSize = ( unsigned ) EXIF_ConvertAnyFormat ( ValuePtr , Format );
        break ;
    } //end switch(Tag)
    //EXIF 信息偏移
    //
    if ( Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET )
    {
      UCHAR * SubdirStart ;
      SubdirStart = OffsetBase + EXIF_Get32u ( ValuePtr );
       if ( SubdirStart < OffsetBase ||
        SubdirStart > OffsetBase + ExifLength )
      {
        EXIF_ERR_OUT ( "Illegal subdirectory link" );
        return 0 ;
      }
      EXIF_ProcessExifDir ( DataStart , dwFilePointerBeforeReadData , SubdirStart , OffsetBase , ExifLength , m_exifinfo , LastExifRefdP );
      continue ;
    } 
 } //end for {for (de=0;de<NumTagEntries;de++)}
 {
    //In addition to linking to subdirectories via exif tags,
    //there's also a potential link to another directory at the end
    //of each directory. This has got to be the result of a
    //committee! 
   
    UCHAR * SubdirStart ;
    unsigned Offset ;
    Offset = EXIF_Get16u ( DirStart + 2 + 12 * NumTagEntries );
    if ( Offset )
    {
      SubdirStart = OffsetBase + Offset ;
      if ( SubdirStart < OffsetBase
        || SubdirStart > OffsetBase + ExifLength )
      {
        EXIF_ERR_OUT ( "Illegal subdirectory link" );
        return 0 ;
      }
      EXIF_ProcessExifDir ( DataStart , dwFilePointerBeforeReadData , SubdirStart , OffsetBase , ExifLength , m_exifinfo , LastExifRefdP );
    }
 }
 
  if ( ThumbnailSize && ThumbnailOffset )
 {
    // 如果文件中存在缩略图,那么将缩略图的数据保存
    // 注意:这里仅仅负责 malloc,调用者需要自己 free
    if ( ThumbnailSize + ThumbnailOffset <= ExifLength )
    {
      // 将缩略图的数据全部拷贝到一块新开辟的内存
      if ( m_exifinfo -> dwExifType & ET_MALLOC_THUMBNAIL )
      {
        UCHAR * pThumbnailData = OffsetBase + ThumbnailOffset ;
        DWORD dw = pThumbnailData - DataStart + dwFilePointerBeforeReadData ;
        m_exifinfo -> ThumbnailPointer = ( UCHAR *) malloc ( ThumbnailSize );
        memcpy ( m_exifinfo -> ThumbnailPointer , pThumbnailData , ThumbnailSize );
      }
      else
      {
        m_exifinfo -> ThumbnailPointer = ( UCHAR *)( OffsetBase + ThumbnailOffset - DataStart + dwFilePointerBeforeReadData );
      }
      m_exifinfo -> ThumbnailSize = ThumbnailSize ;
    }
 }
 
  return TRUE ;
}
 
/*********************************************************************
  函数声明: STATIC BOOL EXIF_process_EXIF(UCHAR * CharBuf, UINT length)
  参    数:
          IN: CONST UCHAR* DataStart: 数据流的起始位置。这个数值仅仅在函数 EXIF_Decode 中能够改变
              CONST DWORD dwFilePointerBeforeReadData: 在读取数据流之前的文件指针位置
              UCHAR * CharBuf: 这个 SECTION 数据内容。注意:前面已经去掉了包含长度的2个字符
              CONST UINT length: 这个 SECTION 数据流的长度
    返回值:
  功能描述: 处理某个 SECTION 中的 EXIF 信息。
            成功返回TRUE表示EXIF信息存在且正确,失败返回FALSE
  引    用:
*********************************************************************/
STATIC BOOL EXIF_process_EXIF ( CONST UCHAR * DataStart CONST DWORD dwFilePointerBeforeReadData ,
                              UCHAR * CharBuf , CONST UINT length )
{
  int FirstOffset = 0 ;
  UCHAR * LastExifRefd = 0 ;
  m_pExifInfo -> FlashUsed = 0 ;
  m_pExifInfo -> Comments [ 0 ] = '/0' ;
 
  m_ExifImageWidth = 0 ;
 
  // 检查 EXIF 头是否正确
 {
    static const unsigned char ExifHeader [] = "Exif/0/0" ;
    if ( memcmp ( CharBuf + 0 , ExifHeader , 6 ))
    {
      EXIF_ERR_OUT ( "Incorrect Exif header" );
      return 0 ;
    }
 }
  // 判断内存中数据的排列是按照 Intel 还是按照 Motorola CPU 排列的
  if ( memcmp ( CharBuf + 6 , "II" , 2 ) == 0 )
 {
    m_MotorolaOrder = 0 ;    //
 }
  else if ( memcmp ( CharBuf + 6 , "MM" , 2 ) == 0 )
 {
    m_MotorolaOrder = 1 //
 }
  else
 {
    EXIF_ERR_OUT ( "Invalid Exif alignment marker." );
    return 0 ;
 }
 
  // 检查下面 2 个字节是否是 0x2A00
  if ( EXIF_Get16u ( CharBuf + 8 ) != 0x2A )
 {
    EXIF_ERR_OUT ( "Invalid Exif start (1)" );
    return 0 ;
 }
 
  // 判断下面的 0th IFD Offset 是否是 0x08000000
  FirstOffset = EXIF_Get32u ( CharBuf + 10 );
  if ( FirstOffset < 8 || FirstOffset > 16 )
 {
    EXIF_ERR_OUT ( "Suspicious offset of first IFD value" );
    return 0 ;
 }
 
  LastExifRefd = CharBuf ;
 
  // 开始处理 EXIF 信息
  if (! EXIF_ProcessExifDir ( DataStart , dwFilePointerBeforeReadData ,
    CharBuf + 14 , CharBuf + 6 , length - 6 , m_pExifInfo , & LastExifRefd ))
 {
    return 0 ;
 }
 
  // This is how far the interesting (non thumbnail) part of the exif went.
  // int ExifSettingsLength = LastExifRefd - CharBuf; 
  // 计算 CCD 宽度(单位:毫米)
  if ( m_pExifInfo -> FocalplaneXRes != 0 )
 {
    m_pExifInfo -> CCDWidth = ( float )( m_ExifImageWidth * m_pExifInfo -> FocalplaneUnits / m_pExifInfo -> FocalplaneXRes );
 }
 
  return 1 ;
}
 
STATIC VOID EXIF_process_SOFn ( CONST UCHAR * Data , int marker )
{
  int data_precision , num_components ;
 
  data_precision = Data [ 2 ];
  m_pExifInfo -> Height = EXIF_Get16m (( void *)( Data + 3 ));
  m_pExifInfo -> Width = EXIF_Get16m (( void *)( Data + 5 ));
  num_components = Data [ 7 ];
 
  if ( num_components == 3 )
 {
    m_pExifInfo -> IsColor = 1 ;
 }
  else
 {
    m_pExifInfo -> IsColor = 0 ;
 }
 
  m_pExifInfo -> Process = marker ;
 
  //if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample/n",
  //               ImageInfo.Width, ImageInfo.Height, num_components, data_precision);
}
 
STATIC int EXIF_Decode ( HANDLE hFile )
{
  int a = 0 , b = 0 ;
  int nHaveCom = 0 ;             // 是否存在注释,并且保存注释字符串的长度
  int nSectionsRead = 0 ;        // 已经读取 SECTION 的个数
  DWORD dwFileRead = 0 ;         // 使用 ReadFile 读取文件时,读取的字节数
  DWORD dwFilePointerBeforeReadData = 0 // 在读取数据流之前,文件指针的位置
  Section_t Sections [ MAX_SECTIONS ];       //JPG 文件中 SECTIONS
 
    int    nSectionLength = 0 ;     //SECTION(APP) 长度
    int    marker = 0 ;           //
    int    ll = 0 , lh = 0 , got = 0 ;     //
    UCHAR * Data = 0 ;            //
 
  // 读入 JPG 第1, 2个字节,判断是否是 0xFF,M_SOI
  ReadFile ( hFile , & a , 1 , & dwFileRead , NULL );
  if ( dwFileRead != 1 )
 {
    EXIF_ERR_OUT ( "Unexpect File End" );
    return - 1 ;
 }
  ReadFile ( hFile , & b , 1 , & dwFileRead , NULL );
  if ( dwFileRead != 1 )
 {
    EXIF_ERR_OUT ( "Unexpect File End" );
    return - 1 ;
 }
  // 判断该文件是否是 EXIF 文件
  //EXIF 文件的起始 2 字节必定是 FF D8
  if ( a != 0xFF || b != M_SOI )
 {
    EXIF_ERR_OUT ( "File Format Error" );
    return - 1 ;
 }
  // 使用一个循环,读取 JPG 文件中的 SECTION
  // 第一个 SECTION 肯定是 APP1,而APP1起始的Marker肯定为 FFE1
  for (;;)
 {
    if ( nSectionsRead >= MAX_SECTIONS )
    {
      EXIF_ERR_OUT ( "Too many sections in this jpg file" );
      return - 1 ;
    }
    // 查找 JPG 文件填充字符,接下来的7个字符必须有一个不是 0xFF
    for ( a = 0 ; a < 7 ; a ++)
    {
      ReadFile ( hFile , & marker , 1 , & dwFileRead , NULL );
      if ( dwFileRead != 1 )
      {
        EXIF_ERR_OUT ( "Unexpect File End" );
        return - 1 ;
      }
      if ( marker != 0xFF ) break ;
     
      if ( a >= 6 )
      {
        EXIF_ERR_OUT ( "Too many padding unsigned chars" );
        return - 1 ;
      }
    }
#ifdef _DEBUG
    if ( nSectionsRead == 0 // 是 APP 1
    {
      ASSERT ( marker == M_APP1 );
    }
#endif
    Sections [ nSectionsRead ]. Type = marker ;
    // 记录读取流数据之前的文件指针位置
    dwFilePointerBeforeReadData = SetFilePointer ( hFile , 0 , NULL , FILE_CURRENT );
    // 读取这个 SECTION 的长度
    ReadFile ( hFile , & lh , 1 , & dwFileRead , NULL );
    ReadFile ( hFile , & ll , 1 , & dwFileRead , NULL );
    nSectionLength = ( lh << 8 ) | ll ;    //EXIF 文件高字节在前,低字节在后,不能读取一个WORD类型
   
    if ( nSectionLength < 2 )
    {
      EXIF_ERR_OUT ( "Invalid Marker" );
      return - 1 ;
    }
    Data = ( UCHAR *) malloc ( nSectionLength );
    if ( Data == NULL )
    {
      EXIF_ERR_OUT ( "Could not allocate memory!" );
      return - 1 ;
    }
    Sections [ nSectionsRead ]. Data = Data ;
   
    // Store first two pre-read unsigned chars.
    Data [ 0 ] = ( UCHAR ) lh ;
    Data [ 1 ] = ( UCHAR ) ll ;
    ReadFile ( hFile , Data + 2 , nSectionLength - 2 , & dwFileRead , NULL );
    if ( dwFileRead !=( DWORD )( nSectionLength - 2 ))
    {
      EXIF_ERR_OUT ( "Premature end of file?" );
      return - 1 ;
    }
    dwFileRead = SetFilePointer ( hFile , 0 , NULL , FILE_CURRENT );
    // 得到当前文件的指针位置
    nSectionsRead += 1 ;
    switch ( marker )
    {
    case M_SOS :   // 到了数据区
      return 1 ;
    case M_EOI :   //End Of Image
      EXIF_ERR_OUT ( "No image in this jpeg file" );
      return - 1 ;
    case M_COM :   // 注释区
      if ( nHaveCom )
      {
        // Discard this section.
        free ( Sections [-- nSectionsRead ]. Data );
        Sections [ nSectionsRead ]. Data = 0 ;
      }
      else
      {
        EXIF_Process_COM ( Data , nSectionLength );
        nHaveCom = 1 ;
      }
    case M_JFIF :
      // 标准的 JPG 文件通常都有 TAG,并且处于 M_APP0 中
      //EXIF 图像使用 exif 标记(在 M_APP1 中)来取代这个
      // 但是有些软件(例如 ACDSee)在修改 JPG 文件后会同时保留这 2 个TAG
      // 我们在这里不需要 M_JFIF 信息,直接跳过去即可
      // 如果是重写文件,仅仅需要把 JFIF 信息复制到新的JPG文件头
      free ( Sections [-- nSectionsRead ]. Data );
      Sections [ nSectionsRead ]. Data = 0 ;
      break ;
    case M_EXIF :
      //EXIF 信息 TAG
      if ( memcmp ( Data + 2 , "Exif" , 4 ) == 0 )
      {
        m_pExifInfo -> IsExif = EXIF_process_EXIF ( Data , dwFilePointerBeforeReadData , ( unsigned char *) Data + 2 , nSectionLength );
      }
      else
      {
        // Discard this section.
        free ( Sections [-- nSectionsRead ]. Data );
        Sections [ nSectionsRead ]. Data = 0 ;
      }
      break ;
    case M_SOF0 :
    case M_SOF1 :
    case M_SOF2 :
    case M_SOF3 :
    case M_SOF5 :
    case M_SOF6 :
    case M_SOF7 :
    case M_SOF9 :
    case M_SOF10 :
    case M_SOF11 :
    case M_SOF13 :
    case M_SOF14 :
    case M_SOF15 :
      EXIF_process_SOFn ( Data , marker );
      break ;
    default :
      // Skip any other sections.
      //if (ShowTags) printf("Jpeg section marker 0x%02x size %d/n",marker, nSectionLength);
      break ;
    }
 }
  return 1 ;
}
/*********************************************************************
  函数声明:
  参    数:
          IN: LPCTSTR pszJpgFileName: JPG 文件全路径名
         OUT:
         I/O: EXIFINFO* pExifInfo: 保存了 EXIF 信息的结构体
    返回值: >=0表示成功,<0读取失败
  功能描述: 读取并返回 JPG 文件中的 EXIF 信息
  引    用: 外部调用者
  注    意:
*********************************************************************/
int EXIF_Read ( LPCTSTR pszJpgFileName , EXIFINFO * pExifInfo )
{
  int nReturn = - 1 ;
  HANDLE hFile = INVALID_HANDLE_VALUE ;
  if ( pExifInfo == 0 )
 {
    EXIF_ERR_OUT ( "Parameter incorreted! pExifInfo must not be NULL!" );
    return - 1 ;
 }
  hFile = CreateFile ( pszJpgFileName //LPCTSTR lpcszFileName
    GENERIC_READ ,                     //DWORD dwAccess
    FILE_SHARE_READ ,                  //DWORD dwShareMode
    NULL ,                             //LPSECURITY_ATTRIBUTES lpSecurityAttributes
    OPEN_EXISTING ,                    //DWORD dwCreate. 打开文件,如果不存在则失败
    FILE_ATTRIBUTE_NORMAL ,            //DWORD dwFlagsAndAttributes
    NULL                               //HANDLE hTemplateFile
    );
  // 打开 JPG 文件失败
  if ( hFile == INVALID_HANDLE_VALUE )
 {
    EXIF_ERR_OUT ( "JPG File Not Found!" );
    return - 1 ;
 }
  // 将文件指针移到最前
  SetFilePointer ( hFile , 0 , NULL , FILE_BEGIN );
  // 开始处理 JPG 文件
  pExifInfo -> ThumbnailPointer = NULL ;
  pExifInfo -> ThumbnailSize = 0 ;
  pExifInfo -> IsExif = FALSE ;
 
  m_pExifInfo = pExifInfo ;
  nReturn = EXIF_Decode ( hFile );
  if ( nReturn >= 0 && ( m_pExifInfo -> dwExifType & ET_NOT_CLOSE_FILE ) )
 {
    m_pExifInfo -> hJpgFileHandle = hFile ;
 }
  else
 {
    CloseHandle ( hFile );
 }
  return nReturn ;
}
 
/*********************************************************************
  函数声明: int EXIF_AddUserComments(LPCTSTR pszJpgFileName, LPCTSTR pszUserComments, DWORD dwCommentLength, COMMENT_TYPE nCommentType)
  参    数:
          IN: LPCTSTR pszJpgFileName: JPG 文件全路径名
              LPCTSTR pszUserComments: 需要写入的注释
              DWORD dwCommentLength: 需要写入的注释的长度
              COMMENT_TYPE nCommentType: 注释写入时的类型,例如 ASCII, UNICODE, JIS 等
         OUT:
         I/O:
    返回值: 成功返回一个>0的数值表示写入的注释长度,失败返回<0
  功能描述: 将指定的用户注释写入到 JPG 文件中
  引    用:
*********************************************************************/
int EXIF_AddUserComments ( LPCTSTR pszJpgFileName , LPCTSTR pszUserComments , DWORD dwCommentLength , COMMENT_TYPE ctCommentType )
{
  int nReturn = - 1 ;
  EXIFINFO exifinfo = { 0 };
  HANDLE hFile = INVALID_HANDLE_VALUE ;
  DWORD dwWriteBytes = 0 ;
  TCHAR pszCommentType [ 8 ] = { 0 };
  switch ( ctCommentType )
 {
  case CT_ASCII :
    strcpy ( pszCommentType , "ASCII" );
    break ;
  case CT_UNDEFINE :
    strcpy ( pszCommentType , "UNICODE" );
    break ;
  case CT_JIS :
    strcpy ( pszCommentType , "JIS" );
    break ;
 }
 
  if ( pszUserComments == 0 )
 {
    EXIF_ERR_OUT ( "Parameter incorreted! pszUserComments must not be NULL!" );
    return - 1 ;
 }
  if ( dwWriteBytes > strlen ( pszUserComments ))
 {
    EXIF_ERR_OUT ( "dwWriteBytes must be bigger or equal than the length of user comments!" );
    return - 1 ;
 }
  hFile = CreateFile ( pszJpgFileName //LPCTSTR lpcszFileName
    GENERIC_WRITE | GENERIC_READ ,       //DWORD dwAccess
    FILE_SHARE_WRITE | FILE_SHARE_READ , //DWORD dwShareMode
    NULL ,                             //LPSECURITY_ATTRIBUTES lpSecurityAttributes
    OPEN_ALWAYS ,                      //DWORD dwCreate. 打开文件,如果不存在则创建
    FILE_ATTRIBUTE_NORMAL ,            //DWORD dwFlagsAndAttributes
    NULL                               //HANDLE hTemplateFile
    );
  // 打开 JPG 文件失败
  if ( hFile == INVALID_HANDLE_VALUE )
 {
    EXIF_ERR_OUT ( "JPG File Not Found!" );
    return - 1 ;
 }
  // 将文件指针移到最前
  SetFilePointer ( hFile , 0 , NULL , FILE_BEGIN );
  // 开始处理 JPG 文件
  exifinfo . dwExifType |= ET_NOT_CLOSE_FILE ;
  exifinfo . ThumbnailPointer = NULL ;
  exifinfo . ThumbnailSize = 0 ;
  exifinfo . UserCOM = 0 ;
  exifinfo . UserCOMLength = 0 ;
  m_pExifInfo = & exifinfo ;
  nReturn = EXIF_Decode ( hFile );
  if ( nReturn >= 0 && ( m_pExifInfo -> dwExifType & ET_NOT_CLOSE_FILE ) )
 {
    m_pExifInfo -> hJpgFileHandle = hFile ;
 }
  else
 {
    CloseHandle ( hFile );
 }
  if ( nReturn < 0 )
 {
    return nReturn ;
 }
  // 如果这个 JPG 文件中没有内存缓冲区
  if ( m_pExifInfo -> UserCOMLength <= 8 )
 {
    EXIF_ERR_OUT ( "This JPG file not include user comments buffer area!" );
    CloseHandle ( hFile );
    return - 1 ;
 }
  SetFilePointer ( hFile , 0 , 0 , FILE_BEGIN );
  SetFilePointer ( hFile , ( DWORD ) m_pExifInfo -> UserCOM , 0 , FILE_BEGIN );
  // 下面写入用户的注释
  // 写入编码方式
  WriteFile ( hFile , pszCommentType , 8 , & dwWriteBytes , NULL );
  m_pExifInfo -> UserCOMLength -= 8 // 前面有 8 个字节的编码方式
  if ( m_pExifInfo -> UserCOMLength > dwWriteBytes )
 {
    WriteFile ( hFile , pszUserComments , dwCommentLength + 1 , & dwWriteBytes , NULL );
    nReturn = ( int ) dwWriteBytes ;
 }
  else
 {
    WriteFile ( hFile , pszUserComments , m_pExifInfo -> UserCOMLength + 1 , & dwWriteBytes , NULL );
    nReturn = ( int ) dwWriteBytes ;
 }
  // 再写入 /0
  //WriteFile(hFile, '/0', 1, &dwWriteBytes, NULL);
  return nReturn ;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值