CFileDialog的钩子函数解决对话框的多选之DoModal问题


在使用MFC(Microsoft Foundation Classes)中的`CFileDialog`时,默认情况下它不支持多选功能。但是,你可以通过自定义钩子函数(Hook Procedure)来修改对话框的行为,虽然这种方法比较复杂且不是直接支持的。然而,更常见且直接的方法是使用`IFileOpenDialog`接口,这是Windows Vista及以后版本中推荐的用于打开和保存文件对话框的接口,它支持多选。

以下是一个使用`IFileOpenDialog`接口实现文件多选的示例,而不是修改`CFileDialog`的DoModal方法(因为`CFileDialog`本身不直接支持多选):


#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <iostream>
#include <vector>

// 辅助函数,用于从IShellItemArray获取文件路径
void GetFilesFromShellItemArray(IShellItemArray* psiItemArray, std::vector<std::wstring>& files)
{
    DWORD dwNumItems = 0;
    HRESULT hr = psiItemArray->GetCount(&dwNumItems);
    if (SUCCEEDED(hr))
    {
        for (DWORD i = 0; i < dwNumItems; ++i)
        {
            IShellItem* psi;
            hr = psiItemArray->GetItemAt(i, &psi);
            if (SUCCEEDED(hr))
            {
                LPWSTR pszFilePath;
                hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
                if (SUCCEEDED(hr))
                {
                    files.push_back(pszFilePath);
                    CoTaskMemFree(pszFilePath);
                }
                psi->Release();
            }
        }
    }
}

// 函数用于打开文件对话框并支持多选
bool OpenFileDialogWithMultiSelect(HWND hwndOwner, std::vector<std::wstring>& files)
{
    IFileOpenDialog* pFileOpen;
    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

    if (SUCCEEDED(hr))
    {
        pFileOpen->SetOptions(FOS_ALLOWMULTISELECT | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST);

        if (hwndOwner)
        {
            pFileOpen->SetOwner(hwndOwner);
        }

        hr = pFileOpen->Show(NULL);

        if (SUCCEEDED(hr))
        {
            IShellItemArray* psiResult;
            hr = pFileOpen->GetResults(&psiResult);

            if (SUCCEEDED(hr))
            {
                GetFilesFromShellItemArray(psiResult, files);
                psiResult->Release();
            }
        }

        pFileOpen->Release();
    }

    return SUCCEEDED(hr);
}

int main()
{
    std::vector<std::wstring> selectedFiles;
    if (OpenFileDialogWithMultiSelect(NULL, selectedFiles))
    {
        for (const auto& file : selectedFiles)
        {
            std::wcout << file << std::endl;
        }
    }
    else
    {
        std::wcout << L"No files selected." << std::endl;
    }

    return 0;
}

在这个示例中,我们使用了`IFileOpenDialog`接口来创建一个支持多选的打开文件对话框。用户选择的文件路径会被存储在`std::vector`中。这是处理文件多选的一种更为现代和推荐的方式。