【问题标题】:CFileDialog with OFN_ALLOWMULTISELECT incorrect results for shortcuts带有 OFN_ALLOWMULTISELECT 的 CFileDialog 快捷方式的结果不正确
【发布时间】:2019-06-15 08:45:44
【问题描述】:

有人可以告诉我我在这里做错了什么吗?

MFC 项目,我使用CFileDialog 让用户选择多个文件,例如:

CFileDialog fd(TRUE, NULL, NULL,
    OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ALLOWMULTISELECT, 
    NULL, this);

if(fd.DoModal() == IDOK)
{
    //Multi-selection
    CString strPaths;
    POSITION fileNamesPosition = fd.GetStartPosition();

    while(fileNamesPosition != NULL)
    {
        if(!strPaths.IsEmpty())
            strPaths += L"\n";

        strPaths += fd.GetNextPathName(fileNamesPosition);
    }  

    AfxMessageBox(strPaths);
}

所以如果说,有两个快捷方式文件:

shortcut_1.lnk 引用的文件:"D:\Folder\Project_B\Release\Name of Project B.exe"

shortcut_2.lnk指的是"D:\Folder\Project_A\Release\Name of Project A.exe"

如果我从上面代码生成的“文件打开”对话框中选择它们,我得到的strPaths 变为以下,这是不正确的:

D:\Folder\Project_A\Release\Name of Project A.exe
D:\Folder\Project_A\Release\Name of Project B.exe

第二条路径错了!

【问题讨论】:

    标签: c++ winapi mfc cfiledialog


    【解决方案1】:

    使用GetStartPosition()GetNextPathName() 函数是一团糟。一方面,他们使用旧式 API which depends on a correct return buffer size defined via OPENFILENAME struct。 MFC 处理这个!正如您的问题所示,即使缓冲区足够大,链接也存在问题。

    不用担心,请使用Vista+ API,可通过CFileDialog::GetIFileOpenDialog() 获得。

    这是一个工作代码示例:

    CFileDialog fd( TRUE, NULL, NULL,
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ALLOWMULTISELECT,
        NULL, nullptr );
    
    if (fd.DoModal() == IDOK)
    {
        //Multi-selection
        CString strPaths;
    
        CComPtr<IFileOpenDialog> piod = fd.GetIFileOpenDialog();
        ASSERT( piod );
    
        CComPtr<IShellItemArray> pResults;
        if( SUCCEEDED( piod->GetResults( &pResults ) ) )
        {
            DWORD count = 0; pResults->GetCount( &count );
            for( DWORD i = 0; i < count; ++i )
            {
                CComPtr<IShellItem> pItem;
                if( SUCCEEDED( pResults->GetItemAt( i, &pItem ) ) )
                {
                    CComHeapPtr<wchar_t> pPath;
                    if( SUCCEEDED( pItem->GetDisplayName( SIGDN_FILESYSPATH, &pPath ) ) )
                    {
                        if( !strPaths.IsEmpty() )
                            strPaths += L"\n";
                        strPaths += pPath;
                    }
                }
            }
        }
    
        AfxMessageBox( strPaths );
    }
    

    【讨论】:

    • 谢谢!一些笔记重新。你的代码。 1) 如果您的piod 为空,我将使用我最初提供的代码作为备用代码。否则它将在 WinXP 上崩溃。 2)小抱怨。我会在for 循环中使用DWORD i 来匹配count 类型以防止出现警告。和 3) 我可以说我的每一寸身体绝对讨厌 COM!真是太可恶了。例如,如果这五个 COM 函数中的任何一个失败,我该怎么办?
    • @c00000fd 1) 确保还为 XP 分配了足够大的返回缓冲区(我在第 1 段中添加了一个链接),否则用户在选择太多文件时会遇到问题。 2)谢谢,已修复。 3) 你对任何错误做你所做的,报告它,记录它,任何合适的。就我个人而言,我会将这些东西包装在一个返回 std::vector&lt;std::wstring&gt; 的函数中,并抛出一个包含 HRESULT 和失败函数名称的 std::system_error。这些函数失败应该是非常罕见的,可能只是内存不足的情况。
    • 是的,感谢您的跟进。我已经为 fd.m_ofn.lpstrFilefd.m_ofn.nMaxFile 变量中的旧 XP 缓冲区预先分配了 32767 wchar_t。只是它没有返回正确的结果——这就是问题所在。但我猜如果有人还在使用它,这个 bug 会一直存在于 XP 中。
    • @c00000fd 否则 Remy 有一个可能的解决方法,它也应该在 XP 下工作。
    • @c00000fd 幸运的是我的雇主不再支持 XP。对于私人项目,我将从 Windows 7 作为最旧的支持操作系统开始。
    【解决方案2】:

    听起来像是CFileDialog 中的一个错误。

    通常,返回的路径是当前显示的目录路径和所选文件名的串联。在 lnk 文件的情况下,CFileDialog 可能只是提取目标文件名并将其连接到 lnk 文件的父文件夹的路径,而不是仅返回 @ 内的完整目标路径987654326@ 文件。如果没有看到CFileDialog 的实际源代码,很难确定。

    为避免这种行为,您可以在调用对话框时包含 OFN_NODEREFERENCELINKS 标志,以便获得实际 lnk 文件的完整路径,然后您可以在对话框之后使用 IShellLink 手动解析它们的目标已被解雇。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-05
      • 2014-01-21
      • 2020-05-09
      • 1970-01-01
      相关资源
      最近更新 更多