據我了解,目前有兩種方法可以使用資源管理器從 Shell 命名空間擴展復制虛擬檔案,以便將復制 GUI 顯示給用戶:
通過
IDataObject介面:讀取檔案是通過
IDataObject::GetData至少應支持CFSTR_FILEDESCRIPTORW,CFSTR_FILECONTENTS和CFSTR_SHELLIDLIST剪貼板格式來完成的。請求CFSTR_FILECONTENTSfromIDataObject::GetData應該創建一個IStream用于訪問資料的。UI 在請求時通過設定FD_PROGRESSUI標志啟用。CFSTR_FILEDESCRIPTORW
通過
ITransferSource介面:讀取檔案是通過
ITransferSource::OpenItem請求IShellItemResources. 然后IShellItemResources應報告{4F74D1CF-680C-4EA3-8020-4BDA6792DA3C}支持的資源(GUID 指示該專案存在 IStream)。最后,IStream通過 parent 請求ShellFolder::BindToObject訪問資料。UI 由資源管理器本身處理,它始終顯示。
我的問題是:這兩種機制分別作業得很好(從螢屏截圖中可以看出)。但是,一旦我同時啟用IDataObjectfromIShellFolder::GetUIObjectOf和ITransferSourcefrom -總是使用IShellFolder::CreateViewObject方法 via導致舊的副本 GUI(如第一個螢屏截圖所示)。IDataObject我從ITransferSource多次請求的跟蹤日志中看到,但沒有執行任何操作,它只是被釋放并立即銷毀。
那么,當從我的 Shell 命名空間擴展復制時,如何強制資源管理器顯示精美的復制 GUI?
可以在此處找到一個最小的可重現示例:https ://github.com/BilyakA/SO_73938149
在處理 Minimal Reproducible 示例時,我設法使其在啟用IDataObject和ITranfserSource介面的情況下按預期作業。它發生在:
- 未注冊的 x64 構建 SNE 示例 (
regsvr32 /u) - 已注冊的 x32 構建 SNE 示例(它在 x64 資源管理器中不起作用,root 未打開)
- 未注冊的 x32
- 再次注冊x64。
復制檔案時以某種方式向我顯示了新的復制 UI。但是在我取消注冊 x64 SNE、重新啟動資源管理器并再次注冊 SNE x64 后,我無法不斷重現此結果。
我試過的:
- 注冊了 x32 和 x64 SNE - 仍然是舊的 GUI
- 使用我的 NSE GUID洗掉
Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Cached值,然后重新啟動資源管理器。仍然是舊的 GUI。
我懷疑有某種快取(注冊表除外)可以跟蹤 NSE 是否支持ITransferSource. 而且由于我一開始就沒有開發和測驗過ITransferSource- 即使我后來添加了它,我的 NSE 也不支持它被快取。并以某種方式注冊 32 位重置該快取值。
uj5u.com熱心網友回復:
當要求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);
}
...
}
SHCreateDataObjectIDataObject已經從(可選)輸入 PIDL(s) 中創建了一個完整的內容,包括 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);
}
...
}
實際上,IDataObjectShell 提供的功能是完整的(支持SetData方法等),因此您永遠不需要自己實作它,它比看起來更復雜。您可以將其用作通用用途IDataObject(您可以為 4 個第一個引數傳遞空值和 0)。
PS:Shell 還提供了“反向”方法:SHCreateShellItemArrayFromDataObject從一個IDataObject(如果資料物件包含預期的剪貼板格式)獲取 Shell 專案串列。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/512747.html
上一篇:Win32API子視窗閃爍
