【问题标题】:mciSendString won't play an audio file if path is too long如果路径太长,mciSendString 将不会播放音频文件
【发布时间】:2017-12-26 13:04:57
【问题描述】:

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

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\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfoijdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav 类型波"

但是:

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

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

如何让 mciSendString 接受长文件名?


注意事项:

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

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

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

【问题讨论】:

  • 试试 mciSendCommand()。
  • @JazzSoft 我刚试过,using this,很遗憾,它是一样的。
  • mciSendString 返回的MCIERROR 是什么?可能是MCIERR_FILENAME_REQUIRED - 文件名无效。确保文件名不超过 8 个 个字符,后跟句点和扩展名。关于open 命令-这里必须是文件名吗?或设备名称?
  • 感谢@RbMm 提供此信息。 “不超过 8 个字符”看起来非常 pre-Windows95-ish!我可以确认它适用于C:\Windows Critical Stop.wav,但不适用于C:\qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav
  • MCI 是 Windows 多媒体的一部分,被微软视为“传统”。 msdn.microsoft.com/en-us/library/hh309469.aspx 所以这很可能是 API 的限制(在这种情况下,它永远不会被修复)

标签: c++ windows winapi audio mci


【解决方案1】:

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

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

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

  3. 要缩短文件名,可以使用 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;
}

【讨论】:

    【解决方案2】:

    这是旧版 MCI 功能的限制。您在使用 MCI API 时会遇到两个问题:

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

    2. 并非所有文件都有“短名称”。 Starting with Windows 7,所谓的8.3 (FILENAME.EXT) 文件创建可以被禁用。这意味着GetShortPathName 可能无法返回允许 MCI 访问文件的路径。

    强烈建议使用现代 API 替换整个内容。正如其他评论者所提到的,DirectDraw 和 Media Foundation 将是合适的替代品。

    【讨论】:

      【解决方案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
      

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

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

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

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

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

      【讨论】:

      • 哇,这太棒了!
      猜你喜欢
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多