Диалог 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 префикс, который является распространенной ошибкой, если вы еще не привыкли делать это. (Конечно, это предполагает, что вы также избавляетесь от дурной привычки использовать явные приведения для того, чтобы заставить замолчать предупреждения компилятора, потому что вы можете легко победить эту сеть безопасности, сделав это.)

Другие вопросы по тегам