Since Windows 8 I fell in love with SkyDrive and use it all the time now. Windows 8.1 has improved this a lot by introducing a new concept that requires some changes to the way you interact with files. In, this post Gaye Oncul Kok, Program Manager on the .NET Framework team, explains everything a .NET developer needs to know.
SkyDrive for Windows 8.1 introduced a new technology, called smart files, which gives access to the files in the cloud by providing their content on demand. The technology was designed to minimize the disk space utilization on your Windows 8.1 device. You can think of the smart files as the avatars of your cloud files on a device. They have the same appearance as regular files, allowing you to browse, search and do common file operations like viewing the properties or a thumbnail of the file without downloading the full content locally. When you want to open the file, or explicitly want to make it available offline, only then are the file’s contents streamed to your device.
The following image shows the properties of a smart file. You can see that this file uses 40KB space on the device even though its full size is 3.95MB in the cloud.
SkyDrive's placholder files are smaller
From a .NET developers’ perspective, if you are developing a Windows Store App or a desktop app targeting platforms that has Windows Runtime support, your app can consume smart files just like regular files by using Windows.Storage APIs. However, applications that depend on .NET Framework System.IO APIs, such as File.Open() or FileInfo.Open(), will have problems, when operating on a smart file unless its content is fully downloaded on the device. This is due to the fact that the smart files are supported in the Windows Shell layer and above, whereas System.IO APIs resides on the Win32 layer.
If you want your desktop app to run on non-Windows Runtime platforms, such as Windows 7, and also work with SkyDrive smart files available with Windows 8.1 you can either use the related Shell APIs through COM-interop and platform invocations or provide two different implementations and distribute two versions of your app; one for Windows Runtime and the other for non-Windows Runtime platforms. There is a third option, which is provided in sample code. This code uses reflection to utilize Windows.Storage APIs, such as StorageFile.GetFileFromPathAsync and StorageFile.OpenAsync(), if the Windows Runtime types are available on the platform or falls back to System.IO APIs otherwise.
The code provides the following public static methods in SmartFileLightUp
class:
public static Task OpenReadAsync(string filePath)
public static Task OpenWriteAsync(string filePath)
public static Task OpenWriteAsync(string filePath, bool createFile)
public static Stream OpenRead(string filePath)
public static Stream OpenWrite(string filePath)
All of these eventually call into the private async method:
private static Task OpenStreamAsync (string filePath, FileAccess mode)
In a nutshell, OpenStreamAsync
calls StorageFile.GetFileFromPathAsync to get a StorageFile object as and then calls StorageFile.OpenAsync()
to open a random-access stream over that file. The returned random-access stream is converted to System.IO.Stream
so that you can continue using the System.IO
APIs once it is open. These operations performed when the application is running on a platform where the required Windows.Storage
types exist. Otherwise, OpenStreamAsync
calls File.Open()
to open the stream.
SmartFileLightUp
class includes OpenRead
and OpenWrite
in addition to their asynchronous counterparts so that you can use them just as a replacement for File.OpenRead() and File.OpenWrite(). Both async and sync versions do not wait for the full content of the smart file to be downloaded. The difference is that Sync waits for the calls to WinRT functions to return but the Async does not.
Let’s look at an example usage. The code below simply opens a file for read and then writes its content to the console. You can call SmartFileLightUp.OpenRead()
instead of File.OpenRead
and it will work just fine with both regular files and smart files:
public void PrintFileContent(string path)
{
//using(Stream s = File.OpenRead(path))
using (Stream s = SmartFileLightUp.OpenRead(path))
{
byte[] b = new byte[1024];
UTF8Encoding temp = new UTF8Encoding(true);
while (s.Read(b, 0, b.Length) > 0)
{
Console.WriteLine(temp.GetString(b));
}
}
Another example is the code below which uses SmartFileLightUp.OpenWrite()
to open the stream for a given file path and then appends the text given as parameter to the end of the file:
public async void WriteToStream(string path, string text)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException("path");
using (Stream s = SmartFileLightUp.OpenWrite(path))
{
s.Seek(0, SeekOrigin.End);
UnicodeEncoding uniencoding = new UnicodeEncoding();
byte[] result = uniencoding.GetBytes(text);
await s.WriteAsync(result, 0, result.Length);
}
}
We provided the synchronous versions of each method in case you want the minimum code change for some legacy code; however we recommend using asynchronous version OpenReadAsync
, and OpenWriteAsync
directly. Below is an example code from a Windows Forms Application, which reads the wallpaper image from a path for displaying it in a PictureBox
:
private async void button_Click(object sender, EventArgs e)
{
string path = InitializeWallpaperPath();
var s = await SmartFileLightUp.OpenReadAsync(path);
var img = Image.FromStream(s);
pictureBox1.Image = img;
}
OpenWriteAsync(string filePath, bool createFile)
is an overload that we provided to for creating the file before opening if it does not already exist. Calling this method with createFile = false
is equivalent to calling OpenWriteAsync(string filePath)
.
Note that by using Windows Runtime APIs, you are accessing the smart files in a way that would not block the caller to wait for the full file content to be downloaded, which could take a long time if the file is large. Instead, sections of a smart file will be progressively downloaded and cached as they’re needed by the application reading the file.
It’s also important to mention that this sample code does not differentiate between smart files and normal files; for platforms that support Windows Runtime all files will be opened via Windows Runtime APIs. Also, the code does not use reflection every time to get the Type
and MethodInfo
objects for invoking. The first time these methods are retrieved, their delegates are cached so you will be invoking the delegates after the first call. This code also caches whether or not the WinRT types are available on the OS platform to minimize use of reflection on previous versions of Windows: After the first call, it will just fall back to System.IO.File.Open
when running on non-WinRT platforms.
Since the calls to Windows Runtime APIs are through reflection, you can just include the provided *.cs
files in your existing projects and compile without any additional settings required for using Windows Runtime APIs in desktop applications. If you choose to compile the class library and reference it in your projects, that is fine, too.
Summary
SkyDrive for Windows 8.1 introduced a new technology, called smart files. These files can be consumed just like regular files by using Windows Runtime APIs. However, applications that uses System.IO APIs will have problems when opening a smart file unless its content is fully downloaded on the device. With this blog post, we provided you a sample code that invokes Windows.Storage APIs by reflection. This sample code will enable you to write desktop apps that operates on smart files available on Windows 8.1 and also supports running on non-Windows Runtime platforms.
Enjoy consuming new smart files in your desktop apps and let us know if you have any feedback!