使用IShellDispatch进行C ++解压缩失败

时间:2015-08-12 06:09:57

标签: c++ winapi zip unzip compression

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

我很明显看过这些帖子:

但答案建议使用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

问题

我做错了什么?

3 个答案:

答案 0 :(得分:1)

您是否尝试单步执行调试器中的代码?你有几个bug,还有一些不是bug的东西,但是有很多潜力......

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

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

wpath更改为wstring并将BSTR更改为_bstr_t即可使代码正常运行。

当然你还应该摆脱VARIANT_variant_t的青睐,当你真的需要它们时,使用_com_ptr_t而不是赤裸的指针,最好使用{ {1}}为此。

这样#import方法需要NameSpace()而不是_variant_t,而VARIANT_variant_t的隐式构造函数,所以你可以直接传递函数_bstr_t,无需摆弄_bstr_t s VARIANT

答案 1 :(得分: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 :(得分:0)

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