如果路径太长,mciSendString将无法播放音频文件。

6
当文件的路径+文件名非常长时,我注意到...
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\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav" type waveaudio alias sample

但是:

  • 我真的需要使用mciSendString而不是PlaySound(),因为PlaySound()无法播放某些文件(48 khz音频文件,有时是24位文件等)

  • 我需要能够播放潜在具有长路径的音频文件,因为我的应用程序的最终用户可能拥有这些文件

如何使mciSendString接受长文件名?


注意事项:

  • 我也尝试了使用mciSendCommand的MSDN示例,但结果相同。

  • 最大路径+文件名长度为127(127:工作正常,128+:不工作)

  • 如果真的无法使mci*函数与超过127个字符的文件名一起工作,那么我应该使用什么替代方法,只使用winapi(不使用外部库)?(PlaySound不是选项,因为它无法可靠地处理所有wav文件,例如48 khz:不起作用等)


尝试使用 *mciSendCommand()*。 - JazzSoft
@JazzSoft 我刚试了一下,使用这个链接,但很遗憾还是一样的。 - Basj
mciSendString 返回的 MCIERROR 是什么?可能是 MCIERR_FILENAME_REQUIRED - 文件名无效。请确保文件名不超过八个字符,并以一个点和一个扩展名结尾。关于 open 命令 - 这里必须是文件名还是设备名? - RbMm
感谢您提供的信息,@RbMm。 "不超过8个字符" 看起来非常像Windows 95之前的样式!我可以证实它适用于 C:\Windows Critical Stop.wav ,但对于 C:\qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav 不适用。 - Basj
2
MCI是Windows多媒体的一部分,被微软视为“遗留技术”。https://msdn.microsoft.com/en-us/library/hh309469.aspx因此它可能是API的限制(在这种情况下,它将永远不会被修复)。 - Simon Mourier
显示剩余9条评论
3个回答

4

127限制看起来很奇怪。我在MSDN上没有找到任何有关它的信息。

  1. 有一种替代语法可以打开:open waveaudio!right.wav

  2. 一个可尝试的选项是将工作目录更改为文件所在的目录,然后该限制仅适用于文件名。->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;
}

3

我已经调试过它(在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

这里使用MMIO_PARSE标记调用mmioOpen,以将文件路径转换为完全限定文件路径。根据MSDN的说明,这有一个限制:

缓冲区必须足够大,至少可容纳128个字符。

也就是说,缓冲区总是假定为128个字节长。对于较长的文件名,缓冲区会变得不足,导致mmioOpen返回错误,使mciSendCommand认为声音文件丢失并返回MCIERR_FILENAME_REQUIRED
不幸的是,由于它正在解析完全限定的文件路径,SetCurrentDirectory无济于事。
由于问题存在于MCI驱动程序(mciwave.dll)中,我怀疑是否有一种方法强制MCI子系统处理长路径。

哇,这太令人印象深刻了! - Basj

2
这是传统 MCI 功能的限制。使用 MCI API 时会面临两个问题:
  1. 路径名 过长,而该 API 无法处理长文件名。如页面上所述,限制通常在 260 字符左右。

  2. 并非所有文件都有“短名称”。从 Windows 7 开始,所谓的 8.3 (FILENAME.EXT) 文件创建可能会被禁用。这意味着可能没有一条路径可以通过 GetShortPathName 返回,以便 MCI 访问该文件。

强烈建议使用现代 API 替换整个过程。正如其他评论者所提到的那样,DirectDrawMedia Foundation 将是合适的替代方案。

不准确:无法禁用指定卷的8.3文件名创建 - 禁用它系统范围内(与卷无关)已经在几乎所有旧版Windows NT中可用,这是可以预料的。请再次阅读您链接的来源。 - AmigoJack

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接