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

mciSendString won't play an audio file if path is too long

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

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 接受长文件名?


备注:

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

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

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

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

    SMB 3.0 does not support short names on shares with continuous availability capability.

    Resilient File System (ReFS) doesn't support short names. If you call GetShortPathName on a path that doesn't have any short names on-disk, the call will succeed, but will return the long-name path instead. This outcome is also possible with NTFS volumes because there's no guarantee that a short name will exist for a given long name.

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

这是旧版 MCI 功能的限制。使用 MCI API:

有两个问题
  1. 路径名是too long,这个API不能处理长文件名。如页面上所述,限制通常在 260 个字符左右。

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

强烈建议用现代 API 替换整个东西。正如其他评论者所提到的,DirectDrawMedia Foundation 将是合适的替代品。

我已经调试过了(在 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,这有一个限制:

The buffer must be large enough to hold at least 128 characters.

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

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

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