如何在使用带有multiselect标志的GetOpenFileName()时获取所选文件的列表

How to get list of selected files when using GetOpenFileName() with multiselect flag?

本文关键字:获取 列表 文件 GetOpenFileName 标志 multiselect      更新时间:2023-10-16

我试过在谷歌上搜索,但人们似乎也有同样的问题:我们无法获得所选文件的列表。

这是一段简单的工作代码,与我使用的代码类似:

OPENFILENAME ofn = { sizeof ofn };
wchar_t file[1024];
file[0] = '';
ofn.lpstrFile = file;
ofn.nMaxFile = 1024;
ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER;
GetOpenFileName(&ofn);

如何实际获得我选择的文件名?目前,我只能在没有OFN_ALLOWMULTISELECT标志的情况下让它工作,所以它将一个选定的文件名返回到ofn.lpstrFile中。我试图打印出该结构中的所有字符串变量,但一无所获。它只显示所选文件的主文件夹。

看起来ofn.lpstrFile包含所有文件名,以NULL分隔,并以另一个NULL结尾(实际上以空字符串结尾)。

如果设置了OFN_ALLOWMULTISELECT标志,并且用户选择了多个文件,则缓冲区将包含当前目录和所选文件的文件名。对于资源管理器样式的对话框,目录和文件名字符串以NULL分隔,最后一个文件名后面有一个额外的NULL字符对于旧式对话框,字符串以空格分隔,函数使用短文件名作为带空格的文件名。您可以使用FindFirstFile函数在长文件名和短文件名之间进行转换如果用户只选择一个文件,则lpstrFile字符串的路径和文件名之间没有分隔符

来自MSDN

解析内容的可能实现可以是:;

wchar_t* str = ofn.lpstrFile;
std::wstring directory = str;
str += ( directory.length() + 1 );
while ( *str ) {
  std::wstring filename = str;
  str += ( filename.length() + 1 );
  // use the filename, e.g. add it to a vector
}

检查文件扩展名可能不可靠,因为如果用户没有输入文件扩展名(只输入点,如"file."),它也可能为0。我认为要区分单文件和多文件选择,必须检查nFileOffset-1位置是否有空字符(终止符)。

试试这个:

wchar_t file[1025] = {0}; // room for an extra null terminator, just in case
...
ofn.nMaxFile = 1024;
...
wchar_t* ptr = ofn.lpstrFile;
ptr[ofn.nFileOffset-1] = 0;
std::wcout << L"Directory: " << ptr << std::endl;
ptr += ofn.nFileOffset;
while (*ptr)
{
    std::wcout << L"File: " << ptr << std::endl;
    ptr += (lstrlenW(ptr)+1);
}

如果在使用OFN_ALLOWMULTISELECT时选择单个文件,则nFileExtension字段包含扩展名的偏移量。如果选择多个文件,则"文件扩展名"字段包含0。

通过这种方式,您可以确定是否选择了单个文件,只需读取/复制lpstrFile字段指向的缓冲区(这将是一个以null结尾的字符串,包含完整路径和文件名,包括扩展名)

或者,如果选择了多个文件,则解析lpstrFile字段指向的缓冲区,首先使用nFileOffset读取/复制文件夹(例如,使用lstrcpyn并指定要读取的长度作为nFilesOffset值),然后从nFileOffsets读取/复制到下一个null,即file1字符串,添加文件字符串长度+1以获得下一个位置来读取/复制下一个文件字符串等,直到您到达以null开头的文件字符串-这是所有文件结尾的双null(作为以null结尾之前的最后一个字符串)

以下是Niall和Remy的答案的更完整版本。

vector<string> &filePaths;
if ( GetOpenFileName( &ofn ) == TRUE )
{
    wchar_t *p = ofn.lpstrFile;
    wstring path = p;
    p += path.size() + 1;
    if ( *p == 0 )
    {
        // there is only one string, being the full path to the file
        filePaths.push_back( ConvertWideCharToUtf8( path.c_str() ) );
    }
    else
    {
        // multiple files follow the directory
        for ( ; *p != 0 ; )
        {
            wstring fileName = p;
            filePaths.push_back( ConvertWideCharToUtf8( ( path + L"\" + fileName ).c_str() ) );
            p += fileName.size() + 1;
        }
    }
}

我们还有一个功能:

string ConvertWideCharToUtf8( const wchar_t *wideText )
{
    int len = WideCharToMultiByte( CP_UTF8, 0, wideText, -1, NULL, 0, NULL, NULL );
    char *buffer = (char *)malloc( len );
    WideCharToMultiByte( CP_UTF8, 0, wideText, -1, buffer, len, NULL, NULL );
    string s = buffer;
    free( buffer );
    return s;
}