本人最近工作当中遇到了解压失败的问题,由于工作中使用C++语言,当前的解压方案是起一个进程去调用7z工具来解压,该方法有时会报错,具体原因尚未明确。于是我在想要不直接使用C++代码解压,先看了一下当前的解压第三方库,一个有名的是zlib,当看了一下解压到目录的实现,好家伙,2000行代码,吃不消,后来放弃这条路了。然后就一直在网上查找是否用简单的方式实现,最好不要依赖第三方库,那样还得引入开源备案及遵守开源协议等一些麻烦事。找了好久,终于找到了一篇博文,介绍了一个使用windows api直接解压到一个目录的代码,由于比较久远,原文链接有时间再补上。代码不多,我自己润色了一下(改了一些变量名并加了好多判断),最终实现了。
`#include <Windows.h>
include <Shldisp.h> ///Shldisp.idl
include <stdio.h>
include <locale.h>
define FreeResource(A) \
if (A != NULL) { \A->Release(); \
}
bool UnZipFolder(wchar_t* zipFile, wchar_t* dstDir)
{
bool bReturn = false;
HRESULT hResult;
IShellDispatch* pISD = NULL;
Folder* pToFolder = NULL;
Folder* pFromFolder = NULL;
FolderItems* pFolderItems = NULL;
FolderItems* pToFolderItems = NULL;
BSTR bstrZipFile = NULL;
BSTR bstrDstDir = NULL;
IDispatch* pDispatch = NULL;
VARIANT vDstDir, vZipFile, vtDispatch, vOpt;
long fromFolderCount = 0;
long toFolderCount = 0;
hResult = CoInitialize(NULL);
if (FAILED(hResult))
{printf("%d failed", __LINE__);goto FINAL;
}hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pISD);
if (FAILED(hResult) || pISD == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}bstrZipFile = SysAllocString(zipFile);
if (bstrZipFile == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}bstrDstDir = SysAllocString(dstDir);
if (bstrDstDir == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}VariantInit(&vZipFile);
vZipFile.vt = VT_BSTR;
vZipFile.bstrVal = bstrZipFile;VariantInit(&vDstDir);
vDstDir.vt = VT_BSTR;
vDstDir.bstrVal = bstrDstDir;hResult = pISD->NameSpace(vZipFile, &pFromFolder);
if (FAILED(hResult) || pFromFolder == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}hResult = pISD->NameSpace(vDstDir, &pToFolder);
if (FAILED(hResult) || pToFolder == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}hResult = pFromFolder->Items(&pFolderItems);
if (FAILED(hResult) || pFolderItems == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}pFolderItems->get_Count(&fromFolderCount);
if (fromFolderCount < 1)
{printf("%d failed", __LINE__);goto FINAL;
}pFolderItems->QueryInterface(IID_IDispatch, (void**)&pDispatch);
if (FAILED(hResult) || pDispatch == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}VariantInit(&vtDispatch);
vtDispatch.vt = VT_DISPATCH;
vtDispatch.pdispVal = pDispatch;VariantInit(&vOpt);
vOpt.vt = VT_I4;
vOpt.lVal = 16 + 4; // Do not display a progress dialog box ~ This will not work properly!printf("Extracting files ...\n");
hResult = pToFolder->CopyHere(vtDispatch, vOpt);
if (FAILED(hResult))
{printf("%d failed", __LINE__);goto FINAL;
}hResult = pToFolder->Items(&pToFolderItems);
if (FAILED(hResult) || pToFolderItems == NULL)
{printf("%d failed", __LINE__);goto FINAL;
}// 等待10000秒,看是否会超时
for (int i = 0; i < 10000; i++)
{toFolderCount = pToFolderItems->get_Count(&toFolderCount);printf("target = %d, current = %d\n", fromFolderCount, toFolderCount);if (toFolderCount >= fromFolderCount){printf("unzip succes done at time %d\n", i);bReturn = true;goto FINAL;}Sleep(1);
}
printf("unzip out time\n");
FINAL:
FreeResource(pISD);
FreeResource(pToFolder);
FreeResource(pFromFolder);
FreeResource(pFolderItems);
FreeResource(pToFolderItems);
FreeResource(pDispatch);
if (bstrZipFile) SysFreeString(bstrZipFile);
if (bstrDstDir) SysFreeString(bstrDstDir);
CoUninitialize();
return bReturn;
}
int wmain(int argc, wchar_t* argv[])
{
setlocale(LC_ALL, "");
wchar_t zip[260] = { 0 };
wchar_t dir[260] = { 0 };wcscpy(zip, L"E:\\test\\游戏.zip");
wcscpy(dir, L"E:\\test\\XXXX");
CreateDirectoryW(dir, 0);DWORD64 tk = GetTickCount64();
UnZipFolder(zip, dir);
printf("\r\nspend %llums\r\n", GetTickCount64() - tk);
system("pause");
return 0;
}`