我尝试在文件选择对话框中仅显示与远程桌面共享的驱动器。以下是我想要展示的内容:
但根据Windows操作系统版本,左窗格中的驱动器和文件夹列表是不同的。屏幕截图的链接在回复评论中。
我们如何在多个Windows版本中显示相同的驱动器和文件夹列表?
这是实施:
CFileDialog my_file_dialog(TRUE);
CComPtr<IFileDialogCustomize> file_dialog = my_file_dialog.GetIFileDialogCustomize();
CComPtr<IFileDialog2> dialog2;
if (FAILED(file_dialog->QueryInterface(&dialog2))) {
::AfxMessageBox(_T("Failed to query the interface of IFileDialog2."));
return;
}
CComPtr<IShellItemFilter> shell_item_filter =
new CVisibleShellItemFilter(REMOTE_APP_VISIBLE_PATHS);
if (FAILED(dialog2->SetFilter(shell_item_filter))) {
::AfxMessageBox(_T("Failed to set the shell item filter to the file dialog."));
return;
}
CComPtr<IShellItem> shell_item;
if (FAILED(::SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, nullptr, IID_PPV_ARGS(&shell_item)))) {
::AfxMessageBox(_T("Failed to create a shell item for the computer folder."));
return;
}
if (FAILED(dialog2->SetDefaultFolder(shell_item))) {
::AfxMessageBox(_T("Failed to set the default folder to the computer folder."));
return;
}
CComPtr<IShellItem> shell_item_desktop;
if (FAILED(::SHCreateItemInKnownFolder(FOLDERID_Desktop, 0, nullptr, IID_PPV_ARGS(&shell_item_desktop)))) {
::AfxMessageBox(_T("Failed to create a shell item for the desktop."));
return;
}
if (FAILED(dialog2->SetNavigationRoot(shell_item_desktop))) {
::AfxMessageBox(_T("Failed to set the navigation root to the computer folder."));
return;
}
CComPtr<IShellItem> currently_selected_folder;
if (FAILED(dialog2->GetFolder(¤tly_selected_folder))) {
::AfxMessageBox(_T("Failed to set the navigation root to the computer folder."));
return;
}
if (!IsVisible(ToPathPartsList(REMOTE_APP_VISIBLE_PATHS), currently_selected_folder)) {
if (FAILED(dialog2->SetFolder(shell_item))) {
::AfxMessageBox(_T("Failed to set the folder to the computer folder."));
return;
}
}
if (my_file_dialog.DoModal() == IDOK) {
m_message.SetWindowTextW(my_file_dialog.GetPathName());
}
else {
m_message.SetWindowTextW(_T(""));
}
我做的是:
我做了2.从IFileDialogCustomize的实例中检索IFileDialog2的实例。这是因为我想支持&#34; Open&#34;和&#34;保存&#34;通过相同的例程进行文件对话。
重点是4 ..稍后我将展示IShellItemFilter的实现。
我做了5.因为如果默认文件夹不是共享驱动器或其后代文件夹,则会显示除共享驱动器或其后代文件夹以外的文件夹。
我做了6.因为我不想在文件对话框中显示最小内容,而且我不想显示桌面文件夹。
我做了7.因为如果当前文件夹不是共享驱动器或其后代文件夹,则显示共享驱动器或其后代文件夹以外的文件夹。
IShellItemFilter的实现是:
// If the desktop_absolute_parsing is "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", it shows the
// virtual folder that represents My Computer (PC).
// http://www.atmarkit.co.jp/ait/articles/1004/09/news094.html
static const std::wstring MY_COMPUTER_PATH = L"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
// If the desktop_absolute_parsing starts with \\tsclient\, it shows a drive shared with
// Remote Desktop (or RemoteApp) or its child folder.
// https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/74673059-17b0-4a80-80ac-66b5dc419b56?forum=vcgeneralja
static const std::wstring REMOTE_APP_SHARED_FOLDER_PATH_PREFIX = L"\\\\tsclient\\";
static const std::vector<std::wstring> REMOTE_APP_VISIBLE_PATHS = {
// Add My Compter to the paths so that users can navigate from the My Compter in the folder
// view.
MY_COMPUTER_PATH,
REMOTE_APP_SHARED_FOLDER_PATH_PREFIX,
};
// Converts a give string to lower cases.
// str String
// return Lower case string.
std::wstring ToLower(std::wstring str) {
std::transform(str.begin(), str.end(), str.begin(),
[](auto ch) { return std::tolower(ch, std::locale()); });
return str;
}
// Split a givn path to the parts. Users need to split a path into the path parts, and pass to
// the function below for the processing speed.
// ex) "c:\\windows\\system32" -> {"c:", "\\", "windows", "system32"}
// file_path Path to be splitted.
// return Path parts.
std::vector<std::wstring> SplitPath(const std::wstring& file_path) {
using std::experimental::filesystem::v1::path;
path p(file_path);
return std::vector<std::wstring>(p.begin(), p.end());
}
// Checks if a path is equal to or an ancestor of another path.
// ancestor_path_parts Given path parts.
// descendant_path_parts Other path parts.
// returns true if ancestor_path_parts is equal to or an ancestor of descendant_path_parts.
// false, otherwise.
bool IsPathSelfOrAncestor(const std::vector<std::wstring>& ancestor_path_parts,
const std::vector<std::wstring>& descendant_path_parts) {
// Do not compare the path strings directly. Because "C:\Windows\System32" matches
// "C:\Windows\System", for example.
return std::search(descendant_path_parts.begin(), descendant_path_parts.end(),
ancestor_path_parts.begin(), ancestor_path_parts.end()) ==
descendant_path_parts.begin();
}
// Checks if two given paths are in a direct line. i.e. A path is equal to, ancestor of, or
// decendant of another path.
// path_parts1 Given path parts.
// path_parts2 Other path parts.
// return true if the given two paths are in a direct line. false, otherwise.
bool IsInDirectLine(const std::vector<std::wstring>& path_parts1, const std::vector<std::wstring>& path_parts2) {
return IsPathSelfOrAncestor(path_parts1, path_parts2) ||
IsPathSelfOrAncestor(path_parts2, path_parts1);
}
// Gets the display name from a given shell item.
// sigdnName SIGDN name.
// shell_item Shell item.
// name Display name.
// return S_OK if this method succeeds. false, otherwise.
HRESULT GetDisplayName(IShellItem* shell_item, SIGDN sigdnName, std::wstring& name) {
LPWSTR raw_name = nullptr;
HRESULT result;
if (FAILED(result = shell_item->GetDisplayName(sigdnName, &raw_name))) {
return result;
}
name = raw_name;
::CoTaskMemFree(raw_name);
raw_name = nullptr;
return S_OK;
}
// Checks if a given shell item is visible in a file/folder view.
// visible_path_parts_list List of the visble paths parts.
// shell_item Shell item to be checked.
// return true if the given shell item is visible. false, otherwise.
bool IsVisible(const std::vector<std::vector<std::wstring> >& visible_path_parts_list, IShellItem* shell_item) {
std::wstring desktop_absolute_parsing;
if (FAILED(GetDisplayName(shell_item, SIGDN_DESKTOPABSOLUTEPARSING,
desktop_absolute_parsing))) {
::AfxMessageBox(_T("Failed to get the diplay name of a shell item."));
return false;
}
auto path_parts = SplitPath(ToLower(desktop_absolute_parsing));
for (const auto& visible_path_parts : visible_path_parts_list) {
// Check if shell_item and visible_path are in the direct line. i.e. shell_item and
// visible_path are same, shell_item is an ancestor of visible_path, or shell_item is
// an descendant of visible_path.
// Let users to navigate in the case that shell_item is an ancestor of visible_path.
// Otherwise, users can not navigate to the visible_path through the folder view in
// the file selection dialog.
if (IsInDirectLine(path_parts, visible_path_parts)) {
return true;
}
}
return false;
}
// Converts the list of paths into the list of the path parts.
std::vector<std::vector<std::wstring> > ToPathPartsList(
const std::vector<std::wstring>& paths) {
std::vector<std::vector<std::wstring> > path_parts_list;
for (const auto& path : paths) {
path_parts_list.push_back(SplitPath(ToLower(path)));
}
return path_parts_list;
}
// CVisibleShellItemFilter show only the visible shell items which are listed in the given list
// of paths.
class CVisibleShellItemFilter : public IShellItemFilter {
public:
CVisibleShellItemFilter(const std::vector<std::wstring>& visible_paths) :
m_visible_path_parts_list(ToPathPartsList(visible_paths)) { }
HRESULT QueryInterface(REFIID riid, void** ppvObject) override {
if (ppvObject == nullptr) {
return E_POINTER;
}
if (riid == IID_IUnknown || riid == IID_IShellItemFilter) {
*ppvObject = static_cast<void*>(this);
AddRef();
return NO_ERROR;
}
else {
*ppvObject = nullptr;
return E_NOINTERFACE;
}
}
ULONG AddRef() override {
return ++m_reference_count;
}
ULONG Release() override {
ULONG reference_count = --m_reference_count;
if (reference_count == 0) {
delete this;
}
return reference_count;
}
HRESULT IncludeItem(IShellItem *psi) override {
return IsVisible(m_visible_path_parts_list, psi) ? S_OK : S_FALSE;
}
HRESULT GetEnumFlagsForItem(IShellItem *psi, SHCONTF *pgrfFlags) override {
*pgrfFlags = static_cast<SHCONTF>(-1);
return S_OK;
}
private:
ULONG m_reference_count = 0;
const std::vector<std::vector<std::wstring> > m_visible_path_parts_list;
};
CVisibleShellItemFilter :: IncludeItem()如果文件的桌面绝对解析是&#34; :: {20D04FE0-3AEA-1069-A2D8-08002B30309D}&#34;则返回S_OK。或以&#34; \ tsclient \&#34;开头。 &#34; {:: 20D04FE0-3AEA-1069-A2D8-08002B30309D}&#34;表示PC(我的电脑)文件夹,&#34; \ tsclient \&#34;是共享驱动器的文件路径前缀。
谢谢,
更新(2017/09/07 11:54 JST) &#34;内容&#34; - &GT; &#34;驱动器和文件夹列表&#34;