在Windows上从同一对话框中选择文件或文件夹?

4

我希望允许Windows游戏的用户使用对话框选择文件夹文件。目前我可以使用SHBrowseForFolder或GetOpenFileName之一,但无法在同一个对话框中同时使用两个选项。这是否可能?(原因是我正在移植一个可以实现此功能的Mac游戏。)


1
SHBrowseForFolder带有BIF_BROWSEINCLUDEFILES参数,但在Vista及更高版本上会弹出一个丑陋的对话框。有两个菜单项,一个用于文件,一个用于文件夹,有什么问题吗? - Sheng Jiang 蒋晟
4个回答

4
当您打开文件对话框时,请给它一个默认的文件名,例如“整个文件夹”,并关闭需要文件存在才能启用“确定”按钮的标志。当对话框返回时,请检查文件名是否与特殊字符串“整个文件夹”匹配,并相应处理。
基于我对另一个问题的回答:如何配置 OpenFileDialog 来选择文件夹?

使用GetOpenFileName对我不起作用。一定是有适用于OpenFileDialog的方法。我正在使用C语言。是否可以直接从C中调用OpenFileDialog,还是必须创建具有C接口的C ++类? - Vern Jensen
@Vern,这真的很奇怪——当我写另一个答案时,我使用了一个内部使用GetOpenFileName的框架进行测试。稍后我会再试一次,看看问题出在哪里。 - Mark Ransom

1
如果您在OPENFILENAME结构体的Flags成员中设置了OFN_NOVALIDATE,那么在输入一个没有'\'结尾的文件夹名称并按下Enter(或Open)时,将会发送CDN_FILEOK通知到钩子过程,而不是CDN_FOLDERCHANGE通知。然后,您可以对文件夹路径进行任何操作。对我来说,这是一个错误,但它可能会对您有所帮助。

0

我知道这个帖子很旧,但最近我遇到了同样的问题。 我发现当点击“确定”按钮时,对话框会发出CDN_FOLDERCHANGE消息,但实际上文件夹并没有改变(与上次调用相同)。 因此,我想出了以下挂钩过程:

UINT_PTR CALLBACK openfilename_cb (
  HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  NMHDR *nmhdr;
  OFNOTIFY *ofnotify;
  static char dir_now[PATH_MAX], dir_prev[PATH_MAX];

  do_debug (5, "openfilename_cb(): hwnd=%p, msg=%d\n", hwnd, msg);
  switch (msg)
  {
    case WM_INITDIALOG:
      do_debug (2, "openfilename_cb(): WM_INITDIALOG\n");
      break;

    case WM_NOTIFY:
      ofnotify = (OFNOTIFY *)lParam;
      nmhdr = &ofnotify->hdr;
      do_debug (4,"openfilename_cb(): WM_NOTIFY, hwnd=%p, id=%d, code=%d\n",
        nmhdr->hwndFrom, nmhdr->idFrom, nmhdr->code);
      switch (nmhdr->code)
      {
        case CDN_INITDONE:
          do_debug (3, "openfilename_cb(): CDN_INITDONE\n");
          dir_prev[0] = '\0';
          break;

        case CDN_FILEOK:
          do_debug (3, "openfilename_cb(): CDN_FILEOK\n");
          break;

        case CDN_FOLDERCHANGE:
          do_debug (3, "openfilename_cb(): CDN_FOLDERCHANGE\n");
          SendMessage (nmhdr->hwndFrom, CDM_GETFOLDERPATH, sizeof(dir_now),
                                                            (LPARAM)dir_now);
          do_debug (3, "  directory=%s\n", dir_now);
          if (ofnotify->lpOFN->lCustData & FN_OPENDIR)
          {
            if (stricmp(dir_now, dir_prev) == 0)
            {
              /* user clicked the OK button */
              strncpy (ofnotify->lpOFN->lpstrFile, dir_now, PATH_MAX);
              ofnotify->lpOFN->lCustData = MAGIC;
              PostMessage (nmhdr->hwndFrom, WM_COMMAND, IDCANCEL, 0);
              do_debug (3, "  closing dialog\n");
              break;
            }
            strncpy (dir_prev, dir_now, sizeof(dir_prev));
          }
          break;

        case CDN_HELP:
          do_debug (3, "openfilename_cb(): CDN_HELP\n");
          break;
        case CDN_SELCHANGE:
          do_debug (3, "openfilename_cb(): CDN_SELCHANGE\n");
          break;

        case CDN_SHAREVIOLATION:
          do_debug (3, "openfilename_cb(): CDN_SHAREVIOLATION\n");
          break;

        case CDN_TYPECHANGE:
          do_debug (3, "openfilename_cb(): CDN_TYPECHANGE\n");
          break;
      }  /* switch (nmhdr->code) */
      break;  /* WM_NOTIFY */
  }  /*   switch (msg) */
  return 0;
}

确保在OPENFILENAME结构的Flags成员中设置OFN_ENABLEHOOK和OFN_EXPLORER位,以便调用钩子过程。当然,还要将lpfnHook成员设置为钩子函数的地址。

我发现使用钩子过程与否对话框的外观有很大不同(元素排序方式不同,在左窗格中有树形视图而不是大图标等等)。

你必须与调用函数来回通信,告诉它你想要/已经选择了一个目录。我使用OPENFILENAME结构的lCustData成员来实现这一点。

我在Windows 10版本19042.1052(64位)和Windows XP(32位)上进行了测试。

Johannes


0
据我所知,常见的对话框无法以一种模式运行,使您可以从同一个对话框中选择文件或文件夹。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接