Как правильно реализовать расширение оболочки обработчика управляемого свойства?

Теперь, когда.NET CLR 4.0 поддерживает параллельную (SxS) операцию, теперь стало возможным писать расширения оболочки в управляемом коде. Я попытался это сделать и успешно кодировал обработчик свойств, который реализует IPropertyStore, IInitializeWithStream и IPropertyStoreCapabilities.

Обработчик работает нормально и вызывается, как и ожидалось, при просмотре файлов через проводник. Он также прекрасно работает при отображении пользовательских свойств на панели предварительного просмотра и на панели "Свойства" свойств файла.

Однако, когда я пытаюсь отредактировать свойство на панели предварительного просмотра, а затем нажимаю "Сохранить", я получаю сообщение об ошибке "Файл используется", говорящий о том, что файл открыт в проводнике Windows.

Несколько лакомых кусочков:

  1. Когда проводник вызывает IInitializeWithStream.Initialize, для свойства STGM устанавливается значение STGM_SHARE_DENY_WRITE.
  2. И ни в коем случае проводник не вызывал IPropertyStore.SetValue или IPropertyStore.Commit.
  3. Я вижу повторяющиеся вызовы моего обработчика в разных потоках для одних и тех же свойств файла.

Так что мне нужно изменить (или установить в реестре), чтобы сохранить свойство для работы?

Обновить:

Благодаря Бену у меня все работает. "Трудной частью" (по крайней мере для меня) было понимание того, что COM-взаимодействие никогда не вызовет Dispose или Finalize для моего PropertyHandler. Это оставляло файлы, которые я обрабатывал, открытыми до запуска GC.

К счастью, "протокол обработчика свойств" работает так, что при вызове IInitializeWithSream.Initialize() для ReadValue() streamMode является ReadOnly, а когда он вызывается для SetValue(), streamMode - ReadWrite, а Commit() вызывается в конце.

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;
}

2 ответа

Решение

Да, вы должны AddRef() поток, чтобы держать его открытым и правильно поддерживать ссылку.

Обратите внимание, что индексатор также будет использовать ваш обработчик свойств для открытия файла. Таким образом, если вы пропустите объект потока, файл останется открытым. Вы можете использовать процедуру sysinternals procxp, чтобы сообщить, какому процессу открыт файл, или procmon, чтобы указать, какие вызовы и параметры он использовал.

Проводник пытается убедиться, что он не мешает другим приложениям, которые могут открыть файл. Может ли файл законно использоваться другим приложением? Открыт ли обработчик предварительного просмотра?

Иногда мы видим обработчики свойств, которые держат свои потоки открытыми дольше, чем необходимо (или файловые обработчики, которые открывают файл с ограниченными правами). Можете ли вы проверить, своевременно ли вы выпускаете поток?

Наконец, я не думаю, что это связано с вашей непосредственной проблемой, но использование расширений оболочки.NET не поддерживается. Мы рекомендуем, чтобы это не было включено ни в один продукт.

-Бен

Другие вопросы по тегам