Now that .NET CLR 4.0 supports side by side (SxS) operation it should now be possible to write shell extensions in managed code. I have attempted this and successfully coded a Property Handler that implements IPropertyStore, IInitializeWithStream and IPropertyStoreCapabilities.
The handler works fine and is called as expected when browsing files via the explorer. It also works fine in displaying the custom properties in the preview panel and the file properties "detail" panel.
However, when I attempt to edit a property in the preview panel, and then click "Save" I get a "File In Use" error saying that the file is open in Windows Explorer.
A few tidbits:
- When explorer calls IInitializeWithStream.Initialize the STGM property is set to STGM_SHARE_DENY_WRITE.
- And at no point did explorer call IPropertyStore.SetValue or IPropertyStore.Commit.
- I see repeated calls to my handler on different threads for the same file properties.
So what do I need to change (or set in the registery) to get the property save to work?
Update:
Thanks to Ben I've got it working. The "difficult part" (at least for me) was understanding that COM interop would never call Dispose or Finalize on my PropertyHandler. This was leaving the files I processed open till the GC ran.
Fortunately, the "property handler protocol" works such that when IInitializeWithSream.Initialize() is called for a ReadValue() the streamMode is ReadOnly, and when it is called for a SetValue() the streamMode is ReadWrite and Commit() will be called at the end.
int IInitializeWithStream.Initialize( IStream stream, uint grfMode )
{
_stream = stream;
_streamMode = (Stgm)grfMode;
Load();
// We release here cause if this is a read operation we won't get called back,
// and our finializer isn't called.
if ( ( _streamMode & Stgm.ReadWrite ) != Stgm.ReadWrite )
{
Marshal.ReleaseComObject( _stream );
_stream = null;
}
return HResult.S_OK;
}
int IPropertyStore.Commit()
{
bool result = false;
if ( _stream != null )
{
result = WriteStream( _stream );
Marshal.ReleaseComObject( _stream );
_stream = null;
}
return result ? HResult.S_OK : HResult.E_FAIL;
}