Search code examples
batch-filevbscript

Why does my VBScript function differently if it is opened by a batch script rather than a person?


Simply put, I have a VBScript titled "tyrian_soundtest.vbs" that plays an .mp3 that is titled "tyrian_soundtest.mp3"

The VBScript code is below

Set Sound = CreateObject("WMPlayer.OCX.7")
Sound.URL = "tyrian_soundtest.mp3"
Sound.Controls.play
do while Sound.currentmedia.duration = 0
wscript.sleep 1
loop
wscript.sleep (int(Sound.currentmedia.duration)+1)*1000

When opened, it plays the .mp3. Simple enough.

The trouble comes in when I run a batch script titled "tyrian_soundtest.bat". Relative to it, the .vbs and .mp3 are in a folder called sfx. Here is what one iteration of that file contained.

@echo off
start %cd%\sfx\tyrian_soundtest.vbs
exit /b

The result is an error stating that Windows couldn't find the file path, likely due to it containing a space. Other attempts of the .bat were replacing line 2 with

start .\sfx\tyrian_soundtest.vbs

or

start "%cd%\sfx\tyrian_soundtest.vbs"

Any attempt I've made gives one of three results. Option 1: There is no error, but the audio simply never plays. Option 2: An error is thrown about the file directory not being found. Option 3: That file path opens up in a new cmd window, but the .vbs is never run.

Is there any way format the .bat to get the .vbs to run through the without an error being caused?


Solution

  • The main issue is that there is used in batch file and in the VBScript file the current directory path. The current directory on starting %SystemRoot%\System32\cmd.exe to process the batch file can be any directory.

    The Windows Explorer sets the directory of the batch file as current directory on double clicking the batch file resulting in starting cmd.exe by explorer.exe to process the double clicked batch file of which full qualified file name is passed to cmd.exe after the option /c as additional argument. But if the batch file is stored on a network resource accessed using UNC path, the Windows command processor changes the current directory from the network resource to %SystemRoot% (the Windows directory) and the batch file fails to start Windows Script Host to process the VBS file. See also: CMD does not support UNC paths as current directories.

    A batch file can be also started by right clicking on it and using Run as administrator. This can result in making the directory %SystemRoot%\System32 (the Windows system directory) the current directory. See also: Why does 'Run as administrator' change (sometimes) batch file's current directory?

    That are just two of many examples where the current directory is different to the directory containing the batch file. So if a batch file references other files stored in same directory as the batch file itself or a subdirectory of the batch files directory, it is advisable to reference these files with the full path of the batch file instead of using a path relative to current directory.

    Example:

    The used files are stored in directory C:\Temp\Development & Test as follows:

    • sfx
      • tyrian_soundtest.vbs
      • tyrian_soundtest.mp3
    • tyrian_soundtest.bat

    A user opens a command prompt window which usually results in making the directory referenced with %USERPROFILE% or with %HOMEDRIVE%%HOMEPATH% the current directory. A user executes next in the command prompt window:

    "C:\Temp\Development & Test\tyrian_soundtest.bat"
    

    So the current directory is definitely not the directory containing the batch file.

    The batch file can be coded as follows to start nevertheless the Windows Script Host for processing the VBS file tyrian_soundtest.vbs and successfully play the MP3 file tyrian_soundtest.mp3.

    @start "" /D "%~dp0sfx" %SystemRoot%\System32\wscript.exe //NoLogo "%~dp0sfx\tyrian_soundtest.vbs"
    

    %~dp0 references the drive and path of argument 0 which is the full path of the currently processed batch file. The batch file path referenced with %~dp0 always ends with a backslash. For that reason the concatenation of %~dp0 with a file/folder name or wildcard pattern should be always done without using an additional backslash as that would result in two \ in series in complete argument string and the Windows file management would need to fix that small error by replacing \\ by just \ before passing the argument string to the file system. See also the Microsoft documentation about Naming Files, Paths, and Namespaces which explains which automatic corrections are usually applied on file/folder strings before passing them to the file system.

    The internal command START of cmd.exe interprets the first double quoted argument string as title string for the console window as it can be seen on running in a command prompt window start /? and reading the output help. For that reason it is not enough to use just:

    @start "%~dp0sfx\tyrian_soundtest.vbs"
    

    That would result in starting one more command process with its own console window with the full qualified file name of the VBS file as title of the console window.

    The Windows Script Host processing a VBS file by default on Windows exists in two versions:

    1. %SystemRoot%\System32\cscript.exe is the console version.
    2. %SystemRoot%\System32\wscript.exe is the Windows GUI version.

    The usage of the console version cscript.exe results usually in opening a console window by the parent process if the parent process is not itself a console application running already with an opened console window like on execution of a batch file processed by %SystemRoot%\System32\cmd.exe being also a console application.

    The usage of the Windows GUI version wscript.exe results in no opening of a window by default at all. The processed script file must contain commands to open a window if that is wanted at all.

    The difference can be also seen on running from within a command prompt window cscript /? and next wscript /?. The first command results in printing the help for the command line options of Windows Script Host in already opened command prompt window while the second command results in showing a graphic window by wscript.exe displaying the same usage help.

    The usage help of Windows Script Host explains also how each user can define which version of Windows Script Host is the default for executing scripts. So it is not advisable to specify just the VBS file name with full path on the command line with start and let cmd.exe look up in Windows registry which version of Windows Script Host to run to process the VBS file. It is better to explicitly run the version of Windows Script Host most suitable for playing the MP3 file which is in this case the Windows GUI version wscript.exe opening by default no window at all to play the MP3 file in background.

    So it would be possible to use:

    @start "" %SystemRoot%\System32\wscript.exe //NoLogo "%~dp0sfx\tyrian_soundtest.vbs"
    

    There is an empty title string defined with "" as the started executable wscript.exe is a Windows GUI application for which no console window is opened at all by cmd.exe. So the title string does not really matter and can be an empty string.

    But there is left one problem with that command line. The VB script references the MP3 file without path which means with a path relative to current directory. The current directory is %USERPROFILE% and not C:\Temp\Development & Test\sfx which contains the MP3 file tyrian_soundtest.mp3. So the VB script would fail to find the MP3 file to play.

    There are two solutions to fix this:

    1. The usage of the following command line in the batch file:

       @start "" /D "%~dp0sfx" %SystemRoot%\System32\wscript.exe //NoLogo "%~dp0sfx\tyrian_soundtest.vbs"
      

      The option /D of command START is used to explicitly set the subdirectory sfx of the batch file directory as start in respectively current directory for the process wscript.exe which cmd.exe starts using the Windows kernel library function CreateProcess with an appropriate created structure STARTUPINFO.

    2. The VBS file references the MP3 file tyrian_soundtest.mp3 with its full path which the VB script file determines itself from its own full qualified file name. That can be achieved in the VB script file tyrian_soundtest.vbs by using in the second line:

      Sound.URL = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName) + "\tyrian_soundtest.mp3"
      

    Best would be using both possible solutions as in this case the VB script file would work also on being executed by a different process than cmd.exe processing the batch file tyrian_soundtest.bat and deletion of directory %~dp0sfx is not possible as long as started wscript.exe is running because of this directory is the current directory of running Windows Script Host.

    So the batch file as well as the VB script file work now both independent on which directory is the current directory as both reference the files with full path determined by the scripts themselves from their full qualified file names.