天天看點

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​​