Диалог OPENFILENAME возвращает азиатские буквы вместо пути к файлу

Я пытаюсь создать функцию, которая получает путь к файлу из 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;
}

Поместив точку останова в закомментированную строку, я смог проверить значение «src», а также «имя файла», которые на данный момент были для меня неидентифицируемыми буквами азиатского происхождения. Почему это происходит? Это проблема конверсии?

РЕДАКТИРОВАТЬ:

Благодаря быстрому ответу и нескольким комментариям код теперь полностью функционален. Спасибо Гансу Пассанту за очень прямое решение, а также огромное спасибо Коди Грэю за переписывание функции, объяснение ошибки, а также за урок о том, как ее следует обрабатывать. Поскольку я все еще делаю свои первые шаги по изучению винапи, эта информация будет полезна мне в будущих программах.

1

Решение

Это классическая ошибка, вызванная смешиванием типов символов ANSI и Unicode. Его отличительной чертой является появление случайных символов азиатского происхождения в строках, в точности как вы описали.

Быстрый взгляд на ваш код сразу обнаруживает проблему. Вы вызываете ANSI-версии функций Win32 —GetOpenFileNameAИ с использованием ANSI-версий структур данныхOPENFILENAMEA— ваш filename массив состоит из широких символов (wchar_t). В Win32 Aтипы с суффиксом всегда соответствуют стандарту ANSI и требуют использования 1 байта char типы. Wтипы с суффиксом всегда Unicode и требуют использования 2-байтовых wchar_t типы. Вы не можете смешать два без явного преобразования, например с помощью MultiByteToWideChar и / или WideCharToMultiByte,

Обратите внимание, что компилятор поймал бы эту ошибку несоответствия типов, если бы вы не использовали явные приведения для его закрытия.

В современную эпоху вы всегда должны использовать WAPI-интерфейсы, потому что вы всегда хотите поддерживать Unicode. Мир не ASCII, и кодировки Windows ANSI устарели, когда все перешли на Windows NT и Windows 98 / ME были мертвы и похоронены.

Поэтому переписайте ваш код следующим образом (я также позволил себе сменить некоторые другие идиомы и очистить код другими способами, как, например, использование языковых конструкций C ++ до нуля):

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 а также OPENFILENAME, Макросы в заголовочных файлах Windows обрабатывают разрешение.

Преимущество этого в том, что компилятор генерирует ошибку несоответствия типов, если вы забыли добавить префикс широких строк к L префикс, который является распространенной ошибкой, если вы еще не привыкли делать это. (Конечно, это предполагает, что вы также избавляетесь от дурной привычки использовать явные приведения для того, чтобы заставить замолчать предупреждения компилятора, потому что вы можете легко победить эту сеть безопасности, сделав это.)

4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]