OPENFILENAME 对话框 returns 亚洲字母而不是文件路径

OPENFILENAME dialog returns Asian letters instead of file path

我正在尝试创建一个从 OPENFILENAME 对话框获取文件路径的函数。我的代码看起来像这样。

wstring src;

bool open()
{
    const string title  = "Select a File";

    wchar_t filename[MAX_PATH];

    OPENFILENAMEA ofn;
    ZeroMemory(&filename, sizeof(filename));
    ZeroMemory(&ofn, sizeof(ofn));

    ofn.lStructSize     = sizeof(ofn);
    ofn.hwndOwner       = NULL; 
    ofn.lpstrFilter     = "Music (.mp3)[=11=]*.mp3[=11=]All[=11=]*.*[=11=]";
    ofn.lpstrFile       = LPSTR(filename);
    ofn.nMaxFile        = MAX_PATH;
    ofn.lpstrTitle      = title.c_str();
    ofn.Flags           = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;

    if (GetOpenFileNameA(&ofn))
    {
        src = filename;    //<----------Save filepath in global variable
        return true;
    }
    return false;
}

在注释行放置断点后,我可以检查 'src' 和 'filename' 的值,此时对我来说,它们是无法识别的亚洲起源字母。为什么会这样?这是转换问题吗?

编辑:

多亏了快速回复和一些评论,代码现在可以正常运行了。感谢 Hans Passant 提供了一个非常直接的解决方案,也非常感谢 Cody Gray 重写了函数,解释了错误,并给我上了一堂应该如何处理的课。由于我仍处于学习 winapi 的第一步,这些信息将对我以后的程序有很大帮助。

这是一个经典的错误,由混合 ANSI 和 Unicode 字符类型引起。它的特点是字符串中出现随机 Asian-origin 个字符,正如您所描述的那样。

快速浏览一下您的代码即可立即发现问题所在。您正在调用 Win32 函数的 ANSI 版本——GetOpenFileNameA——并使用数据结构的 ANSI 版本——OPENFILENAMEA——但是你的 filename 数组由宽字符组成(wchar_t).在 Win32 中,A 后缀的类型总是 ANSI 并且需要使用 1 字节的 char 类型。 W 后缀类型始终是 Unicode 并且需要使用 2 字节 wchar_t 类型。如果没有显式转换,您不能将两者混合使用,例如 使用 MultiByteToWideChar and/or WideCharToMultiByte.

请注意,如果您没有使用显式强制转换来关闭它,编译器会捕获此类型不匹配错误。

在现代,您应该始终使用 W 后缀的 API,因为您总是希望支持 Unicode。世界不是 ASCII,当每个人都切换到 Windows NT 并且 Windows 98/ME 已死并被埋葬时,Windows ANSI 编码已过时。

所以按如下方式重写您的代码(我还冒昧地更改了一些其他习语并以其他方式清理了代码,例如使用 C++ 语言结构将内存归零):

std::wstring src;

bool open()
{
    const std::wstring title = L"Select a File";
    std::wstring filename(MAX_PATH, L'[=10=]');

    OPENFILENAMEW ofn = { };
    ofn.lStructSize     = sizeof(ofn);
    ofn.hwndOwner       = NULL; 
    ofn.lpstrFilter     = L"Music (.mp3)[=10=]*.mp3[=10=]All[=10=]*.*[=10=]";
    ofn.lpstrFile       = &filename[0];  // use the std::wstring buffer directly
    ofn.nMaxFile        = MAX_PATH;
    ofn.lpstrTitle      = title.c_str();
    ofn.Flags           = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST;

    if (GetOpenFileNameW(&ofn))
    {
        src = filename;    //<----------Save filepath in global variable
        return true;
    }
    return false;
}

为避免每次都显式键入 W,请确保为您的项目全局定义 UNICODE_UNICODE。最好的方法是使用项目属性,您可以在其中预定义这些符号。否则,您可以在预编译 header 的顶部定义它们。这确保 W 函数和类型的后缀变体 总是 使用,即使您省略了后缀。所以你可以简单地说 GetOpenFileNameOPENFILENAME。 Windows header 文件中的宏处理分辨率。

这样做的好处是,如果您忘记为 wide-string 文字加上 L 前缀,它会导致编译器生成类型不匹配错误,如果您是还没有这样做的习惯。 (当然,这假设你也改掉了使用显式强制转换来消除编译器警告的坏习惯,因为这样做很容易破坏这个安全网。)