实现托管属性处理程序 Shell 扩展的正确方法是什么?

发布于 2024-09-08 18:01:46 字数 1598 浏览 4 评论 0原文

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

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

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

一些花絮:

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

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

更新:

感谢 Ben,我已经成功了。 “困难的部分”(至少对我来说)是理解 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;
}

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:

  1. When explorer calls IInitializeWithStream.Initialize the STGM property is set to STGM_SHARE_DENY_WRITE.
  2. And at no point did explorer call IPropertyStore.SetValue or IPropertyStore.Commit.
  3. 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

童话 2024-09-15 18:01:46

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

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

Yes, you have to AddRef() the stream to keep it open and to keep the reference alive correctly.

Note that the indexer will use your property handler to open the file as well. So if you leak the stream object, the file will remain open. You can use the sysinternals procexp to tell what process has the file open, or procmon to tell what calls and parameters it used.

回忆那么伤 2024-09-15 18:01:46

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

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

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

-本

Explorer tries to ensure that it doesn't interfere with other applications that may have the file open. Could the file be legitimately in use by another application? Is there a preview handler open?

Sometimes, we see property handlers that keep their streams open longer than necessary (or file-based handlers that open the file with restrictive permissions). Can you verify if you are releasing the stream in a timely manner?

Finally, I don't think this is related to your immediate problem, but using .NET shell extensions is unsupported. We recommend that this not be incorporated into any product.

-Ben

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文