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 кГц: нерабочий и т. д.)
4 ответа
Предел 127 выглядит странно. Я не нашел никакой информации на MSDN об этом.
Существует альтернативный синтаксис для открытия:
open waveaudio!right.wav
Вариант, который вы можете попробовать, это изменить рабочий каталог на каталог файла, тогда ограничение будет применяться только к имени файла. ->
SetCurrentDiectory
Чтобы сократить имя файла, можно использовать функцию 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;
}
Я отладил это (на 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 обрабатывать длинный путь.
Это ограничение унаследованных возможностей MCI. Есть две проблемы, с которыми вы сталкиваетесь при использовании MCI API:
Путь слишком длинный, и этот API не может обрабатывать длинные имена файлов. Ограничение, как правило, вокруг
260
символы, как указано на странице.Не все файлы имеют "короткое имя". Начиная с Windows 7, так называемый
8.3
(FILENAME.EXT
) создание файла может быть отключено. Это означает, что не может быть пути, которыйGetShortPathName
может вернуть, что позволит MCI получить доступ к файлу.
Настоятельно рекомендуется заменить все это современным API. DirectDraw
а также Media Foundation
Как уже упоминалось другими комментаторами, будут подходящие замены.
Учитывая ваши ограничения (не могут изменить 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
на случай, если что-то пойдет не так. Вы действительно должны проверить себя везде здесь.