2016-11-27 7 views
1

Я пытаюсь создать функцию, которая получает путь к файлу из диалогового окна 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», а также «имени файла», который в этот момент были, ко мне, неопознанных письма азиатского происхождения. Почему это происходит? Это проблема конверсии?

EDIT:

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

+0

Попробуйте 'char filename [MAX_PATH];' вместо wchar_t –

+0

Листинг LPSTR довольно злой, что не позволило компилятору сказать вам, что вы делаете это неправильно. Однако это не помешало вам сделать это неправильно. Вместо этого используйте OPENFILENAMEW и GetOpenFileNameW, используйте L "blabla" для генерации широкого строкового литерала. –

+0

«GetOpenFileNameA» - это подфункция ANSI «GetOpenFileName», поэтому вы должны проверить, что ваш код не создан как Unicode. И вы должны использовать «GetOpenFileName» вместо «GetOpenFileNameA», выбор хорошей подфункции будет выполнен автоматически. – Gwen

ответ

3

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

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

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

В современную эпоху вы всегда должны использовать API-интерфейсы W, потому что вы всегда хотите поддерживать Unicode. Мир не ASCII, и кодировки ANSI Windows стали устаревшими, когда все переключались на 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 определены во всем мире для вашего проекта. Лучший способ сделать это - использовать свойства проекта, где вы можете предопределить эти символы. В противном случае вы можете определить их в верхней части своего предварительно скомпилированного заголовка. Это гарантирует, что W -выполненные варианты функций и типов всегда используются, даже если вы опускаете суффикс. Поэтому вы можете просто сказать GetOpenFileName и OPENFILENAME. Макросы в файлах заголовков Windows обрабатывают разрешение.

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

+0

Очень информативное объяснение того, как возникла ошибка, как решить проблему, и указать, почему это решение должно быть предложено выше других. Большое спасибо. – qwarten

 Смежные вопросы

  • Нет связанных вопросов^_^