【问题标题】:C++ - Windows Shell API - How to write/modify the content of a fileC++ - Windows Shell API - 如何编写/修改文件的内容
【发布时间】:2022-08-10 01:00:29
【问题描述】:

考虑以下代码:

bool OpenStream(const std::wstring& fileName)
{
    PIDLIST_ABSOLUTE pidl = nullptr;

    if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
        return false;

    wchar_t buffer[MAX_PATH + 1];

    if (::SHGetPathFromIDListW(pidl, buffer))
    {
        ::OutputDebugString(L\"File IDL path: \");
        ::OutputDebugString(buffer);
        ::OutputDebugString(L\"\\r\\n\");
    }

    IShellFolder* pShellfolder = nullptr;
    LPCITEMIDLIST pidlRelative = nullptr;

    HRESULT hr = ::SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);

    if (FAILED(hr))
    {
        ::CoTaskMemFree(pidl);
        return false;
    }

    if (::SHGetPathFromIDListW(pidl, buffer))
    {
        ::OutputDebugString(L\"Relative IDL path: \");
        ::OutputDebugString(buffer);
        ::OutputDebugString(L\"\\r\\n\");
    }

    IStream* pStream = nullptr;

    //if (FAILED(pShellfolder->BindToObject(pidlRelative, NULL, IID_IStream, (void**)&pStream)))
    if (FAILED(pShellfolder->BindToStorage(pidlRelative, NULL, IID_IStream, (void**)&pStream)))
    {
        pShellfolder->Release();
        ::CoTaskMemFree(pidl);
        return false;
    }

    ULARGE_INTEGER size;
    ::IStream_Size(pStream, &size);

    LARGE_INTEGER pos = {0};

    pStream->Seek(pos, STREAM_SEEK_SET, nullptr);

    unsigned char* pBuffer = new unsigned char[size.QuadPart];

    ULONG actualRead;
    hr = pStream->Read(pBuffer, size.QuadPart, &actualRead);

    std::FILE* pFile;
    fopen_s(&pFile, \"__Copy.bin\", \"wb\");

    if (!pFile)
    {
        delete[] pBuffer;
        pShellfolder->Release();
        ::CoTaskMemFree(pidl);
        return false;
    }

    const std::size_t writeCount = std::fwrite(pBuffer, sizeof(unsigned char), size.QuadPart, pFile);

    std::fclose(pFile);
    delete[] pBuffer;

    pStream->Seek(pos, STREAM_SEEK_SET, nullptr);

    hr = pStream->Write(\"Test-test-test-test\", 19, nullptr);

    pShellfolder->Release();
    ::CoTaskMemFree(pidl);

    return true;
}

此代码在流中打开传入fileName 的文件并将其内容写入新文件,使用std 来实现。直到这里一切正常。

但是,作为最后的操作,我想修改打开文件的内容。但是我不能用上面的代码做到这一点,它确实可以编译和运行,但它什么也不做,我收到一个拒绝访问结果出错。

我应该如何修改上面的代码以允许打开的流读取和写在我的档案里?

另外,作为一个附带问题:上面的代码是否安全且编写良好(即它会产生内存泄漏,还是其中不安全)?欢迎进行详细审查。

    标签: windows winapi windows-shell


    【解决方案1】:

    您的代码不起作用,因为您以只读方式隐式打开流。要打开它进行写操作,您必须传递一个绑定上下文,如下所示:

    IBindCtx* ctx;
    CreateBindCtx(0, &ctx);
    
    BIND_OPTS options = {};
    options.cbStruct = sizeof(BIND_OPTS);
    options.grfMode = STGM_WRITE;
    ctx->SetBindOptions(&options);
    
    ...
    pShellfolder->BindToStorage(pidlRelative, ctx, IID_IStream, (void**)&pStream);
    ...
    

    否则,当您使用 COM 对象时,您应该使用智能指针来避免 COM 引用泄漏。例如,下面,我使用 Visual Studio 附带的 ATL,但也有 WRL 和 C++/WinRT(也可以做一些基本的 COM 东西)

    此外,对于 Shell 对象,使用 IShellItem 和朋友以及所有(“Item”)API 会更容易,就像这样(此处省略了错误检查,但您应该测试每个 HRESULT 是否有错误):

    CComPtr<IShellItem> item;
    SHCreateItemFromParsingName(fileName.c_str(), nullptr, IID_PPV_ARGS(&item));
    
    CComPtr<IBindCtx> ctx;
    CreateBindCtx(0, &ctx);
    
    BIND_OPTS options = {};
    options.cbStruct = sizeof(BIND_OPTS);
    options.grfMode = STGM_WRITE;
    ctx->SetBindOptions(&options);
    
    CComPtr<IStream> stream;
    item->BindToHandler(ctx, BHID_Stream, IID_PPV_ARGS(&stream));
    
    stream->Write("Test-test-test-test", 19, nullptr);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-24
      • 2010-12-09
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 2020-12-08
      • 2013-05-27
      相关资源
      最近更新 更多