Reputation: 1943
Consider the following code:
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;
}
This code opens the file passed in fileName
in a stream and write its content in a new file, using the std to achieve that. Until here all works fine.
However, as a last operation, I want to modify the content of the opened file. However I cannot do that with the above code, indeed it compiles and runs, but it does nothing, and I receive an ACCESS_DENIED error as a result.
How should I modify the above code to allow the opened stream to read AND WRITE in my file?
Also, as a side question: Is the above code safe and well written (i.e will it generates memory leaks, or is something unsafe in it)? A detailed review would be welcome.
Upvotes: 0
Views: 325
Reputation: 138896
Your code doesn't work because you're implicitly opening the stream as read-only. To open it for write operations, you must pass a binding context, something like this:
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);
...
Otherwise, when you work with COM objects, you should smart pointers to avoid COM reference leaks. For example, below, I use ATL that comes with Visual Studio, but there's also WRL and C++/WinRT (which can do some basic COM stuff too)
Also, for Shell objects it's much easier to work with IShellItem and friends and all ("Item") APIs that come with it, like this (error checks omitted here but you should test every single HRESULT for error) and avoid the old IShellFolder
:
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);
Upvotes: 1