如果路径太长,mciSendString不会播放音频文件

时间:2017-07-20 17:26:46

标签: c++ windows winapi audio mci

当文件的路径+文件名很长时,我注意到了

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:\ qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq \ dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd \ Windows Critical Stop.wav"输入waveaudio别名样本

可是:

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

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

如何让mciSendString接受长文件名?

注意:

  • 我也使用mciSendCommand尝试了这个MSDN示例,但它是一样的。

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

  • 如果真的mci*函数无法使用长于127个字符的文件名,那么我可以使用winapi(没有外部库)? (PlaySound不是一个选项,因为它不能与所有wav文件一起使用,例如48 khz:non-working等。)

4 个答案:

答案 0 :(得分:4)

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

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

  2. 您可以尝试的选项是将工作目录更改为文件目录,然后限制仅适用于filename。 - > SetCurrentDiectory

  3. 要缩短文件名,可以使用Winapi函数GetShortPathName
    但是:

      

    SMB 3.0不支持连续共享的短名称   可用性能力。

         

    弹性文件系统(ReFS)不支持短名称。如果你打电话   在磁盘上没有任何短名称的路径上的GetShortPathName,   调用将成功,但将返回长名称路径。   NTFS卷也可以实现这一结果,因为没有   保证给定的长名称存在短名称。

  4. 基于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;
    }
    

答案 1 :(得分:2)

这是传统MCI功能的限制。使用MCI API时会遇到两个问题:

  1. 路径名为too long,此API无法处理长文件名。如页面所示,限制通常约为260个字符。

  2. 并非所有文件都有&#34;短名称&#34;。 Starting with Windows 7,可以禁用所谓的8.3FILENAME.EXT)文件创建。这意味着可能没有GetShortPathName可以返回的路径允许MCI访问该文件。

  3. 强烈建议使用现代API替换整个内容。其他评论者提到的DirectDrawMedia Foundation将是合适的替代品。

答案 2 :(得分: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

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

  

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

也就是说,缓冲区总是假定为128字节长。对于长文件名,缓冲区结果不足,mmioOpen返回错误,导致mciSendCommand认为声音文件丢失并返回MCIERR_FILENAME_REQUIRED

不幸的是,由于它正在解析完全限定的文件路径,SetCurrentDirectory无济于事。

由于问题出在MCI驱动程序(mciwave.dll)内,我怀疑是否有办法迫使MCI子系统处理长路径。

答案 3 :(得分:0)

考虑到你的限制(无法改变任何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
}

您可以使用result上的mciGetErrorString来查找您正在获取的确切错误代码,并使if具体,或者以不同的方式处理其他错误。我看了在the list of errors,但我不确定。

如果您创建了新文件,请不要忘记使用Windows API中的DeleteFile。我不知道你是想在节目结束之前还是在比赛结束后立即保留它,所以决定什么更有意义。

如果出现其他问题,您还需要检查CopyFile是否成功。你真的需要在这里到处检查自己。