The deal with IPropertyStoreCapabilities
Have you ever felt this before? It's the day after you send your product to manufacturing. You step back, look at the big picture, and start to see minor flaws. A dread sinks in when you realize you just blogged about it too! That was Monday morning. :-) The good news is that our analysis shows that the flaws are minor. They are unlikely to affect people in real practice, don't affect the code we wrote, and there are workarounds if you're trying something complicated. So... here's the whirlwind tour of IPropertyStoreCapabilities (IPSC):
We needed a way to mark which properties are writable and which aren't. We looked at a few models and decided we wanted a runtime check for writability. So we invented IPSC to let a property handler declare what properties it supports and those it doesn't. For example, the PNG property handler uses IPSC to tell explorer that it supports writing System.Photo.DateTaken, but nothing else.
Enter the multiplex property store. This is a building block object we wrote to help shell namespaces and property handlers to build their property stores. Like the name suggests, it selects from various inputs. From the start, we found this object hard to design and work with. We had to figure out reasonable ways to select which input to select. We had to decide how to handle writing. How to handle IPSC. To keep things simple, we decided to not have an interface to let the MUX know which store handles what property. Instead...
- The MUX is read-only (i.e. IPropertyStore::SetValue returns STG_E_ACCESSDENIED).
- IPropertyStore::GetValue would select the first value it finds.
- IPSC::IsPropertyWritable queries the first store that implements this interface.
#1 means the store returned by GPS_READWRITE cannot use the MUX. This means if you use IPSC on a GPS_READWRITE store, you don't get the full picture since we're ignoring the read-only data sources.
#2 is interesting, but doesn't affect us much today.
#3 is what made me panic yesterday. Imagine if two stores implemented IPSC and were joined by a MUX. The calling application would only be able to query the first store! So here are the mitigating circumstances:
- The filesystem shell namespace extension uses MUX's, but the only implementation of IPropertyStoreCapabilities is the property handler. So this is OK.
- None of the filetype property handlers that ship with Windows Vista use MUX's internally. So this is OK.
- All other places in Windows Vista that would get in trouble from this are read-only to begin with. So this is OK.
- The only time you'd need two stores to implement IPSC is if the filetype is writable and both stores deal with non-innate properties (because this is in the only time you need to provide the distinction). This is less common than other scenarios.
- Even if a namespace extension (or property handler) needs to have two stores implement IPSC, they could work around this by having the read-only implementation delegate to the read-write implementation.
So in the end, I am going back to my original recommendation: Use IPropertyStoreCapabilities with GPS_DEFAULT. This is what explorer does, the shipping proprety stores are fine, and other hopefully other developers will find and understand my blog.
-Ben