c++ - CFolderPickerDialog - 无 MFC

标签 c++ winapi visual-studio-2015 mfc

我试图弄清楚是否可以使用或重新创建 CFolderPickerDialog 不使用 MFC 的对话框,或者是否已尝试过。到目前为止我还没有找到很多提示。 This old question似乎对我也没有帮助。

我当前打开普通文件夹对话框 SHBrowseForFolder 。但我需要一个资源管理器风格的对话框。

这是来自另一个应用程序的资源管理器样式对话框 (MFC):

Image

#include <afxdlgs.h>需要MFC。 我无法在此特定项目中使用 MFC。

有没有办法在不使用 MFC 的情况下做到这一点?

最佳答案

老实说,我什至不知道 MFC 已经封装了这个。我的类库有它自己的实现。并且,如 Barmak points out ,MFC 实现甚至可能有错误,并且肯定有使用注意事项,如果您没有仔细阅读文档,这些注意事项就不会很明显。

也就是说,一般来说,使用库中已经包含的功能是个好建议,因为这会让您的生活更轻松。如果您不想使用整个库,但仍想了解它如何实现特定功能,则可以检查该库的源代码。 MFC 提供了引用源,以便您可以轻松地完成此操作(也可以调试它)。尽管直接从 MFC 中复制和粘贴代码可能会违反许可证(这也几乎是不可能的,因为它使用了许多 MFC 特定的习惯用法),但您可以查看代码以了解它们的含义。正在做,然后返回到 Windows SDK 文档来弄清楚如何自己编写代码。

本例中,相关的SDK文档为here 。现代版本的 Windows(自 Vista 起)使用通用项对话框 API 来显示打开/保存文件/文件夹对话框。该 API 由一个基本 IFileDialog 接口(interface)以及两个子接口(interface) IFileOpenDialogIFileSaveDialog 组成。这里有很多灵活性;详细信息以及示例代码都在文档中。

请注意,“通用项目对话框”仅在 Windows Vista 及更高版本上可用。如果您需要支持较旧的操作系统(我仍然支持 Windows XP),则需要后备。 SHBrowseForFolder对话框就是后备方案。它当然有其设计缺陷,但总比没有好。

如果您想要一个简单的文件夹选择器对话框,这里是我使用的代码的近似值。它使用一些 ATL/MFC 类型,例如 CStringCComPtr 包装类,但您可以将其转换为您自己选择的替代类(例如 std::wstring_com_ptr_t)。它显示一个简单的浏览文件夹对话框,适合当前操作系统,并具有调用者指定的标题和起始路径。如果成功,则返回一个字符串,其中包含用户所选文件夹的路径;否则,它返回一个空字符串。

namespace
{
   HRESULT Downlevel_SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx* pbc, REFIID riid, void** ppv)
   {
      _ASSERTE(IsWinVistaOrLater());

      HRESULT hResult = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
      const HINSTANCE hinstLib = GetModuleHandle(TEXT("shell32"));
      if (hinstLib)
      {
         typedef HRESULT (WINAPI * pfSHCreateItemFromParsingName)(PCWSTR, IBindCtx*, REFIID, void**);
         const pfSHCreateItemFromParsingName pf = reinterpret_cast<pfSHCreateItemFromParsingName>(GetProcAddress(hinstLib, _CRT_STRINGIZE(SHCreateItemFromParsingName)));
         if (pf)
         {
            hResult = pf(pszPath, pbc, riid, ppv);
         }
      }
      return hResult;
   }

   int CALLBACK BrowseForFolderCallbackProc(HWND hWnd, UINT uMsg, LPARAM /* lParam */, LPARAM lData)
   {
      if (uMsg == BFFM_INITIALIZED)
      {
         // Start with BFFM_SETSELECTION, which is always available.
         SendMessage(hWnd, BFFM_SETSELECTION, TRUE, lData);

      #ifdef UNICODE
         // If possible, also try to use BFFM_SETEXPANDED, which was introduced with
         // version 6.0 of the shell (Windows XP).
         SendMessage(hWnd, BFFM_SETEXPANDED, TRUE, lData);

         // You can also set the caption for the dialog's "OK" button here, if you like
         // (e.g., by loading a string from a resource).
         //SendMessage(hWnd,
         //            BFFM_SETOKTEXT,
         //            0,
         //            reinterpret_cast<LPARAM>(pszOKBtnCaption));
      #endif  // UNICODE
      }
      return 0;
   }
}

CString ShowFolderBrowserDialog(HWND hwndOwner, const CString& strDlgTitle, const CString& strStartPath)
{
   if (IsWinVistaOrLater())
   {
      CComPtr<IFileOpenDialog> pFileOpenDlg;
      if (SUCCEEDED(pFileOpenDlg.CoCreateInstance(__uuidof(FileOpenDialog))))
      {
        if (SUCCEEDED(pFileOpenDlg->SetTitle(strDlgTitle)))
        {
            FILEOPENDIALOGOPTIONS options;
            if (SUCCEEDED(pFileOpenDlg->GetOptions(&options)))
            {
               if (SUCCEEDED(pFileOpenDlg->SetOptions(options | FOS_PATHMUSTEXIST | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM)))
               {
                  CComPtr<IShellItem> psiStartPath;
                  if (SUCCEEDED(Downlevel_SHCreateItemFromParsingName(static_cast<const TCHAR*>(strStartPath),
                                                                      NULL,
                                                                      IID_PPV_ARGS(&psiStartPath))))
                  {
                     if (SUCCEEDED(pFileOpenDlg->SetFolder(psiStartPath)))
                     {
                        if (SUCCEEDED(pFileOpenDlg->Show(hwndOwner)))
                        {
                           CComPtr<IShellItem> pShellItemResult;
                           pFileOpenDlg->GetResult(&pShellItemResult);
                           CComHeapPtr<TCHAR> pszSelectedItem;
                           if (SUCCEEDED(pShellItemResult->GetDisplayName(SIGDN_FILESYSPATH, &pszSelectedItem)))
                           {
                              return pszSelectedItem;
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }
   else
   {
      TCHAR szBuffer[MAX_PATH + 1];
      szBuffer[0] = TEXT('\0');

      BROWSEINFO bi;
      bi.hwndOwner      = hwndOwner;
      bi.pidlRoot       = nullptr;
      bi.pszDisplayName = szBuffer;
      bi.lpszTitle      = strDlgTitle;
      bi.ulFlags        = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE | BIF_SHAREABLE | BIF_NONEWFOLDERBUTTON;
      bi.lpfn           = BrowseForFolderCallbackProc;
      bi.lParam         = reinterpret_cast<LPARAM>(static_cast<const TCHAR*>(strStartPath));

      CComHeapPtr<ITEMIDLIST> pidl(SHBrowseForFolder(&bi));
      if (pidl && SHGetPathFromIDList(pidl, szBuffer))
      {
         return pszSelectedItem;
      }
   }
   return TEXT("");
}

该对话框仅显示文件系统中的实际文件夹。尽管公共(public)项对话框 API 支持其他类型的特殊文件夹和命名空间,但我的应用程序中不需要这些,因此我的代码不处理复杂性。如果您需要更多功能,请将此作为起点以及文档。最值得注意的方面可能是 SHCreateItemFromParsingName 的使用(我已将其包装在动态调用中,以便代码继续在较旧的操作系统上运行)将调用者指定的起始路径(这是一个字符串)转换为 Shell 项对象(根据公共(public)项对话框 API 的要求) )。

关于c++ - CFolderPickerDialog - 无 MFC,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38245583/

相关文章:

c++ - c++中的线程,不能使用参数

windows - 获取 Windows 版本?

c# - FindResource 无法与位图一起使用

c++ - 为什么有 C++14/17 的网络库提案?

c++ - 为什么我的 long long 在整数除法上溢出/变成负值?

c++ - 在这里进行 const 转换安全吗?

windows - 如何查找发生运行时错误的行 - Visual C++

c# - 在 Visual Studio Community 2015 RC 中编译 VSIX 项目时出现问题

c# - VS2015升级后的垃圾回收和Parallel.ForEach问题

c++ - 错误:数组下标高于 std::vector::insert 的数组边界