使用windows api解压zip文件(不依赖第三方工具和库)

  1. 背景
    本人最近工作当中遇到了解压失败的问题,由于工作中使用C++语言,当前的解压方案是起一个进程去调用7z工具来解压,该方法有时会报错,具体原因尚未明确。于是我在想要不直接使用C++代码解压,先看了一下当前的解压第三方库,一个有名的是zlib,当看了一下解压到目录的实现,好家伙,2000行代码,吃不消,后来放弃这条路了。然后就一直在网上查找是否用简单的方式实现,最好不要依赖第三方库,那样还得引入开源备案及遵守开源协议等一些麻烦事。找了好久,终于找到了一篇博文,介绍了一个使用windows api直接解压到一个目录的代码,由于比较久远,原文链接有时间再补上。代码不多,我自己润色了一下(改了一些变量名并加了好多判断),最终实现了。笔者亲测有效,核心代码100行左右
  2. 代码实现
    注:以下代码需用VS编译!!!
点击查看代码
#include <Windows.h>
#include <Shldisp.h>
#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* pFromFolder = NULL;
    FolderItems* pFromFolderItems = NULL;
    Folder* pToFolder = NULL;
    IDispatch* pDispatch = NULL;
    BSTR bstrZipFile = NULL;
    BSTR bstrDstDir = NULL;
    VARIANT vDstDir, vZipFile, vtDispatch, vOpt;
    long fromFolderCount = 0;

    hResult = CoInitialize(NULL);
    if (FAILED(hResult)) {
        goto FINAL;
    }

    hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pISD);
    if (FAILED(hResult) || pISD == NULL) {
        goto FINAL;
    }

    bstrZipFile = SysAllocString(zipFile);
    if (bstrZipFile == NULL) {
        goto FINAL;
    }

    bstrDstDir = SysAllocString(dstDir);
    if (bstrDstDir == NULL) {
        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) {
        goto FINAL;
    }

    hResult = pISD->NameSpace(vDstDir, &pToFolder);
    if (FAILED(hResult) || pToFolder == NULL) {
        goto FINAL;
    }
    
    hResult = pFromFolder->Items(&pFromFolderItems);
    if (FAILED(hResult) || pFromFolderItems == NULL) {
        goto FINAL;
    }

    pFromFolderItems->get_Count(&fromFolderCount);
    if (fromFolderCount < 1) {
        goto FINAL;
    }

    pFromFolderItems->QueryInterface(IID_IDispatch, (void**)&pDispatch);
    if (FAILED(hResult) || pDispatch == NULL) {
        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)) {
        goto FINAL;
    }
  
    printf("unzip success\n");
    bReturn = true;

FINAL:
    FreeResource(pISD);
    FreeResource(pFromFolder);
    FreeResource(pFromFolderItems);
    FreeResource(pToFolder);
    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 zipFile[260] = { 0 };
    wchar_t dstDir[260] = { 0 };
    
    wcscpy(zipFile, L"E:\\test\\游戏.zip");
    wcscpy(dstDir, L"E:\\test\\XXXX");
    CreateDirectoryW(dstDir, 0);

    UnZipFolder(zipFile, dstDir);
    return 0;
}
posted @ 2024-09-17 01:05  肉肉的男朋友  阅读(284)  评论(0)    收藏  举报