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

时间:2016-11-27 10:41:43

标签: c++ getopenfilename

我正在尝试创建一个从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)\0*.mp3\0All\0*.*\0";
    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;
}

在注释行放置一个断点,我可以检查&#39; src&#39;以及&#39;文件名&#39;在这一点上,对我来说,是无法识别的亚洲裔来信。为什么会这样?这是转换问题吗?

编辑:

感谢快速回复和一些评论,代码现已完全正常运行。感谢Hans Passant提供了一个非常直接的解决方案,同时也非常感谢Cody Gray重写功能,解释错误,以及如何处理它。由于我仍然迈出了学习winapi的第一步,这些信息将在未来的课程中很好地为我服务。

1 个答案:

答案 0 :(得分:4)

这是混合ANSI和Unicode字符类型导致的经典错误。它的标志是字符串中随机的亚洲原始字符的出现,正如您所描述的那样。

快速浏览一下代码会立即显示问题。您正在调用Win32函数的ANSI版本 - GetOpenFileNameA - 并使用ANSI版本的数据结构 - OPENFILENAMEA - 但您的filename数组由宽字符组成({{1 }})。在Win32中,wchar_t - 后缀类型始终为ANSI,并且需要使用1字节A类型。 char - 后缀类型始终为Unicode,需要使用2字节W类型。如果没有使用wchar_t和/或MultiByteToWideChar进行明确转换,例如,则无法将两者混合使用。

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

在现代,您应该始终使用WideCharToMultiByte - 后缀API,因为您始终希望支持Unicode。世界不是ASCII,当所有人都切换到Windows NT和Windows 98 / ME时,Windows ANSI编码已经过时了。

所以重写你的代码如下(我也冒昧地改变其他一些习惯用法并以其他方式清理代码,比如使用C ++语言结构来零内存):

W

为避免每次都明确键入std::wstring src; bool open() { const std::wstring title = L"Select a File"; std::wstring filename(MAX_PATH, L'\0'); OPENFILENAMEW ofn = { }; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFilter = L"Music (.mp3)\0*.mp3\0All\0*.*\0"; 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; } ,请确保为项目全局定义WUNICODE。执行此操作的最佳方法是使用项目属性,您可以在其中预定义这些符号。否则,您可以在预编译头的顶部定义它们。这可以确保使用_UNICODE - 后缀的函数和类型变体始终,即使省略后缀也是如此。所以你可以简单地说WGetOpenFileName。 Windows头文件中的宏处理分辨率。

这样做的好处是,如果您忘记为带有OPENFILENAME前缀的宽字符串文字添加前缀,则会导致编译器生成类型不匹配错误,如果您尚未使用前缀,这是一个常见的错误这样做的习惯。 (当然,这假设您也摆脱了使用显式强制转换来消除编译器警告的坏习惯,因为您可以通过这样做轻松击败此安全网。)