天天看点

mfc zlib minizip vs2019

需要注意Release和Debug使用的dll可能有差异,Release时需要使用对应的Dll

zlib源码文件中zlib123\contrib\vstudio此文件夹包含所需的zip、unzip相关实例代码minizip

最新版为zlib1.2.11

  1. 相关库文件
  2. 导入zlib.h,zconf.h

include "zlib.h"

include "zconf.h"

pragma comment(lib,"zlib.lib")

LNK2001:

默认调用规则不一致,添加预定义即可

define ZLIB_WINAPI

LNK4098: defaultlib "libc.lib" conflicts with use of other libs; use /NODEFAULTLIB:library

项目->属性中->配置属性->链接器->输入->在忽略特定库中写入打不开的文件的名称libc.lib

D8016:

项目”—>“属性”—>“C/C++” “常规”—>“调试信息格式”—>选择“程序数据库(/Zi)”或“无”

项目”—>“属性”—>“C/C++” 代码生成”—>“启用函数集链接”—>选择“是 (/Gy)”

LNK1104无法打开libc.lib

“项目”-->“配置属性->链接器->输入->忽略特定库”后填入“LIBC.lib ”。

LNK1281 无法生成 SAFESEH 映像

“项目”-->“配置属性->链接器-命令行 输入 /SAFESEH:NO

1>unzip.obj : error LNK2019: 无法解析的外部符号_inflateInit2_,该符号在函数_unzOpenCurrentFile3 中被引用

1>unzip.obj : error LNK2019: 无法解析的外部符号_inflate,该符号在函数_unzReadCurrentFile 中被引用

1>unzip.obj : error LNK2019: 无法解析的外部符号_crc32,该符号在函数_unzReadCurrentFile 中被引用

1>zip.obj : error LNK2001: 无法解析的外部符号_crc32

1>unzip.obj : error LNK2019: 无法解析的外部符号_inflateEnd,该符号在函数_unzCloseCurrentFile 中被引用

1>zip.obj : error LNK2019: 无法解析的外部符号_get_crc_table,该符号在函数_zipOpenNewFileInZip4_64 中被引用

1>zip.obj : error LNK2019: 无法解析的外部符号_deflateInit2_,该符号在函数_zipOpenNewFileInZip4_64 中被引用

1>zip.obj : error LNK2019: 无法解析的外部符号_deflate,该符号在函数_zipWriteInFileInZip 中被引用

1>zip.obj : error LNK2019: 无法解析的外部符号_deflateEnd,该符号在函数_zipCloseFileInZipRaw64 中被引用

mfc zlib minizip vs2019

zconf.h

# define ZEXPORT WINAPI   替换为           #define ZEXPORT __cdecl
#define ZEXPORTVA WINAPIV    替换为   #define WINAPIV     __cdecl      

解压缩时在inffas32.asm中报错的处理方式&编译时会提示inflate_fast重复定义

1. 在zlib源码的zlibvc工程属性-预处理器定义里面去掉 ASMINF、ASMV 定义,屏蔽掉汇编模块
2. 打开zlib-1.2.11\contrib\masmx86下面的汇编文件inffas32.asm,将里面_inflate_fast全部替换成其他任意函数名      

zip相关实现

zip解压缩

  1. 将zlib-1.x.x\contrib\minizip文件夹下除miniunz.c和minizip.c以外的.h与.c文件放到项目目录下,并将zip.h与unzip.h导入到工程中。
  2. 打开需要解压缩的文件
//传入zip文件的绝对路径,返回一个unzFile对象。这是一个void*指针,用于指向打开的zip文件
unzFile unzOpen(const char *path);      

操作完毕后需要关闭已打开的压缩文件:

int unzClose OF(unzFile file);      
  1. 获取zip文件的信息
int unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info);      

传入获取到的unzFile对象,传入一个空的unz_global_info,函数执行完成后,会将zip文件的信息填充到该unz_global_info指针中。

传入的unz_global_info定义如下:

//注意number_entry的值是zip文件中的文件盒文件夹总数。与是否嵌套无关。
//例如:
//将3个文件打包成一个zip,那么获取到的该值就是3;
//一个文件夹A,含有2个文件以及一个子文件夹B;B中含有1个文件。直接对A进行打包,那么调用该函数获取到的值是5。这是因为这个压缩包中,共含有2个文件//夹和3个文件,总数为5。
typedef struct unz_global_info_s
{
  uLong number_entry;         // zip文件中的文件和文件夹总数
  uLong size_comment;         // zip文件的注释所占大小
} unz_global_info;      

返回值

#define UNZ_OK                          (0)
#define UNZ_END_OF_LIST_OF_FILE         (-100)
#define UNZ_ERRNO                       (Z_ERRNO)
#define UNZ_EOF                         (0)
#define UNZ_PARAMERROR                  (-102)
#define UNZ_BADZIPFILE                  (-103)
#define UNZ_INTERNALERROR               (-104)
#define UNZ_CRCERROR                    (-105)      
  1. 对于zip文件中定位的当前文件,可以通过函数来获取其信息:
int unzGetCurrentFileInfo(  unzFile file,
              unz_file_info *pfile_info,
              char *szFileName,
              uLong fileNameBufferSize,
              void *extraField,
              uLong extraFieldBufferSize,
              char *szComment,
              uLong commentBufferSize);      
关于其参数:
①   unzFile file:已打开的unzFile对象

②   unz_file_info *pfile_info:一个头文件信息对象的指针,传入后,函数会将当前文件的信息填充到该指针指向的对象中。详见下面说明

③   char *szFileName:用于保存文件名,需在外部new出足够大的存储空间,函数会将文件的路径+文件名保存在该对象中。注意该文件名的路径是个相对路径,其最顶层为被压缩的最上层文件或文件夹

④   uLong fileNameBufferSize:与③统一,告知函数new了多大的char*内存

⑤   void* extraField:用于保存扩展内容,需在外部new出足够大的存储空间,函数会将文件的扩展内容保存在该对象中

⑥   uLong extraFieldBufferSize:与⑤统一,告知函数new了多大的内存

⑦   char* szComment:用于保存注释内容,需在外部new出足够大的存储空间,函数会将文件的注释内容保存在该对象中

⑧   uLong commentBufferSize:与⑦统一,告知函数new了多大的内存      

对于其中的unz_file_info结构体,定义如下:

typedef struct unz_file_info_s
{
  uLong version;              // 压缩文件所使用的pkware版本 2 bytes
  uLong version_needed;       // 解压文件所需pkware版本   2 bytes
  uLong flag;                 // 全局方式位标记        2 bytes
  uLong compression_method;   // 压缩方式         2 bytes
  uLong dosDate;              // 文件最后修改日期时间     4 bytes
  uLong crc;                  // CRC-32校验         4 bytes
  uLong compressed_size;      // 压缩后大小          4 bytes
  uLong uncompressed_size;    // 未压缩大小          4 bytes
  uLong size_filename;        // 文件名长度          2 bytes
  uLong size_file_extra;      // 文件扩展字段长度       2 bytes
  uLong size_file_comment;    // 文件注释长度         2 bytes
 
  uLong disk_num_start;       // 磁盘开始号          2 bytes
  uLong internal_fa;          // 内部文件属性         2 bytes
  uLong external_fa;          // 外部文件属性         4 bytes
 
  tm_unz tmu_date;         // 文件创建日期
} unz_file_info;      

实际上,一般的zip文件很少使用扩展与注释。对于没有扩展与注释的,参数⑤⑦可以传入null,参数⑥⑧可以传入0。

注意获取到的文件名。若该文件名以路径分隔符”/”结尾,表明这是个文件夹;否则表明这是个文件。

判断一个文件名是文件还是文件夹,还可以利用external_fa字段。该字段的值会被填充为winnt.h中的FILE宏定义。通常,若external_fa  = 16,即0x00000010,则说明是个文件夹;若external_fa   = 32,即0x00000020,则说明是个文件。

在winnt.h中:

#define FILE_ATTRIBUTE_DIRECTORY            0x00000010  
#define FILE_ATTRIBUTE_ARCHIVE              0x00000020       

一般来说,在这里若external_fa !=FILE_ATTRIBUTE_DIRECTORY,则认为是个文件。

对于已经定位到的zip中的单个文件,就可以执行解压缩了。解压缩过程如下:

打开该定位到的文件:
int unzOpenCurrentFile(unzFile file);
返回值为执行的结果信息。

对于加密的文件,需要传入密码:
int unzOpenCurrentFilePassword(unzFile file, const char* password);

若返回值不为UNZ_OK,则解压出错,可以根据返回值判断是文件本身问题还是解压问题。

读取文件内容:
int unzReadCurrentFile(unzFile file, voidp buf, unsigned len);

传入unzFile对象,创建一个缓冲区用于存放读取的内容,将缓冲区指针和大小传入。

由于缓冲区并不一定足够大,所以需要进行多次读取。每次读取,当前的读取位置都会被修改。

例如,一个文件读取需要10M空间。创建了一个1M大小的缓冲区。第一次调用该函数读取,当前位置指向文件的开始,读了1M大小的数据,将该数据存入缓冲区中;第二继续调用该函数来读取,此时当前位置由于第一次的读取,已经指向了1M数据结尾的地方,所以第二次读取会从1M结尾开始,读取1M大小的数据……调用该函数10次,即可读取完所有的数据。

文件的读取状况,是依靠返回值来判定的:

return>0:正常读取了文件的一部分内容,返回值为读取的字节数

return==0:没有数据被读取,说明文件已经读取完毕

return<0:读取出错,返回错误码


将缓冲区内容写入到创建的文件中:
这需要借助操作系统的函数来进行。同样是要对一个文件进行多次写入。

const int BUFFER_SIZE = 4096;
char szReadBuffer[BUFFER_SIZE];
while (TRUE)
{
  memset(szReadBuffer, 0, BUFFER_SIZE); //清理缓冲区
  int nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE); //读取
 
  int result;
  if (nReadFileSize > 0) result = 1; //读取文件部分数据
  if (nReadFileSize == 0) result = 0; //读取文件结束
  if (nReadFileSize < 0) result = -1;  //读取文件失败
  switch (result)
  {
  case 1:
  {
    //读取文件部分数据
    //将其写入到创建的文件中
  }
  break;
  case 0:
  {
    //读取文件结束
    unzCloseCurrentFile(unzfile);
    return;
  }
  break;
  case -1:
  {
    //读取文件失败
    unzCloseCurrentFile(unzfile);
  }
  break;
  }

}      

当前文件解析完毕,则跳转到下一个文件:

int unzGoToNextFile(unzFile file);      

然后重复步骤上面的几个步骤

若成功,则return UNZ_OK

若失败,说明已到文件尾,则return UNZ_END_OF_LIST_OF_FILE

对zip文件解压缩完毕后,要将文件关闭:

int unzClose(unzFile file);      

对于zip中各个文件和文件夹的定位,可以使用以下函数:

int unzGoToFirstFile(unzFile file);
int unzGoToNextFile(unzFile file);
int unzLocateFile(unzFile file, const char *szFileName, int iCaseSensitivity);      

关于iCaseSensitivity参数:

iCaseSensitivity = 1,则文件名的匹配区分大小写。

iCaseSensitivity = 2,则文件名的匹配不区分大小写。

iCaseSensitivity = 0,则文件名的匹配按操作系统自行决定。Unix使用1,Windows使用2

对于zip中各个文件和文件夹的位置,可以使用索引:

typedef struct unz_file_pos_s
{
  uLong pos_in_zip_directory;   // 文件在zip文件中的偏移
  uLong num_of_file;            // 在文件中的索引
} unz_file_pos;
 
int unzGetFilePos(unzFile file, unz_file_pos* file_pos);
int unzGoToFilePos(unzFile file, unz_file_pos* file_pos);      

unzGetFilePos函数会将当前定位文件的信息填入传入的unz_file_pos指针中。

unzGoToFilePos函数会根据传入的unz_file_pos指针中的信息,将当前文件定位到指定文件。

获取zip文件的注释:

int unzGetGlobalComment(unzFile file, char *szComment, uLong uSizeBuf);
      

封装后的代码

​​UZip​​