Closing Streams in WinRT
File IO in .NET for Metro style apps (aka .NET Core) can be a challenge for seasoned .NET developers. The classes in Windows.Storage
namespace are different from both System.IO.IsolatedStorage
and System.IO
and require some getting used to. On top of that even the remaining classes in System.IO
are missing some of properties and methods.
One such method is Stream.Close()
(notice the remark at the top of the linked page). This doesn't mean that you can forget about closing or disposing streams once you're done with them, though. Try calling the following piece of code twice in a row and you'll see what I mean:
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml",
CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream outStream = randomStream.GetOutputStreamAt(0);
XmlSerializer serializer = new XmlSerializer(typeof(string));
Stream stream = outStream.AsStreamForWrite();
serializer.Serialize(stream, "Test");
await outStream.FlushAsync();
The second call to CreateFileAsync()
ends up with an UnauthorizedAccessException
: Access is denied, the reason being that the file is still being locked by the objects from the first call. To avoid that you need to call Dispose()
on either the Stream
class:
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml",
CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream outStream = randomStream.GetOutputStreamAt(0);
XmlSerializer serializer = new XmlSerializer(typeof(string));
using (Stream stream = outStream.AsStreamForWrite())
{
serializer.Serialize(stream, "Test");
await outStream.FlushAsync();
}
or the IOutputStream
interface:
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml",
CreationCollisionOption.ReplaceExisting);
IRandomAccessStream randomStream = await file.OpenAsync(FileAccessMode.ReadWrite);
using (var outStream = randomStream.GetOutputStreamAt(0))
{
XmlSerializer serializer = new XmlSerializer(typeof(string));
Stream stream = outStream.AsStreamForWrite();
serializer.Serialize(stream, "Test");
await outStream.FlushAsync();
}
For some reason calling Dispose()
only on the IRandomAccessStream
interface is NOT enough:
StorageFolder folder = ApplicationData.Current.LocalFolder;
StorageFile file = await folder.CreateFileAsync("file.xml",
CreationCollisionOption.ReplaceExisting);
using (IRandomAccessStream randomStream =
await file.OpenAsync(FileAccessMode.ReadWrite))
{
IOutputStream outStream = randomStream.GetOutputStreamAt(0);
XmlSerializer serializer = new XmlSerializer(typeof(string));
Stream stream = outStream.AsStreamForWrite();
serializer.Serialize(stream, "Test");
await outStream.FlushAsync();
}
Keep that in mind when working with streams in WinRT.