CFileDialog根据Windows版本

时间:2017-09-07 01:36:15

标签: windows cfiledialog

我尝试在文件选择对话框中仅显示与远程桌面共享的驱动器。以下是我想要展示的内容:

但根据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(&currently_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(""));
    }

我做的是:

  1. 构建CFiledialog的实例。
  2. 通过CFiledialog :: GetIFileDialogCustomize()检索IFileDialogCustomize的实例。
  3. 从IFileDialogCustomize的isntance中检索了一个IFileDialog2的实例。
  4. 通过IFileDialog :: SetFilter()将IShellItemFilter的实例设置为文件对话框。
  5. 通过IFileDialog :: SetDefaultFolder()将默认文件夹设置为PC(我的电脑)文件夹。
  6. 通过IFileDialog :: SetNavigationRoot()将导航根目录设置为PC(我的电脑)文件夹。
  7. 如果当前文件夹不是共享驱动器或其后代文件夹,则通过IFileDialog :: SetFolder()将文件夹设置为PC(我的电脑)文件夹。
  8. 显示文件对话框。
  9. 我做了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;

0 个答案:

没有答案