【问题标题】:C++ unzip using IShellDispatch fails使用 IShellDispatch 解压缩 C++ 失败
【发布时间】:2015-11-04 14:12:22
【问题描述】:

所以我有一个非常简单的工作要做:解压缩一个 zip 文件。以为我会在 5 秒内在线找到一个简单的解决方案,但我仍然在苦苦挣扎。

我显然读过这些帖子:

但答案建议使用zliblibzipminiz。 我确信这些方法效果很好。然而,在现有的 VS2013 解决方案中尝试应用这种方法似乎并不简单。

然后我遇到了这个简单的解决方案,ref1ref2,它利用了IShellDispatch object

我赶紧去实现:

bool DecompressZIP(_In_ const wpath& pathFile, _In_ const wpath& pathDstDir)
{
    BSTR source = _bstr_t(pathFile.string().c_str());
    BSTR dest = _bstr_t(pathDstDir.string().c_str());

    HRESULT hResult = S_FALSE;
    IShellDispatch *pIShellDispatch = NULL;
    Folder *pToFolder = NULL;
    VARIANT variantDir, variantFile, variantOpt;

    CoInitialize(NULL);

    hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
        IID_IShellDispatch, (void **)&pIShellDispatch);

    if (SUCCEEDED(hResult) && NULL != pIShellDispatch)
    {
        VariantInit(&variantDir);
        variantDir.vt = VT_BSTR;
        variantDir.bstrVal = dest;
        hResult = pIShellDispatch->NameSpace(variantDir, &pToFolder);

        if (SUCCEEDED(hResult) && NULL != pToFolder)
        {
            Folder *pFromFolder = NULL;
            VariantInit(&variantFile);
            variantFile.vt = VT_BSTR;
            variantFile.bstrVal = source;
            hResult = pIShellDispatch->NameSpace(variantFile, &pFromFolder);

            if (SUCCEEDED(hResult) && NULL != pFromFolder)
            {
                FolderItems *fi = NULL;
                pFromFolder->Items(&fi);

                VariantInit(&variantOpt);
                variantOpt.vt = VT_I4;
                variantOpt.lVal = FOF_NO_UI;

                VARIANT newV;
                VariantInit(&newV);
                newV.vt = VT_DISPATCH;
                newV.pdispVal = fi;
                hResult = pToFolder->CopyHere(newV, variantOpt);
                Sleep(1000);
                pFromFolder->Release();
                pToFolder->Release();
            }
        }
        pIShellDispatch->Release();
    }

    CoUninitialize();

    return true;
}

但它不起作用!

行: hResult = pIShellDispatch->NameSpace(variantFile, &pFromFolder); 总是导致pFromFolder == NULL

  • hResultS_FALSE
  • SUCCEEDED(hResult) 是真的
  • GetLastError 为 0

问题

我做错了什么?

【问题讨论】:

  • 您可以先告诉我们从函数返回的HRESULT,该函数会向您抛出一个空指针。您是否声称 SUCCEEDED(hResult)true,但 pFromFolder 是 NULL ?带有一些详细调试消息的else 条件可能证明是值得添加的。如果我必须猜测,那将是您创建的 sourcedest BSTR 值来自临时的 bstr_t 对象,这些对象在您真正使用之前早已不复存在i> 他们的“内在小孩”.. 即你有一对很好的悬空指针。
  • 这是一种冒险的做法。预计在 shell 关联损坏的机器上会出现故障。扔掉这段代码,使用真正的 zip 库。
  • Explorer 中的 Zip 支持无论如何都非常有限。例如。不支持 unicode,不支持安全加密方法,不支持大 (> 4GB) 档案。

标签: c++ winapi zip unzip compression


【解决方案1】:
bool CUnZip::Unzip2Folder(BSTR lpZipFile, BSTR lpFolder)
{
IShellDispatch *pISD;
Folder  *pZippedFile = 0L;
Folder  *pDestination = 0L;

long FilesCount = 0;
IDispatch* pItem = 0L;
FolderItems *pFilesInside = 0L;

VARIANT Options, OutFolder, InZipFile, Item;
CoInitialize(NULL);
__try{
    if (CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD) != S_OK)
        return 1;

    InZipFile.vt = VT_BSTR;
    InZipFile.bstrVal = lpZipFile;
    pISD->NameSpace(InZipFile, &pZippedFile);
    if (!pZippedFile)
    {
        pISD->Release();
        return 1;
    }

    OutFolder.vt = VT_BSTR;
    OutFolder.bstrVal = lpFolder;
    pISD->NameSpace(OutFolder, &pDestination);
    if (!pDestination)
    {
        pZippedFile->Release();
        pISD->Release();
        return 1;
    }

    pZippedFile->Items(&pFilesInside);
    if (!pFilesInside)
    {
        pDestination->Release();
        pZippedFile->Release();
        pISD->Release();
        return 1;
    }

    pFilesInside->get_Count(&FilesCount);
    if (FilesCount < 1)
    {
        pFilesInside->Release();
        pDestination->Release();
        pZippedFile->Release();
        pISD->Release();
        return 0;
    }

    pFilesInside->QueryInterface(IID_IDispatch, (void**)&pItem);

    Item.vt = VT_DISPATCH;
    Item.pdispVal = pItem;

    Options.vt = VT_I4;
    Options.lVal = 1024 | 512 | 16 | 4;//http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx

    bool retval = pDestination->CopyHere(Item, Options) == S_OK;

    pItem->Release(); pItem = 0L;
    pFilesInside->Release(); pFilesInside = 0L;
    pDestination->Release(); pDestination = 0L;
    pZippedFile->Release(); pZippedFile = 0L;
    pISD->Release(); pISD = 0L;

    return retval;

}
__finally
{
    CoUninitialize();
}

}

来源:https://social.msdn.microsoft.com/Forums/vstudio/en-US/45668d18-2840-4887-87e1-4085201f4103/visual-c-to-unzip-a-zip-file-to-a-specific-directory?forum=vclanguage

主要是调用:

CUnZip * Z = new CUnZip();
        BSTR bstrFile = strFileName.AllocSysString();
        BSTR bstrPath = m_strPathName.AllocSysString();
        Z->Unzip2Folder(bstrFile, bstrPath);
        delete Z;
        Z = NULL;

【讨论】:

    【解决方案2】:

    您是否尝试在调试器中单步执行代码?你有几个错误,还有相当多的东西还不是错误,但有很多潜力......

    首先,正如 WhozCraig 所说 - 你有两个悬空指针,两个 BSTRs。 (事实上​​,如果你设法让第一个NameSpace 调用成功,你会看到第二个BSTR 更改为某个GUID。显然,第二个调用在接受该参数时会失败。)

    其次,您使用的是wpath 而不是wstring 或类似的东西。 wpath 在路径组件之间提出斜线。但是很多 Windows 代码不能使用正斜杠,只能使用反斜杠。 LoadLibrary() 就是一个例子。实际上,Shell 中的所有内容都是另一回事(例如,请参见 this link 底部的屏幕截图)。

    wpaths 更改为wstrings 并将BSTRs 更改为_bstr_ts 足以使代码正常工作。


    当然你也应该去掉VARIANT_variant_ts,当你真的需要它们时,使用_com_ptr_ts而不是裸指针,最好使用#import

    这样NameSpace() 方法采用_variant_t 而不是VARIANT,并且_variant_t 具有来自_bstr_t 的隐式构造函数,因此您可以直接将_bstr_t 传递给函数,而无需摆弄VARIANTs _variant_ts.

    【讨论】:

      【解决方案3】:

      我遇到同样的问题,当我使用绝对路径并且路径分隔符号是'\'时,问题是slove

      【讨论】:

        猜你喜欢
        • 2020-03-11
        • 1970-01-01
        • 2021-07-18
        • 1970-01-01
        • 2011-11-12
        • 2010-11-09
        • 1970-01-01
        • 2012-08-12
        • 1970-01-01
        相关资源
        最近更新 更多