首页 文章

如果路径太长,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);

失败命令的示例:

打开“C:\ qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq \ dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd \ Windows Critical Stop.wav”type waveaudio alias sample

但:

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

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

How to make mciSendString accept long filenames?


笔记:

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

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

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

4 回答

  • 2

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

    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时会遇到两个问题:

    • 路径名是too long,此API无法处理长文件名 . 限制通常在 260 字符左右,如页面上所述 .

    • 并非所有文件都有"short name" . Starting with Windows 7,可以禁用所谓的 8.3FILENAME.EXT )文件创建 . 这意味着可能没有 GetShortPathName 可以返回的路径允许MCI访问该文件 .

    强烈建议使用现代API替换整个内容 . 如其他评论者所述, DirectDrawMedia Foundation 将是合适的替代品 .

  • 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
    

    这里,使用 MMIO_PARSE flag调用mmioOpen将文件路径转换为完全限定的文件路径 . 根据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
    }
    

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

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

    您还想检查 CopyFile 的成功,以防出现其他问题 . 你真的需要在这里检查自己 .

相关问题