ElD*_*ado 5 winapi windows-shell shell-extensions shell-namespace-extension
据我了解,目前有两种方法可以使用资源管理器从 Shell 命名空间扩展复制虚拟文件,以便向用户显示复制 GUI:
通过IDataObject接口:
读取文件是通过至少IDataObject::GetData支持CFSTR_FILEDESCRIPTORW,CFSTR_FILECONTENTS和剪贴板格式来完成的。CFSTR_SHELLIDLIST请求应创建一个用于访问数据的对象CFSTR_FILECONTENTS。当需要时,通过设置标志来启用 UI 。IDataObject::GetDataIStreamFD_PROGRESSUICFSTR_FILEDESCRIPTORW
通过ITransferSource接口:
读取文件是通过ITransferSource::OpenItem请求IShellItemResources. 然后IShellItemResources应将{4F74D1CF-680C-4EA3-8020-4BDA6792DA3C}资源报告为受支持(GUID 指示该项目有一个 IStream)。最后,IStream通过父级请求ShellFolder::BindToObject访问数据。UI 由资源管理器本身处理,它始终显示。
我的问题是:这两种机制单独工作得很好(正如您从屏幕截图中看到的)。但是一旦我启用了IDataObjectfromIShellFolder::GetUIObjectOf和ITransferSourcefrom IShellFolder::CreateViewObject- 总是使用 via 方法IDataObject,导致旧的副本 GUI(如第一个屏幕截图所示)。我从跟踪日志中看到ITransferSource多次请求,但没有执行任何操作,它只是立即释放并销毁。
那么,当从 Shell 命名空间扩展进行复制时,如何强制资源管理器显示精美的复制 GUI?
可以在这里找到最小的可重现示例:https://github.com/BilyakA/SO_73938149
在研究最小可重复示例时,我设法使其在启用IDataObject和ITranfserSource接口的情况下按预期工作。事情发生在之后:
regsvr32 /u)不知何故,复制文件时向我显示了新的复制 UI。但在取消注册 x64 SNE、重新启动资源管理器并再次注册 SNE x64 后,我无法不断重现此结果。
我尝试过的:
Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Cached使用我的 NSE GUID删除值,然后重新启动资源管理器。还是旧的图形用户界面。我怀疑有某种缓存(除了注册表之外)可以跟踪 NSE 是否支持ITransferSource. ITransferSource而且由于我一开始就没有开发和测试- 它被缓存,我的 NSE 不支持它,即使我后来添加了它。并且以某种方式注册 32 位重置该缓存值。
当请求 IDataObject 时,当前代码正在执行类似的操作:
HRESULT CFolderViewImplFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT rgfReserved, void **ppv)
{
....
if (riid == IID_IDataObject)
{
CDataObject* dataObject = new (std::nothrow) CDataObject(this, cidl, apidl);
return ::SHCreateDataObject(m_pidl, cidl, apidl, dataObject, riid, ppv);
}
...
}
Run Code Online (Sandbox Code Playgroud)
SHCreateDataObject已经创建了一个完整的 Shell,IDataObject其中包含(可选)输入 PIDL 所需的所有内容,包括 Shell 命名空间中项目的所有 Shell 格式(CFSTR_SHELLIDLIST等等)。
传递内部对象 1) 可能会与 Shell 的功能发生冲突,2) 需要良好的实现(我没有检查过)。
所以你可以像这样替换它:
HRESULT CFolderViewImplFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT rgfReserved, void **ppv)
{
....
if (riid == IID_IDataObject)
{
return ::SHCreateDataObject(m_pidl, cidl, apidl, NULL, riid, ppv);
}
...
}
Run Code Online (Sandbox Code Playgroud)
事实上,IDataObjectShell 提供的功能是完整的(支持SetData方法等),因此您永远不需要自己实现它,它比看起来更复杂。您可以将其重用为通用用途IDataObject(您可以为第 4 个参数传递 null 和 0)。
PS:Shell 还提供了“反向”方法:SHCreateShellItemArrayFromDataObjectIDataObject ,它从(如果数据对象包含预期的剪贴板格式)获取 Shell 项目列表。
| 归档时间: |
|
| 查看次数: |
441 次 |
| 最近记录: |