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
函数和类型的后缀变体 总是 使用,即使您省略了后缀。所以你可以简单地说 GetOpenFileName
和 OPENFILENAME
。 Windows header 文件中的宏处理分辨率。
这样做的好处是,如果您忘记为 wide-string 文字加上 L
前缀,它会导致编译器生成类型不匹配错误,如果您是还没有这样做的习惯。 (当然,这假设你也改掉了使用显式强制转换来消除编译器警告的坏习惯,因为这样做很容易破坏这个安全网。)
我正在尝试创建一个从 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
函数和类型的后缀变体 总是 使用,即使您省略了后缀。所以你可以简单地说 GetOpenFileName
和 OPENFILENAME
。 Windows header 文件中的宏处理分辨率。
这样做的好处是,如果您忘记为 wide-string 文字加上 L
前缀,它会导致编译器生成类型不匹配错误,如果您是还没有这样做的习惯。 (当然,这假设你也改掉了使用显式强制转换来消除编译器警告的坏习惯,因为这样做很容易破坏这个安全网。)