Sunday, February 6, 2011

Notes on safe use of StorageDevice in XNA

I recently found a bug in StorageTasks.fs, an exception that I did not expect can be thrown in some situations. I am therefore going over the code and checking if I'm handling all exceptions correctly.

The bug I just fixed was an uncaught InvalidOperationException when attempting to open a StorageContainer after pulling the memory unit (which was picked as the storage device). The method that threw that exception was StorageDevice.EndOpenContainer. Sadly, the msdn documentation does not mention that.

I thought I would use EasyStorage as a reference. Unfortunately, even EasyStorage isn't 100% correct. For instance, see SaveDevice.OpenContainer: It does not catch InvalidOperationException. Although unlikely, this can happen if the user disconnects the storage device after BeginOpenContainer and before EndOpenContainer.

Anyway, I'll just go on and rely on testing to get it right... In the mean time, here are my findings regarding exceptions thrown by the StorageDevice API in XNA.

StorageDevice.BeginShowSelector
  • GuideAlreadyVisibleException
StorageDevice.EndShowSelector
  • None known
StorageDevice.BeginOpenContainer
  • InvalidOperationException (from msdn)
  • ArgumentNullException (from msdn)
  • StorageDeviceNotConnectedException
StorageDevice.EndOpenContainer
  • InvalidOperationException
  • StorageDeviceNotConnectedException
StorageContainer.OpenFile
  • StorageDeviceNotConnectedException
  • FileNotFoundException
StorageContainer.CreateFile
  • StorageDeviceNotConnectedException
  • Possibly other exceptions, e.g. if no space is left on the device?
I have listed StorageDeviceNotConnectedException under most methods. Although it's never mentioned explicitly on msdn in the method documentation, it seems reasonable to expect it in any situation where the device might be accessed.

There are other failure scenarios associated to Stream and serialization which I'm not listing here. In particular, XML de-serialization of F# types (discriminated unions, records, tuples, lists...) will fail at run-time due to these types being immutable and lacking a default constructor.

2 comments:

Chris said...

Hey, I noticed no one has posted a comment on this...so I decided to, even though its over a year old.

Thanks for the detective work!

I am in the final stages of my c# game for the xbox indie market, and I was going to do exactly this to find all the exceptions to have a clean try/catch block during save/load.

Why no one else has commented on this I have no clue...I've tested quite a few games for peer reviews and judging by the reviewers and the posters, most are not using try/catches. Oh wells

Good work and thnx again!

Johann Deneux said...

Thanks for the kind words Chris! I'm glad this post helped someone.