Skip to content

ScreenGrab opens the file in a shared writing mode #661

@CookiePLMonster

Description

@CookiePLMonster

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:

  1. Multiple threads opened the same file for writing, and none of them failed.
  2. 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:

  1. Opening the same filename for shared writing prior to using InitializeFromFilename lets it succeed with no issues.
  2. Opening the same filename for exclusive writing prior to using InitializeFromFilename blocks execution inside of that function, probably waiting for the file to become available.

This raises multiple questions:

  1. Should ScreenGrab stop using IWICStream and instead use SHCreateStreamOnFileEx to create an exclusive stream? The following replacement works fine and returns a sharing violation if concurrent writes are attempted:
     ComPtr<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);
    However, it also creates a dependency on Shlwapi.lib, which the current solution does not have.
  2. Should the docs of IWICStream::InitializeFromFilename be updated to say that the default file share mode is shared, and SHCreateStreamOnFileEx should 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 with GENERIC_READ succeeds. However, opening the first stream with GENERIC_READ and the second with GENERIC_WRITE fails with a sharing violation...

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions