CFolderPickerDialog - 没有 MFC

Posted

技术标签:

【中文标题】CFolderPickerDialog - 没有 MFC【英文标题】:CFolderPickerDialog - No MFC 【发布时间】:2016-07-07 12:32:49 【问题描述】:

我正在尝试确定是否可以在不使用 MFC 的情况下使用或重新创建 CFolderPickerDialog 对话框,或者是否有过尝试。到目前为止,我没有找到很多提示。 This old question 似乎也对我没有帮助。

我目前使用SHBrowseForFolder 打开普通文件夹对话框。但我需要一个 Explorer 风格的对话框。

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

#include <afxdlgs.h> 需要 MFC。 我不能在这个特定项目中使用 MFC。

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

【问题讨论】:

你为什么不看一下MFC源代码?它会告诉你如何在没有 MFC 的情况下做到这一点。 How to use IFileDialog with FOS_PICKFOLDER while still displaying file names in the dialog的可能重复 msdn.microsoft.com/en-us/library/windows/desktop/bb776913.aspx MFC 版本有问题。有时它不会恢复原始窗口的标题栏。请改用上面的链接。请注意,在调用GetDisplayName 之后,您必须使用CoTaskMemFree 释放wchar_t* 缓冲区 【参考方案1】:

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

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

在这种情况下,相关的 SDK 文档是here。现代版本的 Windows(自 Vista 起)使用 Common Item Dialog API 来显示打开/保存文件/文件夹对话框。 API 包含一个基本的IFileDialog 接口,以及两个子接口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("");

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

【讨论】:

感谢您的详细回答和 MSDN 链接。顺便说一句:在调用CoCreateInstance 之前,需要先调用CoInitializeEx 是的,没错。通常,该代码位于应用程序的初始化方法中,因为它只需要调用一次。大概这不是 Windows 应用程序使用的唯一 COM API。 与我的想象相比真的很复杂:(

以上是关于CFolderPickerDialog - 没有 MFC的主要内容,如果未能解决你的问题,请参考以下文章

linux下tomcat启动没有日志,没有进程,没有报错,没有监听端口

程序崩溃, 没有任何提示!没有异常!没有任何错误日志。。。

为啥 HKSample 数组对于一个没有限制、没有谓词、没有锚点的 HKAnchoredObjectQuery 总是有 1 个值?

为啥 HKSample 数组对于一个没有限制、没有谓词、没有锚点的 HKAnchoredObjectQuery 总是有 1 个值?

Eclipse C++ 问题:没有二进制文件(但没有编译器错误),没有类(但在类层次结构中没有条目)

PLSQL提示没有登录ORA-01012:没有登录