Debug Arnaut
Debug Arnaut

Reputation: 71

UWP - Can't open file for reading if another application has it open

2018 EDIT: As indicated below and by other sources, targeting the Creator's Update or higher allows for read-only file access of a file opened for write by another process. Hooray!

I seem to have hit a brick wall when trying to develop a Windows Store app for the desktop. I'm trying to open a large (100+ MB) log file that another application has open and do real time processing on the latest events as they're written to the file.

With regular, non-sandboxed C#, this is pretty straightforward:

System.IO.FileStream stream = File.Open("LOGFILE PATH HERE", System.IO.FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

Unfortunately, in a UWP, I get "UnauthorizedAccessException" whenever I try to open a file that's in use by another app. I've tried every API in every combination I could find, but have had zero luck, so I've come here for some suggestions.

Some of what I've tried:

Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.List;
//Prompt the user to open the log file:
Windows.Storage.StorageFile logFile = await picker.PickSingleFileAsync();
picker.FileTypeFilter.Add(".txt");

//This won't work in any case, because it doesn't use the handle that the user picked,
// so the UWP sandboxing blocks it:
new FileStream(logFile.Path, FileMode.OpenOrCreate, FileAccess.Read);

//EDIT: These don't work if the file is open either, I must have made a mistake earlier
await FileIO.ReadBufferAsync(logFile);
await FileIO.ReadLinesAsync(logFile);

//These work if the file is not open by another app, but fail if another app has the file open
await logFile.OpenAsync( Windows.Storage.FileAccessMode.Read);
await logFile.OpenStreamForReadAsync();

Quick Repro:

Open a PowerShell window, and run this command to hold open "test.txt" in your home directory:

$f = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite);

Upvotes: 3

Views: 1614

Answers (2)

smaillet-ms
smaillet-ms

Reputation: 66

This is expected behavior for the Universal APIs as of the Anniversary Update. (aka RS1). The Windows.Storage.* APIs and streams use what is called a "Polite Reader" model. In this model readers can be interrupted by a writer, which generates the OPLOCK break errors. In RS1 this also means that readers are blocked if ANY open handle for write exists already.

In the Creators Update (aka RS2) some things are changing on this. As the Universal Platform evolved from the original WinRT with a single foreground app, the need to allow apps to use more traditional models arose. Thus, in RS2 we are making a few changes to help in this scenario.

  1. An Unmodified polite reader will no longer fail on open if a writer already exists. However, readers will still get oplock breaks if the writer actually writes to the file.
  2. Sharing Violations are surfaced directly to the caller instead of being translated into AccessDenied. (For compatibility, this new behavior is gated on the calling app declaring RS2 as the tested platform in the apps’ manifest)
  3. There are new StorageOpenOptions available so that apps can change their code to use the new options to get behavior that doesn’t involve the oplocks, effectively opting out of the OpLock behavior.

Upvotes: 3

Romasz
Romasz

Reputation: 29792

I've made a simple test and it should work. The test goes like this: - open a file.txt with Notepad, the file contains only one line of text, - run the app with code below, - pick a file which is still opened in Notepad, - you should see in debug output the first line and empty second.

The code:

public async Task GetFile()
{
    Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.List;
    picker.FileTypeFilter.Add(".txt");
    //Prompt the user to open the log file:
    Windows.Storage.StorageFile logFile = await picker.PickSingleFileAsync();

    try
    {
        using (var stream = await logFile.OpenStreamForReadAsync())
        using (var reader = new StreamReader(stream))
        {
            var line = await reader.ReadLineAsync();
            Debug.WriteLine($"The first line: {line} - waiting");
            await Task.Delay(10000);
            line = await reader.ReadLineAsync();
            Debug.WriteLine($"The next line: {line} - waiting");
        }
    }
    catch (Exception exc)
    {
        Debug.WriteLine($"Exception {exc.Message}");
    }
}

In the second test I have modified the file in Notepad and saved it, while the code above hits await Task.Delay(), then when trying to read the second line, you will likely get: 'Exception The handle with which this oplock was associated has been closed. The oplock is now broken.'.

I see that you are not disposing the streams, maybe the problem is here? Have you tried to use using for Idisposable?

Upvotes: 1

Related Questions