如果我的 VBScript 是由批处理脚本而不是由人打开的,为啥它的功能会有所不同?

Posted

技术标签:

【中文标题】如果我的 VBScript 是由批处理脚本而不是由人打开的,为啥它的功能会有所不同?【英文标题】:Why does my VBScript function differently if it is opened by a batch script rather than a person?如果我的 VBScript 是由批处理脚本而不是由人打开的,为什么它的功能会有所不同? 【发布时间】:2021-06-27 01:17:26 【问题描述】:

简单地说,我有一个名为“tyrian_soundtest.vbs”的VBScript,它播放一个名为“tyrian_soundtest.mp3”的.mp3

VBScript 代码如下

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

打开后,它会播放 .mp3。很简单。

当我运行一个名为“tyrian_soundtest.bat”的批处理脚本时,问题就来了。相对于它,.vbs 和 .mp3 位于一个名为 sfx 的文件夹中。这是该文件的一个迭代包含的内容。

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

结果是一个错误,指出 Windows 找不到文件路径,可能是因为它包含空格。 .bat 的其他尝试将第 2 行替换为

start .\sfx\tyrian_soundtest.vbs

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

我所做的任何尝试都会给出三个结果之一。选项 1:没有错误,但音频根本不会播放。选项 2:关于找不到文件目录的错误。选项 3:该文件路径在新的 cmd 窗口中打开,但 .vbs 永远不会运行。

有什么方法可以格式化 .bat 以使 .vbs 运行而不会导致错误?

【问题讨论】:

如果 vbs 已经在当前工作目录中,则不必指定任何路径,只需指定脚本即可。当使用START 命令时,第一组引号被认为是窗口的TITLE。因此,您使用一组空引号。 start "" "%cd%\sfx\tyrian_soundtest.vbs"。作为最佳实践,我通常使用 cscript.exe 从批处理文件启动我的 vbscripts。 问题也可能是 mp3 文件的路径。给出文件的完整路径Sound.URL @Squashman 这有一个奇怪的结果,文件现在可以正常播放,但前提是 vbscript 位于同一文件目录中(尽管指定了文件夹 sfx) 【参考方案1】:

主要问题是批处理文件和VBScript文件中使用了当前目录路径。启动%SystemRoot%\System32\cmd.exe处理批处理文件的当前目录可以是任意目录。

Windows资源管理器在双击批处理文件时将批处理文件的目录设置为当前目录导致explorer.exe启动cmd.exe以处理将完整限定文件名传递给@987654330的双击批处理文件@ 在选项 /c 之后作为附加参数。但是,如果批处理文件存储在使用 UNC 路径访问的网络资源上,Windows 命令处理器会将当前目录从网络资源更改为 %SystemRoot%(Windows 目录),并且批处理文件无法启动 Windows Script Host 来处理VBS 文件。另见:CMD does not support UNC paths as current directories。

也可以通过右键单击批处理文件并使用以管理员身份运行来启动它。这可能导致目录%SystemRoot%\System32(Windows 系统目录)成为当前目录。另见:Why does 'Run as administrator' change (sometimes) batch file's current directory?

这只是当前目录与包含批处理文件的目录不同的众多示例中的两个。因此,如果批处理文件引用与批处理文件本身或批处理文件目录的子目录存储在同一目录中的其他文件,建议使用批处理文件的完整路径引用这些文件,而不是使用相对于当前目录的路径.

例子:

使用的文件存放在C:\Temp\Development & Test目录中,如下:

音效 tyrian_soundtest.vbs tyrian_soundtest.mp3 tyrian_soundtest.bat

用户打开一个command prompt 窗口,这通常会导致使用%USERPROFILE%%HOMEDRIVE%%HOMEPATH% 引用的目录成为当前目录。用户在命令提示符窗口中执行下一步:

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

所以当前目录肯定不是批处理文件所在的目录。

批处理文件可以编写如下代码来启动Windows Script Host来处理VBS文件tyrian_soundtest.vbs并成功播放MP3文件tyrian_soundtest.mp3

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

%~dp0 引用参数 0 的驱动器和路径,即当前处理的批处理文件的完整路径。 %~dp0 引用的批处理文件路径始终以反斜杠结尾。因此,%~dp0 与文件/文件夹名称或通配符模式的连接应始终在不使用额外反斜杠的情况下完成,因为这将导致两个 \ 在完整的参数字符串中串联,并且 Windows 文件管理需要在将参数字符串传递给文件系统之前,通过将 \\ 替换为 \ 来修复这个小错误。另请参阅有关 Naming Files, Paths, and Namespaces 的 Microsoft 文档,该文档解释了在将文件/文件夹字符串传递到文件系统之前通常对它们应用哪些自动更正。

cmd.exe 的内部命令 START 将第一个双引号参数字符串解释为控制台窗口的标题字符串,因为它可以在命令提示符窗口 start /? 中运行并读取输出帮助。因此,仅使用是不够的:

@start "%~dp0sfx\tyrian_soundtest.vbs"

这将导致使用自己的控制台窗口再启动一个命令进程,其中 VBS 文件的完整限定文件名作为控制台窗口的标题。

Windows 上默认处理 VBS 文件的 Windows Script Host 有两个版本:

    %SystemRoot%\System32\cscript.exec单机版。 %SystemRoot%\System32\wscript.exeWindows GUI 版本。

控制台版本cscript.exe的使用通常会导致父进程打开控制台窗口,如果父进程本身不是控制台应用程序,并且已经在打开的控制台窗口中运行,例如在执行由@处理的批处理文件时987654353@ 也是一个控制台应用程序。

使用 Windows GUI 版本 wscript.exe 会导致默认情况下根本不打开窗口。如果需要,处理后的脚本文件必须包含打开窗口的命令。

在命令提示符窗口cscript /? 和下一个wscript /? 中运行时也可以看到差异。第一个命令导致在已打开的命令提示符窗口中打印 Windows Script Host 的命令行选项的帮助,而第二个命令导致显示一个图形窗口 wscript.exe 显示相同的用法帮助。

Windows Script Host 的使用帮助还解释了每个用户如何定义哪个版本的 Windows Script Host 是执行脚本的默认版本。因此,不建议在命令行中使用start 仅指定带有完整路径的VBS 文件名,并让cmd.exe 在Windows 注册表中查找要运行哪个版本的Windows Script Host 来处理VBS 文件。最好显式运行最适合播放 MP3 文件的 Windows Script Host 版本,在这种情况下,Windows GUI 版本wscript.exe 默认打开根本没有窗口在后台播放 MP3 文件。

所以可以使用:

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

有一个用"" 定义的空标题字符串,因为启动的可执行文件wscript.exe 是一个Windows GUI 应用程序,cmd.exe 根本没有打开任何控制台窗口。所以标题字符串并不重要,可以是一个空字符串。

但是该命令行还存在一个问题。 VB 脚本引用没有路径的 MP3 文件,这意味着相对于当前目录的路径。当前目录是%USERPROFILE%,而不是包含MP3 文件tyrian_soundtest.mp3C:\Temp\Development & Test\sfx。所以VB脚本会找不到要播放的MP3文件。

有两种解决方案可以解决此问题:

    以下命令行在批处理文件中的用法:

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

    命令START的选项/D用于显式设置批处理文件目录的子目录sfx分别在wscript.exe启动的进程的当前目录中启动cmd.exe使用带有适当创建结构 STARTUPINFO 的 Windows 内核库函数 CreateProcess。

    VBS 文件引用 MP3 文件 tyrian_soundtest.mp3 及其完整路径,VB 脚本文件根据其自己的完整限定文件名自行确定。这可以在 VB 脚本文件tyrian_soundtest.vbs 中通过在第二行中使用来实现:

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

最好同时使用这两种可能的解决方案,因为在这种情况下,VB 脚本文件也可以由与cmd.exe 处理批处理文件tyrian_soundtest.bat 的不同进程执行,并且无法删除目录%~dp0sfx,因为只要startedwscript.exe在运行,因为这个目录就是运行Windows Script Host的当前目录。

因此,批处理文件和 VB 脚本文件现在都可以独立于哪个目录是当前目录,因为它们都引用了具有完整路径的文件,这些文件的完整路径由脚本本身根据其完整限定文件名确定。

【讨论】:

以上是关于如果我的 VBScript 是由批处理脚本而不是由人打开的,为啥它的功能会有所不同?的主要内容,如果未能解决你的问题,请参考以下文章

iPhone:照片框架:如何仅获取由我的应用程序创建的那些相册/图像,而不是由其他人创建的?

git rm 做啥而不是由操作系统和 git add 删除

内部版本号不是由 fastlane 设置的,而是由 Xcode 设置的

为每个对象的行为创建脚本,而不是在对象脚本主体中编写所有内容

Vbscript从批处理文件运行而不将击键发送到批处理文件

是否可以在批处理文件中嵌入和执行VBScript而不使用临时文件?