我正在尝试创建一个从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的第一步,这些信息将在未来的课程中很好地为我服务。
答案 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;
}
,请确保为项目全局定义W
和UNICODE
。执行此操作的最佳方法是使用项目属性,您可以在其中预定义这些符号。否则,您可以在预编译头的顶部定义它们。这可以确保使用_UNICODE
- 后缀的函数和类型变体始终,即使省略后缀也是如此。所以你可以简单地说W
和GetOpenFileName
。 Windows头文件中的宏处理分辨率。
这样做的好处是,如果您忘记为带有OPENFILENAME
前缀的宽字符串文字添加前缀,则会导致编译器生成类型不匹配错误,如果您尚未使用前缀,这是一个常见的错误这样做的习惯。 (当然,这假设您也摆脱了使用显式强制转换来消除编译器警告的坏习惯,因为您可以通过这样做轻松击败此安全网。)