mciSendString не будет воспроизводить аудиофайл, если путь слишком длинный

когда путь+имя файла действительно длинный, я заметил, что

PlaySound(fName.c_str(), NULL, SND_ASYNC);

работает, но не

mciSendString((L"open "" + fName + L"" type waveaudio alias sample").c_str(), NULL, 0, NULL);
mciSendString(L"play sample", NULL, 0, NULL);

пример неудачной команды:

открыть "C:qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksqdajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsdWindows критическая остановка.wav " введите образец псевдонима waveaudio

но:

  • Я действительно нужна mciSendString вместо PlaySound (), потому что PlaySound () не воспроизводит определенные файлы (48 khz audio files, иногда 24-bit files и т. д.)

  • мне нужно иметь возможность воспроизводить аудиофайлы с потенциально длинными путями, потому что у конечного пользователя моего приложения могут быть такие файлы

как заставить mciSendString принимать длинные имена файлов?


Примечания:

  • Я также пробовал с этим MSDN пример использования mciSendCommand, но это то же самое.

  • максимальный путь+длина файла 127 (127: рабочий, 128+: не работает)

  • если действительно это невозможно сделать mci* функции работают с более длинными, чем-127-char именами файлов, что я мог бы использовать вместо этого, только с winapi (без внешних библиотек)? (PlaySound - это не вариант, потому что не работает realiably с все wav файлы, такие как 48 кГц: нерабочий и т. д.)

4 ответов


предел 127 выглядит странно. Я не нашел никакой информации об этом в MSDN.

  1. существует альтернативный синтаксис для открытия: open waveaudio!right.wav

  2. опция, которую вы можете попробовать, - это изменить рабочий каталог на каталог файла, тогда ограничение применяется только к filename. -> SetCurrentDiectory

  3. для сокращения имени файла можно использовать функцию Winapi GetShortPathName
    Но:

    SMB 3.0 не поддерживает короткие имена на акциях с непрерывным возможность наличия.

    Resilient File System (ReFS) не поддерживает короткие имена. Если ты позвонишь ... GetShortPathName на пути, который не имеет коротких имен на диске, вызов будет успешным, но вместо этого вернет путь с длинным именем. Этот результат также возможен с томами NTFS, потому что нет гарантия того, что короткое имя будет существовать в течение заданного времени имя.

на основе примера из MSDN:

#include <string>
#include <Windows.h>

template<typename StringType>
std::pair<bool, StringType> shortPathName( const StringType& longPathName )
{
    // First obtain the size needed by passing NULL and 0.
    long length = GetShortPathName( longPathName.c_str(), NULL, 0 );
    if (length == 0) return std::make_pair( false, StringType() );

    // Dynamically allocate the correct size 
    // (terminating null char was included in length)
    StringType  shortName( length, ' ' );

    // Now simply call again using same long path.
    length = GetShortPathName( longPathName.c_str(), &shortName[ 0 ], length );
    if (length == 0) return std::make_pair( false, StringType() );

    return std::make_pair(true, shortName);
}


#include <locale>
#include <codecvt>

#include <iostream>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;

//std::string narrow = converter.to_bytes( wide_utf16_source_string );
//std::wstring wide = converter.from_bytes( narrow_utf8_source_string );

int main( int argc, char** argv )
{
    std::wstring myPath = converter.from_bytes( argv[0] );

    auto result = shortPathName( myPath );
    if (result.first)
        std::wcout << result.second ;


    return 0;
}

это ограничение устаревших возможностей MCI. Есть две проблемы, с которыми вы сталкиваетесь с использованием API MCI:

  1. путь слишком долго, и этот API не может обрабатывать длинные имена файлов. Ограничение, как правило, вокруг 260 символы как указано на странице.

  2. не все файлы имеют "короткое имя". начиная с Windows 7, так называемый 8.3 (FILENAME.EXT) создание файла может быть отключено. Этот означает, что не может быть пути, который GetShortPathName может вернуть, что позволит MCI получить доступ к файлу.

настоятельно рекомендуется заменить все это современным API. DirectDraw и Media Foundation, как упоминалось другими комментаторами, будет подходящей заменой.


Я отладил его (on mciSendCommand примеру). Проблема возникает, когда mwOpenDevice звонки mmioOpen:

winmm.dll!_mciSendCommandW@16
winmm.dll!mciSendCommandInternal
winmm.dll!mciSendSingleCommand
winmm.dll!_mciOpenDevice@12
winmm.dll!mciLoadDevice
    winmm.dll!_mciSendCommandW@16
    winmm.dll!mciSendCommandInternal
    winmm.dll!mciSendSingleCommand
    winmmbase.dll!_DrvSendMessage@16
    winmmbase.dll!InternalBroadcastDriverMessage
        mciwave.dll!_DriverProc@20
        mciwave.dll!_mciDriverEntry@16
        mciwave.dll!_mwOpenDevice@12
        winmmbase.dll!_mmioOpenW@12

здесь mmioOpen С MMIO_PARSE флаг для преобразования пути к файлу в полный путь к файлу. Согласно MSDN, это имеет ограничение:

буфер должен быть достаточно большим, чтобы содержать минимум 128 символов.

то есть буфер всегда считается длиной 128 байт. Надолго имена файлов, буфер оказывается недостаточным и mmioOpen возвращает ошибку, причинив mciSendCommand думать, что звуковой файл отсутствует, а вернуть MCIERR_FILENAME_REQUIRED.

к сожалению, поскольку он разрешает полный путь к файлу,SetCurrentDirectory не поможет.

так как проблема находится внутри драйвера MCI (mciwave.dll) я сомневаюсь, что есть способ заставить подсистему MCI обрабатывать длинный путь.


учитывая ваши ограничения (не могу изменить ни один из API), которые являются MCI вещь, я бы рассмотреть возможность копирования запрошенного в настоящее время файла в temp и играя там, в случае неудачи вашей первой попытки:

int result = mciSendString(...);
if (result > 0 ) { #See notes below to specify this
     CopyFile("C:\long\path.wav","C:\temp\play.wav",0); #windows.h
     result = mciSendString(...); #In c:/temp/play.wav
}

можно использовать mciGetErrorString on result чтобы найти точный код ошибки, который вы получаете-и сделать if конкретные, возможно, обработка других ошибок по-разному.. Я посмотрел на список ошибок но я не уверен.

не забудьте использовать DeleteFile из API Windows, Если вы создали новый файл. Я не знаю, хотите ли вы сохранить его до окончания программы или сразу после игры, поэтому решите, что имеет больше смысла.

вы также хотите проверить на успех CopyFile, если что-то пойдет не так. Тебе действительно нужно везде себя проверять.