The previous post presented the WinRT Contracts that are available to exchange information between WSAs and WSAs/DAs. It is time to dig into the details of leveraging the file association feature of Windows to let one application activate another application.
WARNING: all these techniques are violating the point 3.1 from the Windows Store Requirements that states: “Your app may only depend on software listed in the Windows Store“. Therefore, you should not use them for a WSA that you plan to publish into the Windows Store because it will be rejected.
Using file association to communicate between applications
In Windows, the file association mechanism allows automatic creation of a process to open a file based on its extension.
How to activate another application?
The first step is to figure out how to trigger the file-based activation. For a WSA, you simply need to create a file with the expected extension and pass the corresponding StorageFile object to the
Launcher.LaunchFileAsync method and Windows does the rest. The other
LauncherOptions parameter lets you decide if you want the confirmation dialog to appear.
UPDATE – 2013/03/09: If there is no application (either DA or WSA) associated to the file extension, the dialog will always popup, even if you did not ask for it.
As you can see from the flyout, the end user is able to “look for an app in the Store” that would support the file extension. If there is no such an application, the search page will be empty
It is good to see that the Windows Store is able to find a WSA based on a file extension but you won’t be able to find a DA that way into the Store.
Last point to take into account when you try to activate another application via
Launcher.LaunchFileAsync: this method will always return true even if no application was found locally or into the Store!
In my sample WSA, the file is created inside its local folder given by
ApplicationData.Current.LocalFolder. I’ve first tried to create it in the user Document library folder but a WSA is allowed to create a file in this folder only if the file has an extension supported by the WSA itself… this is definitively not compatible with what I’m trying to achieve since the DA already handles the files with this extension and I don’t want that a confirmation dialog pops up to let the user pick which application should open the file!
In the case of a DA, you call
CreateProcess with the filename as parameter. In both cases, you write the parameters you want to pass to the other application into the file.
How to associate an application to a file extension?
Both a WSA and a DA can register its execution with a file extension. The former requires changes in the manifest:
The latter requires more work in the Registry:
As you can see, the registration happens in two places under HKEY_CLASSES_ROOT. The default value associated to the extension name (without forgetting the “dot” character) gives the name of the next key where to look at for the shell/open/command default value. This is where the command line to execute is defined: note the %1 used as placeholder for the command line corresponding to the filename that triggers the launch.
The DAFileAssociation project calls the
AssociationRegistrationHelper.RegisterFileAssociation method that takes care of all the Registry setup. The returned enumeration value lets you know what happened
public enum AssociationRegistrationStatus
The calling process must run under an elevated administrator account to update the Registry or the
SecurityFailure value will be returned because the updated failed.
Last but not least, if you need to check that the file associations are correct on a machine, Control Panel is your friend via the WIN+X|Control Panel shortcut.
How to handle application activation?
The activation process will work but the activated WSA will have to carefully access the file. As you may know, a WSA is very restricted in term of file access: it is allowed to access a file without going through the File Picker if the capability corresponding to the containing folder (from Documents, Music and Pictures libraries) has been added into its manifest. As I just mentioned, there is another condition to open or create a file there: its extension must also be added into the Declarations section of the manifest. This is perfect for the WSA that we want to activate but not possible for another WSA because we don’t want to have both activated for the same type of file.
OnFileActivated override from the
Application-derived class allows you to access files without going through the File Picker. The
FileActivatedEventArgs event argument provides two important parameters. Its string Verb property has the “open“ value in this kind of activation but other are supported such as print. The
IReadOnlyList<Windows.Storage.IStorageItem> Files property lets you get to the list of files that activate the WSA. However, there is a trick here: only the pathname of a file is accessible by the
IStorageItem interface via its Name or Path string property as shown in the MSDN documentation.
If you try to access the file by calling the
StorageFile.GetFileFromPathAsync method passing the pathname, an
UnauthorizedAccessException is thrown with AccessDenied.
The solution is to cast the
IStorageItem into a
StorageFile and use a stream/data reader pair to read the file content:
StorageFile sf = file as StorageFile;
if (sf!= null)
IRandomAccessStream stream = await sf.OpenAsync(FileAccessMode.Read);
DataReader reader = new DataReader(stream.GetInputStreamAt(0));
UInt32 size = await reader.LoadAsync((UInt32)stream.Size);
string content = reader.ReadString(reader.UnconsumedBufferLength);
For DA, the filename is available from
Environment.CommandLine with the following format:
“C:\…\DaFileAssociation.exe” C:\Users\<user>\ AppData\Local\Packages\8fe887ce-3234-45e3-9f00-71c02429fd5f_2g303v4t3xrhw\LocalState\2269fc55-6189-4d9a-b2d9-3d45a5a095ee.daparams
The sample application code looks for the last “ character to extract the filename. In a DA, the limits to read the file contents do not exist and StreamReader.ReadToEnd() is your friend:
using (StreamReader sr = new StreamReader(filename))
String line = sr.ReadToEnd();
tbWorkload.Text = line;
What data to exchange?
The simple examples shown since the beginning of the post are exchanging plain text. It would be better to be able to exchange more structured data. If you try to use basic .NET serialization, you will find out that the binary serializer is not available for a WSA written in .NET; use my BCLDiff tool to have the complete list of missing types:
Fortunately, the DataContract-based serialization is usable from both sides which makes it a good candidate to exchange strongly typed objects. Then, you need to decide which formatter to use; for example Json could be used to exchange smaller payload than with XML. Also note that WinRT
JsonObject types make it easy to manipulate and parse Json structures for a WSA, while a DA could use a
As you may know, there is no API for a WSA to change the Windows desktop wallpaper. However, Microsoft provides C# code sample to do so in a DA. The WSA built for this post allows the end user to use the webcam to take a picture and send the fill option with the image filename as a workload serialized in Json
to the DA that use it to change the Windows wallpaper
The next post will describe how to achieve the same kind of features but with custom protocols.