首先 GitHub 上下载 zlib的发行版本进行编译;
如果出现 zlib报“LNK2001:无法解析的外部符号”错误 的情况,是因为编译zlib(dll)的工程默认有预处理器定义ZLIB_WINAPI,ZEXPORT在zconf.h中的定义为WINAPI:
/* If building or using zlib with the WINAPI/WINAPIV calling convention,
* define ZLIB_WINAPI.
* Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
*/
# ifdef ZLIB_WINAPI
# ifdef FAR
# undef FAR
# endif
# include <windows.h>
/* No need for _export, use ZLIB.DEF instead. */
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
# define ZEXPORT WINAPI
# ifdef WIN32
# define ZEXPORTVA WINAPIV
# else
# define ZEXPORTVA FAR CDECL
# endif
# endif
#define WINAPI __stdcall所以,zlib(dll)的导出函数调用约定为__stdcall,和zlib(dll)的导入函数调用约定__cdecl不一致。
针对zlib编译dll文件编译出错,有以下两种解决办法:
1. 在zlivc工程中去掉预处理器定义ZLIB_WINAPI(预处理定义:vs工程属性->C/C++->预处理器)
去掉该定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:
#ifndef ZEXPORT
# define ZEXPORT
#endif// 所以导出函数unzGoToNextFile
extern int ZEXPORT unzGoToNextFile (unzFile file)
// 等价于
extern int unzGoToNextFile (unzFile file)
// 也等价于(vs默认是__cdecl函数调用约定)
extern int __cdecl unzGoToNextFile (unzFile file)所以在新的工程中导入zlib相关的库文件和dll文件后,编译dll的工程和引入dll的工程的函数调用约定均为__cdecl方式,导出函数能够正确解析了,编译自然就通过了。由上宏定义注释可知,标准编译ZLIB1.DLL是没有ZLIB_WINAPI预处理定义的,函数调用约定也是__cdecl,所以这里推荐该解决方案。
2. 在引入zlib工程中添加预处理器定义ZLIB_WINAPI
在引入dll工程中添加ZLIB_WINAPI定义后,宏定义ZEXPORT在zconf.h文件中实际定义如下:
#define ZEXPORT WINAPI
#define WINAPI __stdcall与导出zlib的dll工程均为__stdcall函数调用约定,编译也能顺利通过。
zlibHelper.h
#pragma once
#include <iostream>
#include <string>
#include "zip.h"
#include "unzip.h"
#include "zlib.h"
class CZlibHelper
{
public:
CZlibHelper();
~CZlibHelper();
//主要的压缩函数
//将文件添加到zip文件,若zip文件已存在,则覆盖该文件
static bool CreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
//将文件添加到zip文件,若该zip文件不存在则创建一个zip文件
static bool CreateZipfromDir2(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName);
//主要的解压函数
static bool UnzipFile(const std::string& strFilePath, const std::string& strTempPath);
private:
//添加文件和文件夹到
static bool AddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile);
static bool CollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName);
//字符串 字符替换功能函数
static std::string& replace_all(std::string& str, const std::string& old_value, const std::string& new_value);
//创建多级目录
static bool CreatedMultipleDirectory(const std::string& direct);
};
zlibHelper.cpp
#include "stdafx.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <Shlwapi.h>
//#include "zip.h"
//#include "unzip.h"
//#include "zlib.h"
#include "zlibHelper.h"
using namespace std;
#pragma comment(lib, "Shlwapi.lib")
CZlibHelper::CZlibHelper()
{
}
CZlibHelper::~CZlibHelper()
{
}
/*
* 函 数 :AddfiletoZip
* 函数功能 :添加文件到要打包的zipFile对象的文件里
* 参数备注 :参数zfile 表示zipFile文件对象
* 参数fileNameinZip 表示要压缩的文件路径
* 参数srcfile 表示要压缩的文件(为空表示为空目录)
*/
bool CZlibHelper::AddfiletoZip(zipFile zfile, const std::string& fileNameinZip, const std::string& srcfile)
{
if (NULL == zfile || fileNameinZip.empty()/* || srcfile.empty()为空代表空目录*/)
{
OutputDebugString("ZKTeco-AddfiletoZip--压缩路径为空");
return 0;
}
int nErr = 0;
zip_fileinfo zinfo = { 0 };
tm_zip tmz = { 0 };
zinfo.tmz_date = tmz;
zinfo.dosDate = 0;
zinfo.internal_fa = 0;
zinfo.external_fa = 0;
char sznewfileName[MAX_PATH] = { 0 };
memset(sznewfileName, 0x00, sizeof(sznewfileName));
strcat_s(sznewfileName, fileNameinZip.c_str());
if (srcfile.empty())
{
strcat_s(sznewfileName, "\\");
}
nErr = zipOpenNewFileInZip(zfile, sznewfileName, &zinfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
if (nErr != ZIP_OK)
{
std::string strLog = "ZKTeco-AddfiletoZip--zipOpenNewFileInZip failed! error = ";
strLog += std::to_string(nErr);
OutputDebugString(strLog.c_str());
return false;
}
if (!srcfile.empty())
{
//打开源文件
FILE* srcfp = _fsopen(srcfile.c_str(), "rb", _SH_DENYNO);
if (NULL == srcfp)
{
//std::cout << "Open source file failed." << std::endl;
std::string strLog = "ZKTeco-AddfiletoZip--Open source file failed. error = ";
int nErr = GetLastError();
strLog += std::to_string(nErr);
OutputDebugString(strLog.c_str());
return false;
}
//读入源文件写入zip文件
int numBytes = 0;
char* pBuf = new char[1024 * 100];
if (NULL == pBuf)
{
//std::cout << "new buffer failed." << std::endl;
std::string strLog = "ZKTeco-AddfiletoZip--New buffer failed. error = ";
int nErr = GetLastError();
strLog += std::to_string(nErr);
OutputDebugString(strLog.c_str());
return 0;
}
while (!feof(srcfp))
{
memset(pBuf, 0x00, sizeof(pBuf));
numBytes = fread(pBuf, 1, sizeof(pBuf), srcfp);
nErr = zipWriteInFileInZip(zfile, pBuf, numBytes);
if (ferror(srcfp))
{
break;
}
}
if (pBuf)
{
delete[] pBuf;
pBuf = NULL;
}
if (srcfp)
{
fclose(srcfp);
srcfp = NULL;
}
}
zipCloseFileInZip(zfile);
return true;
}
/*
* 函 数 :CollectfileInDirtoZip
* 函数功能 :添加文件到要打包的zipFile对象的文件里
* 参数备注 :参数zfile 表示zipFile文件对象
* 参数fileNameinZip 表示要压缩的文件路径
* 参数srcfile 表示要压缩的文件(为空表示为空目录)
*/
bool CZlibHelper::CollectfileInDirtoZip(zipFile zfile, const std::string& filepath, const std::string& parentdirName)
{
if (NULL == zfile || filepath.empty())
{
printf("zfile or filepath are null.");
OutputDebugString("ZKTeco-CollectfileInDirtoZip--zfile or filepath are null.");
return false;
}
bool bFile = false;
std::string relativepath = "";
WIN32_FIND_DATAA findFileData;
memset(&findFileData, 0x0, sizeof(WIN32_FIND_DATAA));
char szpath[MAX_PATH] = { 0 };
if (::PathIsDirectoryA(filepath.c_str()))
{
strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
int len = strlen(szpath) + strlen("\\*.*") + 1;
strcat_s(szpath, len, "\\*.*");
}
else
{
bFile = true;
strcpy_s(szpath, sizeof(szpath) / sizeof(szpath[0]), filepath.c_str());
}
HANDLE hFile = ::FindFirstFileA(szpath, &findFileData);
if (NULL == hFile)
{
printf("FindFirstFile failed.");
std::string strLog = "ZKTeco-CollectfileInDirtoZip--FindFirstFile failed. error = ";
int nErr = GetLastError();
strLog += std::to_string(nErr);
OutputDebugString(strLog.c_str());
return false;
}
do
{
if (parentdirName.empty())
{
relativepath = findFileData.cFileName;
}
else
{
relativepath = parentdirName + "\\" + findFileData.cFileName;//生成zip文件中的相对路径
}
if (findFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY)
{
//过滤文件夹
if (strcmp(findFileData.cFileName, ".") != 0 && strcmp(findFileData.cFileName, "..") != 0)
{
AddfiletoZip(zfile, relativepath, "");
char szTemp[MAX_PATH] = { 0 };
strcpy_s(szTemp, filepath.c_str());
strcat_s(szTemp, "\\");
strcat_s(szTemp, findFileData.cFileName);
CollectfileInDirtoZip(zfile, szTemp, relativepath);
}
continue;
}
char szTemp[MAX_PATH] = { 0 };
if (bFile)
{
//注意:处理单独文件的压缩
strcpy_s(szTemp, filepath.c_str());
}
else
{
//注意:处理目录文件的压缩
strcpy_s(szTemp, filepath.c_str());
strcat_s(szTemp, "\\");
strcat_s(szTemp, findFileData.cFileName);
}
AddfiletoZip(zfile, relativepath, szTemp);
} while (::FindNextFileA(hFile, &findFileData));
FindClose(hFile);
return true;
}
/*
* 压缩函数 :CreateZipfromDir
* 函数功能 :压缩zip、rar文件
* 参数备注 :参数dirpathName 表示要压缩的文件路径
* 参数zipfileName 表示要压缩的文件名称
* 参数parentdirName 表示要压缩的文件创建父目录(为空代表空目录,不创建父目录)
*/
bool CZlibHelper::CreateZipfromDir(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
bool bRet = false;
/* zipOpen 参数注释
* APPEND_STATUS_CREATE 创建追加
* APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式)
* APPEND_STATUS_ADDINZIP 直接追加
*/
zipFile zFile = NULL;
if (!::PathFileExistsA(zipfileName.c_str()))
{
zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
}
else
{
//zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATEAFTER);
}
if (NULL == zFile)
{
std::cout << "create zip file failed." << std::endl;
OutputDebugString("ZKTeco-CreateZipfromDir--zipOpen failed.");
return bRet;
}
if (CollectfileInDirtoZip(zFile, dirpathName, parentdirName))
{
bRet = true;
}
zipClose(zFile, NULL);
return bRet;
}
/*
* 压缩函数 :CreateZipfromDir
* 函数功能 :压缩zip、rar文件
* 参数备注 :参数dirpathName 表示要压缩的文件路径
* 参数zipfileName 表示要压缩的文件名称
* 参数parentdirName 表示要压缩的文件创建父目录(为空代表空目录,不创建父目录)
*/
bool CZlibHelper::CreateZipfromDir2(const std::string& dirpathName, const std::string& zipfileName, const std::string& parentdirName)
{
bool bRet = false;
/* zipOpen 参数注释
* APPEND_STATUS_CREATE 创建追加
* APPEND_STATUS_CREATEAFTER 创建后追加(覆盖方式)
* APPEND_STATUS_ADDINZIP 直接追加
*/
zipFile zFile = NULL;
if (!::PathFileExistsA(zipfileName.c_str()))
{
zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATE);
}
else
{
zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_ADDINZIP);
//zFile = zipOpen(zipfileName.c_str(), APPEND_STATUS_CREATEAFTER);
}
if (NULL == zFile)
{
//std::cout << "create zip file failed." << std::endl;
OutputDebugString("ZKTeco-CreateZipfromDir--zipOpen failed.");
return bRet;
}
if (CollectfileInDirtoZip(zFile, dirpathName, parentdirName))
{
bRet = true;
}
zipClose(zFile, NULL);
return bRet;
}
bool CZlibHelper::UnzipFile(const std::string& strFilePath, const std::string& strTempPath)
{
int nReturnValue;
string tempFilePath;
string srcFilePath(strFilePath);
string destFilePath;
std::cout << "Start unpacking the package... " << endl;
if (!::PathFileExistsA(strTempPath.c_str()))
{
CreatedMultipleDirectory(strTempPath);
}
//打开zip文件
unzFile unzfile = unzOpen(srcFilePath.c_str());
if (unzfile == NULL)
{
printf("unzOpen failed.");
OutputDebugString("ZKTeco-UnzipFile--unzOpen failed.");
return false;
}
//获取zip文件的信息
unz_global_info* pGlobalInfo = new unz_global_info;
nReturnValue = unzGetGlobalInfo(unzfile, pGlobalInfo);
if (nReturnValue != UNZ_OK)
{
printf("unzGetGlobalInfo failed.");
//std::cout << "The number of compressed package files is " << pGlobalInfo->number_entry << endl;
std::string strLog = "ZKTeco-UnzipFile--unzGetGlobalInfo failed. error = ";
strLog += std::to_string(nReturnValue);
OutputDebugString(strLog.c_str());
return false;
}
//解析zip文件
unz_file_info* pFileInfo = new unz_file_info;
char szZipFName[MAX_PATH] = { 0 };
char szExtraName[MAX_PATH] = { 0 };
char szCommName[MAX_PATH] = { 0 };
//存放从zip中解析出来的内部文件名
for (int i = 0; i < (int)pGlobalInfo->number_entry; i++)
{
//解析得到zip中的文件信息
nReturnValue = unzGetCurrentFileInfo(unzfile, pFileInfo, szZipFName, MAX_PATH, szExtraName, MAX_PATH, szCommName, MAX_PATH);
if (nReturnValue != UNZ_OK)
{
printf("unzGetCurrentFileInfo failed.");
std::string strLog = "ZKTeco-UnzipFile--unzGetCurrentFileInfo failed. error = ";
strLog += std::to_string(nReturnValue);
OutputDebugString(strLog.c_str());
return false;
}
std::cout << "ZipName: " << szZipFName << " Extra: " << szExtraName << " Comm: " << szCommName << endl;
string strZipFName = szZipFName;
if (pFileInfo->external_fa == FILE_ATTRIBUTE_DIRECTORY || (strZipFName.rfind('/') == strZipFName.length() - 1))
{
destFilePath = strTempPath + "//" + szZipFName;
CreateDirectoryA(destFilePath.c_str(), NULL);
}
else
{
//创建文件
string strFullFilePath;
tempFilePath = strTempPath + "\\" + szZipFName;
strFullFilePath = tempFilePath;//保存完整路径
int nPos = tempFilePath.rfind("/");
int nPosRev = tempFilePath.rfind("\\");
if (nPosRev == string::npos && nPos == string::npos)
{
continue;
}
size_t nSplitPos = nPos > nPosRev ? nPos : nPosRev;
destFilePath = tempFilePath.substr(0, nSplitPos + 1);
if (!PathIsDirectoryA(destFilePath.c_str()))
{
//将路径格式统一
destFilePath = replace_all(destFilePath, "/", "\\");
//创建多级目录
int bRet = CreatedMultipleDirectory(destFilePath);
}
strFullFilePath = replace_all(strFullFilePath, "/", "\\");
HANDLE hFile = CreateFileA(strFullFilePath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
int nErr = GetLastError();
if (!::PathFileExistsA(strFullFilePath.c_str()))//如果目录已经存在,则跳过,不在重新创建
{
return false;
}
}
//打开文件
nReturnValue = unzOpenCurrentFile(unzfile);
if (nReturnValue != UNZ_OK)
{
CloseHandle(hFile);
printf("unzOpenCurrentFile failed.");
std::string strLog = "ZKTeco-UnzipFile--unzOpenCurrentFile failed. error = ";
strLog += std::to_string(nReturnValue);
OutputDebugString(strLog.c_str());
return false;
}
//读取文件
uLong BUFFER_SIZE = pFileInfo->uncompressed_size;;
void* szReadBuffer = NULL;
szReadBuffer = (char*)malloc(BUFFER_SIZE);
if (NULL == szReadBuffer)
{
break;
}
while (TRUE)
{
memset(szReadBuffer, 0, BUFFER_SIZE);
int nReadFileSize = 0;
nReadFileSize = unzReadCurrentFile(unzfile, szReadBuffer, BUFFER_SIZE);
if (nReadFileSize < 0) //读取文件失败
{
unzCloseCurrentFile(unzfile);
CloseHandle(hFile);
printf("读取文件失败");
break;
}
else if (nReadFileSize == 0) //读取文件完毕
{
unzCloseCurrentFile(unzfile);
CloseHandle(hFile);
printf("读取文件完毕");
break;
}
else //写入读取的内容
{
DWORD dWrite = 0;
BOOL bWriteSuccessed = WriteFile(hFile, szReadBuffer, BUFFER_SIZE, &dWrite, NULL);
if (!bWriteSuccessed)
{
unzCloseCurrentFile(unzfile);
CloseHandle(hFile);
break;
}
}
}
free(szReadBuffer);
}
unzGoToNextFile(unzfile);
}
if (pFileInfo)
{
delete pFileInfo;
pFileInfo = NULL;
}
if (pGlobalInfo)
{
delete pGlobalInfo;
pGlobalInfo = NULL;
}
//关闭
if (unzfile)
{
unzClose(unzfile);
}
std::cout << "End unpacking the package... " << endl;
return true;
}
std::string& CZlibHelper::replace_all(std::string& str, const std::string& old_value, const std::string& new_value)
{
while (true)
{
std::string::size_type pos(0);
if ((pos = str.find(old_value)) != std::string::npos)
str.replace(pos, old_value.length(), new_value);
else
break;
}
return str;
}
//创建多级目录
bool CZlibHelper::CreatedMultipleDirectory(const std::string& direct)
{
std::string Directoryname = direct;
if (Directoryname[Directoryname.length() - 1] != '\\')
{
Directoryname.append(1, '\\');
}
std::vector<std::string> vpath;
std::string strtemp;
bool bSuccess = false;
for (int i = 0; i < (int)Directoryname.length(); i++)
{
if (Directoryname[i] != '\\')
{
strtemp.append(1, Directoryname[i]);
}
else
{
vpath.push_back(strtemp);
strtemp.append(1, '\\');
}
}
std::vector< std::string>::iterator vIter = vpath.begin();
for (; vIter != vpath.end(); vIter++)
{
//CreateDirectory 创建一个新目录
//第一个参数值为文件夹名称,第二个参数值为安全属性,一般设置为NULL即可。
//如果正确创建,返回值为1,如果没有正常创建文件夹,则返回0。
bSuccess = CreateDirectoryA(vIter->c_str(), NULL) ? true : false;
}
return bSuccess;
}
用法测试
#include "stdafx.h"
#include <iostream>
#include <string>
#include "zlibHelper.h"
int _tmain(int argc, _TCHAR* argv[])
{
#if 1
std::string dirpath = "C:\\Users\\Administrator\\Desktop\\zlib"; //源文件/文件夹
std::string zipfileName = "C:\\Users\\Administrator\\Desktop\\zlib1.zip"; //目的压缩包
//CZlibHelper::CreateZipfromDir(dirpath, zipfileName, "zlib2");
CZlibHelper::CreateZipfromDir(dirpath, zipfileName, "");
#endif
std::string srcFilePath = "C:\\Users\\Administrator\\Desktop\\zlib1.zip";
std::string tempdir = "C:\\Users\\Administrator\\Desktop\\zlib2";
CZlibHelper::UnzipFile(srcFilePath, tempdir);
system("pause");
return 0;
}
版权声明:本文为hallyz945原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。