无法获取 Windows 7 C++ 上虚拟文件夹的路径(shell 命名空间扩展相关)
Posted
技术标签:
【中文标题】无法获取 Windows 7 C++ 上虚拟文件夹的路径(shell 命名空间扩展相关)【英文标题】:cannot get the path for the virtual folder on windows 7 C++(shell namespace extension related) 【发布时间】:2017-11-07 23:05:19 【问题描述】:通过使用 Microsoft windows SDK 7.0 explorerDataProvider,我在 windows 7 上安装了一个虚拟文件夹。
当我从应用程序打开文件浏览对话框时,
CFileDialog dlg(TRUE, NULL, 0, OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);
INT_PTR result = dlg.DoModal();
if (result == IDOK)
.
.
.
//some actions
它还可以显示虚拟文件夹:
但是当我选择文件的时候,像“零”然后可以点击“打开”,
我尝试添加断点
INT_PTR result = dlg.DoModal();
但似乎这个错误发生在DoModal()
内部。
我正在研究并修改虚拟文件夹的代码:
HRESULT CFolderViewImplFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, ULONG *rgfInOut)
// If SFGAO_FILESYSTEM is returned, GetDisplayNameOf(SHGDN_FORPARSING) on that item MUST
// return a filesystem path.
HRESULT hr = E_INVALIDARG;
DWORD dwAttribs = 0;
dwAttribs |= SFGAO_BROWSABLE;
if (1 == cidl)
int nLevel = 0;
hr = _GetLevel(apidl[0], &nLevel);
if (SUCCEEDED(hr))
BOOL fIsFolder = FALSE;
hr = _GetFolderness(apidl[0], &fIsFolder);
if (SUCCEEDED(hr))
if (fIsFolder)
dwAttribs |= SFGAO_FOLDER;
dwAttribs |= SFGAO_FILESYSANCESTOR;
else
dwAttribs |= SFGAO_SYSTEM;
dwAttribs |= SFGAO_FILESYSTEM;
if (nLevel < g_nMaxLevel)
dwAttribs |= SFGAO_HASSUBFOLDER;
*rgfInOut &= dwAttribs;
return hr;
并返回GetDisplayNameOf()
中的路径
HRESULT CFolderViewImplFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF shgdnFlags, STRRET *pName)
HRESULT hr = S_OK;
if (shgdnFlags & SHGDN_FORPARSING)
WCHAR szDisplayName[MAX_PATH];
if (shgdnFlags & SHGDN_INFOLDER)
// This form of the display name needs to be handled by ParseDisplayName.
hr = _GetName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
else
PWSTR pszThisFolder = L"Computer\\Jerry";
StringCchCopy(szDisplayName, ARRAYSIZE(szDisplayName), pszThisFolder);
StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), L"\\");
WCHAR szName[MAX_PATH];
hr = _GetName(pidl, szName, ARRAYSIZE(szName));
if (SUCCEEDED(hr))
StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), szName);
if (SUCCEEDED(hr))
hr = StringToStrRet(szDisplayName, pName);
else
PWSTR pszName;
hr = _GetName(pidl, &pszName);
if (SUCCEEDED(hr))
hr = StringToStrRet(pszName, pName);
CoTaskMemFree(pszName);
return hr;
但它仍然在应用程序中显示“路径不存在”的错误消息。
我在GetDisplayNameOf
中添加了断点,但是当应用程序打开文件浏览对话框时它永远不会触发。但是如果我只是双击“计算机”打开一个普通文件夹,它就会被触发。
“路径不存在”的错误消息似乎不能被弃用或覆盖。有什么方法可以修改虚拟文件夹以返回正确的路径?
更新:我试过IFileDialog
,我复制了MSDN示例中的代码,代码,加断点看看会发生什么。然而,
// Show the dialog
hr = pfd->Show(NULL);//the breakpoint is added here
if (SUCCEEDED(hr))
// Obtain the result once the user clicks
// the 'Open' button.
// The result is an IShellItem object.
IShellItem *psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
//do something
当我点击“打开”按钮时,仍然出现“路径不存在,请检查路径并重试”的错误消息框。在我点击“打开”按钮后,它永远不会从pfd->Show(NULL)
中出来。
根据示例代码中的 cmets,它应该来自 hr = pfd->Show(NULL);
并到达下一行。但错误消息发生在 hr = pfd->Show(NULL);
内。
更新:
在IFileDialog
,我试过了
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS);
if (SUCCEEDED(hr))
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
// Obtain the result once the user clicks
// the 'Open' button.
// The result is an IShellItem object.
IShellItem *psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
// We are just going to print out the
// name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
&pszFilePath);
if (SUCCEEDED(hr))
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
psiResult->Release();
我也试过了
hr = pfd->SetOptions(dwFlags);//delete FOS_FORCEFILESYSTEM
试过了
DWORD dwFlags = 0;
if (SUCCEEDED(hr))
// overwrite options completely
hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS);
我还尝试在命名空间扩展代码中返回全名:
WCHAR szDisplayName[MAX_PATH];
if (shgdnFlags & SHGDN_INFOLDER)
// This form of the display name needs to be handled by ParseDisplayName.
hr = _GetName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
else
PWSTR pszThisFolder;// = L"Computer\\Jerry";
hr = SHGetNameFromIDList(m_pidl, (shgdnFlags & SHGDN_FORADDRESSBAR) ? SIGDN_DESKTOPABSOLUTEEDITING : SIGDN_DESKTOPABSOLUTEPARSING, &pszThisFolder);
if (SUCCEEDED(hr))
StringCchCopy(szDisplayName, ARRAYSIZE(szDisplayName), pszThisFolder);
StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), L"\\");
WCHAR szName[MAX_PATH];
hr = _GetName(pidl, szName, ARRAYSIZE(szName));
if (SUCCEEDED(hr))
StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), szName);
CoTaskMemFree(pszThisFolder);
if (SUCCEEDED(hr))
hr = StringToStrRet(szDisplayName, pName);
但结果是:
更新:
hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);
这对我有用!!
【问题讨论】:
您的 SNE 是根计算机命名空间的子节点,而不是任何文件系统驱动器的子节点。你可以随心所欲地谎报你的物品属性,你的 SNE 仍然不是文件系统的一部分,所以没有文件系统路径可供对话框检索。很明显,您正在调用一个只知道如何处理文件系统的旧对话框,而不是一个支持虚拟项目的新对话框 @RemyLebeau 你能告诉我哪个较新的对话框可以支持虚拟物品吗?IFile(Open|Save)Dialog
,在 Vista 中引入。 CFileDialog
原包装Get(Open|Save)FileName()
,只支持文件系统。在 VS2008+ 中,CFileDialog
在其构造函数中有一个bVistaStyle
参数,当您为 Vista+ 编译时,应该默认使用较新的 API。我建议放弃CFileDialog
,直接使用IFileOpenDialog
。
@RemyLebeau 你能检查我的更新吗?好像不行。
在显示之前您将哪些选项传递给IFileDialog
?请显示minimal reproducible example。您是否偶然启用了FOS_FORCEFILESYSTEM
选项? “确保返回的项目是文件系统项目 (SFGAO_FILESYSTEM
)”,而你的不是。您是否尝试过FOS_ALLNONSTORAGEITEM
选项? "使用户能够选择 Shell 命名空间中的任何项目,而不仅仅是那些具有 SFGAO_STREAM
或 SFAGO_FILESYSTEM
属性的项目。"
【参考方案1】:
在@RemyLebeau 的帮助下,我在我的代码中做了一些测试。最后,我找到了适合我的工作解决方案:
我从 MSDN 复制了 IFileDialog
的代码:https://msdn.microsoft.com/en-us/library/windows/desktop/bb776913.aspx#file_types
然后稍微修改一下:
HRESULT BasicFileOpen()
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
// Hook up the event handler.
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
// Set the options on the dialog.
DWORD dwFlags = 0;
if (SUCCEEDED(hr))
// the default flag is FOS_PATHMUSTEXIST and FOS_FILEMUSTEXIST, so I set overwrite it with these two flags.
hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);
if (SUCCEEDED(hr))
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
// Obtain the result once the user clicks
// the 'Open' button.
// The result is an IShellItem object.
IShellItem *psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
// We are just going to print out the
// name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
&pszFilePath);
if (SUCCEEDED(hr))
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
psiResult->Release();
// Unhook the event handler.
pfd->Unadvise(dwCookie);
pfde->Release();
pfd->Release();
return hr;
关键是用SetOptions
覆盖选项。基于MSDN:https://msdn.microsoft.com/en-us/library/windows/desktop/dn457282(v=vs.85).aspx,
FOS_PATHMUSTEXIST
The item returned must be in an existing folder. This is a default value.
FOS_FILEMUSTEXIST
The item returned must exist. This is a default value for the Open dialog.
这些标志是阻止它返回的默认值。 所以我把它设置为:
hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);
这些标志意味着:
FOS_NOVALIDATE
Do not check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors.
和
FOS_ALLNONSTORAGEITEMS
Enables the user to choose any item in the Shell namespace, not just those with SFGAO_STREAM or SFAGO_FILESYSTEM attributes. This flag cannot be combined with FOS_FORCEFILESYSTEM.
然后它终于起作用了!
【讨论】:
以上是关于无法获取 Windows 7 C++ 上虚拟文件夹的路径(shell 命名空间扩展相关)的主要内容,如果未能解决你的问题,请参考以下文章
对文件“F:\虚拟机14.0\Virtual Machines\Windows 7 x64\Windows 7 x64.vmdk”的操作失败。
无法在 Windows 7 机器上的 Microsoft Visual C++ 2010 中运行 OpenCV