jpg中读取GPS信息

需要读取EXIF信息,自己阅读了一些资料,现在共享一下
需要注意的是,由于EXIF是一种可交换的文件格式,所以可以用在Intel系列和Motorola系列的CPU上(至于两者CPU的区别,大家可以到网上找找,这里不做说明)。在文件中有一个标志,如果是“MM”表示MotorolaCPU,否则为“II”表示IntelCPU
  

这个代码是使用C写的,读取文件用的API。本来我想写成一个VC使用的类的,但是自己还是太懒了。需要的自己去转换吧

 

 


//      .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;
}

 

转自:http://blog.vckbase.com/hengai/archive/2005/11/08/14612.aspx

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值