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);

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

open «C: \ qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq \ dajdjqjdlqjdlkjazejoizajoijoifjoifjifsfjsfszjfoijdsjfoijsjof

Но:

  • Мне действительно нужно mciSendString вместо PlaySound (), потому что PlaySound () не воспроизводит определенные файлы (аудиофайлы 48 кГц, иногда 24-битные файлы и т. Д.)

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

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


Заметки:

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

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

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

6

Решение

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

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

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

  3. Чтобы сократить имя файла, можно использовать функцию Winapi GetShortPathName
    Но:

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

    Эластичная файловая система (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;
}
4

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

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

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

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

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

2

Я отладил это (на 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 обрабатывать длинный путь.

2

Учитывая ваши ограничения (не могут изменить 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 на result найти точный код ошибки, который вы получаете — и сделать if конкретные, возможно, обрабатывать другие ошибки по-другому .. Я смотрел на список ошибок но я не уверен

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

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

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