Isolated storage, Windows, and ClickOnce

Originally posted to Shawn Hargreaves Blog on MSDN, Thursday, December 16, 2010

You want to implement save games.

You’ve decided to do this using isolated storage  (perhaps because you are targeting Windows Phone, or because you want something simpler than the Xbox StorageContainer APIs).

But since XNA is so awesomely portable, you are also making a Windows version of your game  (perhaps you want to ship it on Windows, or maybe you just want to use Windows tools to debug and profile your Xbox or phone game).

So we make a new Windows game project. In our Initialize method, we add:

    IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForApplication();

It compiles!  (which proves this API must be properly portable, right?  :-)

But when we run on Windows, we get an IsolatedStorageException with the message "Unable to determine application identity of the caller."

What gives?

Isolated storage on Windows actually provides several Get*Store* methods, which differ in how the storage location is identified. Some are per-user, others per-machine. Some use the identity of the calling assembly, or the current AppDomain, or the current website, to isolate the save location.

Windows Phone only supports per-user per-application storage (via GetUserStoreForApplication), so it does not include all the other Get*Store* methods that are available on Windows. But in order to save data per-application, the current application must have a well defined identity. That is always the case on Xbox or Windows Phone, where applications are deployed from packages that include manifests with name and version information. But Windows can directly run arbitrary .exe files, which may not have an application identity as required by this call.

One option is to use a different isolated store on Windows. We can wrap this platform difference in a helper method:

    IsolatedStorageFile GetUserStoreAsAppropriateForCurrentPlatform()
    {
#if WINDOWS
        return IsolatedStorageFile.GetUserStoreForDomain();
#else
        return IsolatedStorageFile.GetUserStoreForApplication();
#endif
    }

Both versions return an IsolatedStorageFile object, so after we call this helper the rest of our save code can be the same on all platforms.

If we really want to use GetUserStoreForApplication on Windows, there are two ways to provide the necessary application identity:

We can deploy the application using a ClickOnce installer. This is great for end users, but not so much for testing during development!

Or we can enable a special debugger feature that emulates ClickOnce security while debugging inside Visual Studio:

Now GetUserStoreForApplication will work when we F5 debug inside Visual Studio (but will still fail if we run the application standalone, such as via Ctrl+F5, until we properly install it using ClickOnce).

Beware of a Visual Studio bug. If you turn on "Enable ClickOnce security settings" but not "Enable the Visual Studio hosting process", you get an error message:

"The security debugging option is set but it requires the Visual Studio hosting process which is unavailable in this debugging configuration. The security debugging option will be disabled. This option may be re-enabled in the Security property page. The debugging session will continue without security debugging."

That sounds fair enough, but Visual Studio doesn't actually do what the message says :-)  In fact it leaves your project in a broken state, where GetUserStoreForApplication will continue to fail even after you turn "Enable the Visual Studio hosting process" back on!

If this happens to your project, you can fix it by deleting the .csproj.user file that is stored alongside your main .csproj.

Blog index   -   Back to my homepage