-
Notifications
You must be signed in to change notification settings - Fork 471
Description
While SaveDDSTextureToFile opens the file for writing in an exclusive mode and fails with a sharing violation if the access is denied, SaveWICTextureToFile opens files in a shared writing mode and enables multiple writers to race to write multiple screenshots to the same file.
In my project, I prototyped moving the file write to a separate thread, and only handed off the D3D surface from the main thread to the temporary writer thread. My screenshots are timestamped with a granularity of one second, so spamming enough screenshots could result in multiple attempts to write to the same file. This resulted in the following consequences:
- Multiple threads opened the same file for writing, and none of them failed.
- They produced a corrupted image.
Writing WIC textures utilizes IWICStream::InitializeFromFilename to create a stream. The following is stated in the Remarks:
The IWICStream interface methods do not enable you to provide a file sharing option.
To create a shared file stream for an image, use the SHCreateStreamOnFileEx function.
This wording would imply that the default file sharing option is exclusive, and the user should use the Shell function to create a shared stream. However, the opposite is true, at least on Windows 10 22H2:
- Opening the same filename for shared writing prior to using
InitializeFromFilenamelets it succeed with no issues. - Opening the same filename for exclusive writing prior to using
InitializeFromFilenameblocks execution inside of that function, probably waiting for the file to become available.
This raises multiple questions:
- Should ScreenGrab stop using
IWICStreamand instead useSHCreateStreamOnFileExto create an exclusive stream? The following replacement works fine and returns a sharing violation if concurrent writes are attempted:However, it also creates a dependency onComPtr<IStream> stream; hr = SHCreateStreamOnFileEx(fileName, STGM_WRITE|STGM_SHARE_EXCLUSIVE|STGM_CREATE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, stream.GetAddressOf()); if (FAILED(hr)) return hr; auto_delete_file_wic delonfail(stream, fileName);
Shlwapi.lib, which the current solution does not have. - Should the docs of
IWICStream::InitializeFromFilenamebe updated to say that the default file share mode is shared, andSHCreateStreamOnFileExshould be used for exclusive access, essentially flipping the current remark?Or perhaps the WIC stream is opened exclusively when reading, and not writing?EDIT: I checked locally, opening two WIC streams for the same file withGENERIC_READsucceeds. However, opening the first stream withGENERIC_READand the second withGENERIC_WRITEfails with a sharing violation...