"Droplet" batch script - filenames containing ampersands "Droplet" batch script - filenames containing ampersands windows windows

"Droplet" batch script - filenames containing ampersands


There is a long-standing bug in Windows drag and drop functionality regarding file paths that contain & or ^ but don't contain a <space>.

If a file path contains at least one <space>, then Windows automatically encloses the path in quotes so that it gets parsed properly. Windows should do the same thing if the file path contains & or ^, but it does not.

If you create the following simple batch file and drag files onto it, you can see the problem.

@echo offsetlocal enableDelayedExpansionecho cmd=!cmdcmdline!echo %%1="%~1"pauseexit

The !cmdcmdline! variable contains the actual command that launched the batch file.The batch file prints out the command line and the first parameter.

If you drag and drop a file named "a.txt" you get

cmd=cmd /c ""C:\test\drag.bat" C:\test\a.txt"%1=C:\test\a.txtPress any key to continue . . .

If you disregard the quotes around the entire command you see that there are no quotes around the file argument. There are no special characters, so there is no problem.

Now drag and drop "a b.txt" and you get

cmd=cmd /c ""C:\test\drag.bat" "C:\test\a b.txt""%1="C:\test\a b.txt"Press any key to continue . . .

You can see how Windows detects the space in the name and encloses the file in quotes. Again there is no problem.

Now drag and drop "a&b.txt" and you get

cmd=cmd /c ""C:\test\drag.bat" C:\test\a&b.txt"%1=C:\test\aPress any key to continue . . .

Windows doesn't find a space in the name, so it does not enclose it in quotes. Big problem! Windows passes "C:\test\a" to the batch file and treats "b.txt" as a second file to be executed after the batch file completes. The hard EXIT command in the batch file prevents any split filenames from executing after the batch. Of course b.txt could never execute. But if the file were named "a&b.bat" and "b.bat" existed, then that could be trouble if the hard EXIT were not in the batch file.

It is possible to drag multiple files onto a batch file, and each one should be passed as a parameter.

The !cmdcmdline! is the only way to reliably access drag and drop arguments. But that will not work if files are passed as normal arguments in a normal call to the batch file.

Below is a batch file that can detect if it was called using drag and drop versus a normal call. (It is not bullet proof, but I think it should work in most situations) It will process each file argument, one at a time, regardless of the type of call. (The process simply echos the file name, but you can substitute whatever processing you want.) If the batch was called using drag and drop then it will do a hard exit to protect against split file names.

@echo offsetlocal disableDelayedExpansion:::: first assume normal call, get args from %*set args=%*set "dragDrop=":::: Now check if drag&drop situation by looking for %0 in !cmdcmdline!:: if found then set drag&drop flag and get args from !cmdcmdline!setlocal enableDelayedExpansionset "cmd=!cmdcmdline!"set "cmd2=!cmd:*%~f0=!"if "!cmd2!" neq "!cmd!" (  set dragDrop=1  set "args=!cmd2:~0,-1! "  set "args=!args:* =!"):::: Process the argsfor %%F in (!args!) do (  if "!!"=="" endlocal & set "dragDrop=%dragDrop%"  rem ------------------------------------------------  rem - Your file processing starts here.  rem - Each file will be processed one at a time  rem - The file path will be in %%F  rem -  echo Process file "%%~F"  rem -  rem - Your file processing ends here  rem -------------------------------------------------):::: If drag&drop then must do a hard exit to prevent unwanted execution:: of any split drag&drop filename argumentif defined dragDrop (  pause  exit)

It looks like your existing batch is only designed to handle one file. I can't tell if you need to make modifications to the calls to support multiple files. I modified the above batch to only process the first argument, and substituted your process into the argument processing loop. This is untested, but I think it should work for you.

@echo offsetlocal disableDelayedExpansion:::: first assume normal call, get args from %*set args=%*set "dragDrop=":::: Now check if drag&drop situation by looking for %0 in !cmdcmdline!:: if found then set drag&drop flag and get args from !cmdcmdline!setlocal enableDelayedExpansionset "cmd=!cmdcmdline!"set "cmd2=!cmd:*%~f0=!"if "!cmd2!" neq "!cmd!" (  set dragDrop=1  set "args=!cmd2:~0,-1! "  set "args=!args:* =!"):::: Process the first argument onlyfor %%F in (!args!) do (  if "!!"=="" endlocal & set "dragDrop=%dragDrop%"  rem ------------------------------------------------  rem - Your file processing starts here.  rem - Use %%F wherever you would normally use %1  rem  rem Change to drive and directory of input file  %%~dF  cd %%~pF  rem ffmpeg: mix to one channel, double the volume  %HOMEDRIVE%%HOMEPATH%\ffmpeg.exe -i "%%~nxF" -ac 1 -vol 1024 "%%~nF fixed%%~xF"  rem  rem - Your file processing ends here  rem -------------------------------------------------  goto :continue):continueif defined dragDrop (  pause  exit)


I admire dbenham's batch programming skills in silent awe.I tried his solution and stumbled upon two problems that I present here as I don't have enough reputation to comment:

  1. There seems to be an extra space in front of the last quotation mark on line 15 of his batch template. I suppose it should read:

      set "args=!cmd2:~0,-1!"

    Someone with not-so-stellar batch programming knowledge could have serious problems finding this, like me. I tried but was unable to edit dbenham's post because of the stupid "Edits must be at least 6 characters" limitation.

  2. The solution is generally not suitable for files/folders containing , (comma) or ; (semicolon) in their full path.It can be modified to work in case there is only one file/folder dropped onto a batch file by enclosing args in quotes on line 20:

    for %%F in ("!args!") do (

    When more than one file/folder is dropped onto a batch file, I am afraid no general workaround of the Windows bug is possible that could cope with comma/semicolon in file path. The SendTo mechanism of Windows obviously has the very same deficiency (bug), so can't be used to work around the drag-and-drop bug. It is thus up to Microsoft to finally fix this bug.