5

现在 .NET CLR 4.0 支持并行 (SxS) 操作,现在应该可以在托管代码中编写 shell 扩展。我已经尝试过这个并成功编写了一个实现 IPropertyStore、IInitializeWithStream 和 IPropertyStoreCapabilities 的属性处理程序。

该处理程序工作正常,并在通过资源管理器浏览文件时按预期调用。它还可以在预览面板和文件属性“详细信息”面板中显示自定义属性。

但是,当我尝试在预览面板中编辑属性,然后单击“保存”时,我收到“正在使用的文件”错误,指出该文件已在 Windows 资源管理器中打开。

一些花絮:

  1. 当资源管理器调用 IInitializeWithStream.Initialize 时,STGM 属性设置为 STGM_SHARE_DENY_WRITE。
  2. 并且资源管理器从未调用 IPropertyStore.SetValue 或 IPropertyStore.Commit。
  3. 我看到在相同文件属性的不同线程上重复调用我的处理程序。

那么我需要更改(或在注册表中设置)以使属性保存工作?

更新:

多亏了本,我已经成功了。“困难的部分”(至少对我而言)是理解 COM 互操作永远不会在我的 PropertyHandler 上调用 Dispose 或 Finalize。这使我处理的文件保持打开状态,直到 GC 运行。

幸运的是,“属性处理程序协议”的工作原理是,当为 ReadValue() 调用 IInitializeWithSream.Initialize() 时,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;
}
4

2 回答 2

3

是的,您必须 AddRef() 流以使其保持打开并保持引用正确。

请注意,索引器也将使用您的属性处理程序来打开文件。因此,如果您泄漏流对象,该文件将保持打开状态。您可以使用 sysinternals procexp 来判断哪个进程打开了文件,或者使用 procmon 来判断它使用了哪些调用和参数。

于 2010-07-13T04:23:04.277 回答
1

Explorer 会尝试确保它不会干扰可能打开文件的其他应用程序。该文件是否可以被其他应用程序合法使用?是否打开了预览处理程序?

有时,我们会看到属性处理程序使流的打开时间超过必要时间(或基于文件的处理程序以限制性权限打开文件)。您能否验证您是否及时发布了流?

最后,我认为这与您的直接问题无关,但不支持使用 .NET shell 扩展。我们建议不要将其合并到任何产品中。

-本

于 2010-07-13T01:32:02.433 回答