6

When the path+filename of a file is really long, I've noticed that

PlaySound(fName.c_str(), NULL, SND_ASYNC);

works, but not

mciSendString((L"open \"" + fName + L"\" type waveaudio alias sample").c_str(), NULL, 0, NULL);
mciSendString(L"play sample", NULL, 0, NULL);

Example of failing command:

open "C:\qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav" type waveaudio alias sample

But:

  • I really need mciSendString instead of PlaySound(), because PlaySound() doesn't play certain files (48 khz audio files, sometimes 24-bit files, etc.)

  • I need to be able to play audio files with potentially long paths because the end user of my app might have such files

How to make mciSendString accept long filenames?


Notes:

  • I've also tried with this MSDN example using mciSendCommand, but it's the same.

  • The max path+filename length is 127 (127: working, 128+: not working)

  • If really it's impossible to make mci* functions work with longer-than-127-char filenames, what could I use instead, just with winapi (without external libraries)? (PlaySound is not an option because doesn't work realiably with all the wav files, such as 48 khz: non-working, etc.)

Basj
  • 41,386
  • 99
  • 383
  • 673
  • Try *mciSendCommand()*. – JazzSoft Jul 20 '17 at 18:15
  • @JazzSoft I just tried, [using this](https://msdn.microsoft.com/en-us/library/windows/desktop/dd743675(v=vs.85).aspx), and it's the same, sadly. – Basj Jul 20 '17 at 19:11
  • what is `MCIERROR` returned by `mciSendString` ? may be [`MCIERR_FILENAME_REQUIRED`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd797980(v=vs.85).aspx) - The filename is invalid. Make sure the filename is no longer than **eight** characters, followed by a period and an extension. and about [`open`](https://msdn.microsoft.com/en-us/library/windows/desktop/dd743638(v=vs.85).aspx) command - are here must be filename ? or device name ? – RbMm Jul 20 '17 at 19:17
  • Thanks for this info @RbMm. "No longer than 8 characters" looks very pre-Windows95-ish! I can confirm it works with `C:\Windows Critical Stop.wav` but not with `C:\qisdjqldlkjsqdjqdqjslkdjqlksjlkdjqsldjlqjsdjqdksq\dajdjqjdlqjdlkjazejoizajoijoifjoifjdsfjsfszjfoijdsjfoijdsoifoidsjfojdsofjdsoijfoisjfoijoisdjfosjfqsd\Windows Critical Stop.wav` – Basj Jul 20 '17 at 19:20
  • but you sure that you use correct format for [open](https://msdn.microsoft.com/en-us/library/windows/desktop/dd743638(v=vs.85).aspx) command ? Identifier of an MCI device or device driver. This can be either a device name (as given in the registry or the SYSTEM.INI file) or the filename of the device driver. If you specify the filename of the device driver, you can optionally include the .DRV extension, **but you should not include the path to the file.** – RbMm Jul 20 '17 at 19:23
  • Yes @RbMm, I tried both [this](https://stackoverflow.com/a/2049859/1422096) and [this more official version](https://msdn.microsoft.com/en-us/library/windows/desktop/dd743675(v=vs.85).aspx), and it's the same: 90% of the files are played correctly, except those with long path. – Basj Jul 20 '17 at 19:27
  • Have you been able to find the exact limit of the file name or the command string, and whether or not the limit applies to the former or the latter? That information may be valuable in finding out, where the limit is enforced, and whether or not there may be a workaround. – IInspectable Jul 21 '17 at 19:20
  • @IInspectable The limit is 127 characters for path+filename. – Basj Jul 23 '17 at 00:44
  • 2
    MCI is part of Windows Multimedia which is considered "Legacy" by Microsoft. https://msdn.microsoft.com/en-us/library/hh309469.aspx so it may well be a limitation of the API (and in this case, it will never be fixed) – Simon Mourier Jul 23 '17 at 15:43
  • OK @SimonMourier, what would be the current replacement for this legacy api? – Basj Jul 23 '17 at 17:51
  • have you tried converting your path to short form with `GetShortPathName`? With short paths, it can fail too, but it will be much more rare. – geza Jul 23 '17 at 21:24
  • @Basj - you could use DirectShow (https://msdn.microsoft.com/en-us/library/windows/desktop/dd389098.aspx) or Media Foundation (https://msdn.microsoft.com/en-us/library/windows/desktop/ms703190.aspx). They are both COM-based API. – Simon Mourier Jul 24 '17 at 08:52
  • @SimonMourier Are they both available directly on a fresh new install of win xp vista 7 8 10 or will users have to install third party exe like redistribuable packages etc. ? – Basj Jul 24 '17 at 13:14
  • MF is the most recent, so it's Vista minimum, DirectShow should support XP, but you'll have to test a lot of configs if you want to support that range of OS (don't forget xp has 3 Service Packs, etc...) – Simon Mourier Jul 25 '17 at 09:03

3 Answers3

4

The 127 limit looks strange. I didn't find any information on MSDN about it.

  1. There is an alternative syntax to open: open waveaudio!right.wav

  2. An option You could try is to change the working directory to the directory of the file, then the limit only applies to filename. -> SetCurrentDiectory

  3. To shorten the filename a Winapi function can be used GetShortPathName
    But:

    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.

Based on example from 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;
}
Robert Andrzejuk
  • 5,076
  • 2
  • 22
  • 31
3

I have debugged it (on mciSendCommand example). The problem occurs when mwOpenDevice calls 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

Here, mmioOpen is called with MMIO_PARSE flag to convert file path to fully qualified file path. According to MSDN, this has a limitation:

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

That is, buffer is always assumed to be 128 bytes long. For long filenames, the buffer turns out to be insufficient and mmioOpen returns error, causing mciSendCommand to think that sound file is missing and return MCIERR_FILENAME_REQUIRED.

Unfortunately, since it's resolving fully qualified file path, SetCurrentDirectory will not help.

Since the problem is inside MCI driver (mciwave.dll) I doubt there is a way to force MCI subsystem to handle long path.

Codeguard
  • 7,787
  • 2
  • 38
  • 41
2

This is a limitation of the legacy MCI capabilities. There are two issues you face with using the MCI API:

  1. The path name is too long, and this API cannot handle long filenames. The limitation is generally around 260 characters as noted on the page.

  2. Not all files have a "short name". Starting with Windows 7, so called 8.3 (FILENAME.EXT) file creation could be disabled. This means that there may not be a path that GetShortPathName can return that will allow MCI to access the file.

Replacing the whole thing with a modern API is highly recommended. DirectDraw and Media Foundation, as mentioned by other commenters, would be suitable replacements.

Andrakis
  • 167
  • 1
  • 4
  • Not exactly: 8.3 filename creation could not be disabled **for specified volumes** - disabling it systemwide (unbound to volumes) was already available with almost any older Windows NT version and had to be expected. Read your linked source again. – AmigoJack Jun 20 '22 at 20:07