Isolated storage, Windows, and ClickOnce

Isolated storage, Windows, and ClickOnce

  • Comments 5

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:

  • Open the Visual Studio project properties
  • Switch to the Debug tab, and check "Enable the Visual Studio hosting process"
  • Switch to the Security tab, and check "Enable ClickOnce security settings"

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.

  • Hey Shawn,

    Would you say this can safely replace something like EasyStorage?

    Seems straight forward and easy which leaves me feeling a bit scared that it will fail in some critical way, like during peer review. ;)

    So is this a fool proof way to always provide a storage location for saved games etc?

    I.e. will work if the user pulls their memory cards etc?

    Or am I understanding the post incorrectly?

  • From what someone else told me while referencing this thread, this method guarantees the stored data will reside next to the game binary, and any time you remove the drive the game is on it will crash (at least I would assume).

    Some reviews I read were concerned about saving data without allowing the user to select a device (which is what this seems to allow), but I think it would make small save files much easier to implement.

    So yeah I'm curious as well if there are any potential pitfalls of using this in Peer Review?

  • This article was exactly what I needed for my ClickOnce apps.  Thanks very much.

  • I just found a caveat with the ClickOnce solution.  If you do this, you cannot use the CLRProfiler to track memory allocations (it crashes when trying to access the store). This leaves us to use the GetUserStoreForDomain() solution.

  • Hi Shaun,

    Thankyou just the answer I was looking for.

Page 1 of 1 (5 items)
Leave a Comment
  • Please add 6 and 6 and type the answer here:
  • Post