拖放操作调用的 vbscript 中的当前工作目录
current working directory in a vbscript invoked by a drag & drop operation
当我试图为我的批处理脚本获得提升权限时,当我发现两个相关的 SO 问题时
- How to request Administrator access inside a batch file
- How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required?
...这导致了部分有效的答案。出于某种原因,我在 VBS 脚本中为包含空格的文件路径参数传递命令行时遇到问题,因此我尝试将解决方案分成 3 个部分并集中在内部 (VBS) 步骤,然后通过调用添加最后一步尽管与 VBS 脚本位于同一文件夹中,但无法找到该 VBS 的批处理。我发现拖放不是 "that easy" 和 当使用 .vbs
而不是 .bat
或 .exe
作为拖放时它是不同的目标。
这是我的实际问题:
如果我将文件拖放到可执行文件(exe) 或批处理文件(bat、cmd) 上,当前工作目录由拖放项目的来源决定。它的目录被设置为处理它的程序或脚本的工作目录。
如果我将文件放到 VBS 脚本上,情况就不一样了。在 Windows 8.1 x64 上,我观察到它是 C:\Windows\System32
,即使参数与 VBS 位于同一文件夹中。
我可以像这样简单地使用批处理文件(作为拖放中继)
my.vbs %*
获得"the normal".bat
行为(drop source dictates CWD),但我也想了解它。
这是错误还是功能?它在所有 Windows 版本中是否一致?
编辑: 为显示我如何到达那里的问题添加了背景(在顶部)(+小更正)
c:\windows\system32\CScript.exe
或 c:\windows\system32\Wscript.exe
是 运行 vbscript 的程序。如您所见,它们位于 system32
.
Windows 使用 ShellExecuteEx 启动程序 - 请参阅 MSDN 的规则:
ShellExecuteEx function (Windows)
ShellExecuteEx 使用CreateProcess (CreateProcessEx) 来实际启动一个程序。 CreateProcess function (Windows)
编辑
CMD 不使用注册表作为它自己的东西。这些注册表项用于 CMD 以外的程序。
CMD 的主要目标是在增强它的同时与 MS Dos 5 兼容,它将正确 运行 大多数 DOS 批处理文件。它是由致力于 OS/2 NOT Windows.
的 IBM 工程师编写的
编辑 2
你的问题的核心是你试图编写程序,就好像你是一个打字操作计算机的用户。
作为程序员,您不做假设。不做假设的最简单方法 是指定您想要的内容的完整路径。您的批处理文件不应该关心当前目录是什么。 EG 在 CMD 中,每个驱动器都有一个当前目录,以与 MSDos 5 兼容(控制台中的程序倾向于共享它们,但不是必须的)。在 Windows 中,每个程序有一个当前目录。这些年来默认的当前目录发生了变化。
只有在编写供用户使用的批处理文件时才应使用当前目录。 EG 如果您键入 dir
它会执行当前目录,作为通用命令的批处理文件应该以相同的方式工作。
Arguments
属性 包含脚本中所有掉落物品的完整路径,因此您可以像这样确定每个掉落物品的目录:
Set fso = CreateObject("Scripting.FileSystemObject")
For Each item In WScript.Arguments
WScript.Echo fso.GetParentFolderName(item)
Next
假设工作目录将由拖放到脚本中的内容来定义是一种误入歧途的方法。如果您需要该逻辑,您可以自己在脚本中实现它,例如像这样:
Set fso = CreateObject("Scripting.FileSystemObject")
Set sh = CreateObject("WScript.Shell")
For Each item In WScript.Arguments
sh.CurrentDirectory = fso.GetParentFolderName(item)
'working directory is now the parent folder of the current item
'...
Next
如果您需要工作目录作为 VBScript 文件的父目录,您可以从 ScriptFullName
属性:
Set fso = CreateObject("Scripting.FileSystemObject")
WScript.Echo fso.GetParentFolderName(WScript.ScriptFullName)
如果我在 Windows 注册表中查找文件类型,我会在它们的 Shell\Open\Command
值中看到以下内容:
- 蝙蝠文件:
"%1" %*
- 命令文件:
"%1" %*
- 可执行文件:
"%1" %*
- VBS 文件:
"%SystemRoot%\System32\WScript.exe" "%1" %*
这似乎表明 bat
、cmd
、exe
本身 被视为可执行文件 ,也许是出于历史原因,而 VBS 被认为是一个 普通脚本 ,它只是可执行的,因为它的扩展名注册了一些可执行文件以调用它来解释它。与 Python 或 Perl 几乎相同。
[更新] 实际上我证明了 Python 脚本显示了与我的 VBS 脚本完全相同的行为:从提供参数的命令行调用它保持 CWD ,将文件放在上面会导致 CWD 为 C:\Windows\System32
。所以我的问题似乎有点错误,但最终它帮助人们为我指明了进一步研究的正确方向......
经过一些API监控,这是我看到的
当您将文件拖放到 .exe
文件上时,explorer.exe
使用 CreateProcess
API 函数启动进程,将可执行文件作为 lpApplicationName
,可执行文件和删除文件为 lpCommandLine
。 lpCurrentDirectory
在函数调用中由调用进程设置为包含已删除文件的文件夹[1].
当您将文件拖放到 .cmd
文件上时,explorer.exe
也使用 CreateProcess
API,但在这种情况下 lpApplicationName
是 null
和 lplCommandLine
包含批处理文件和删除的文件。 lpCurrentDirectory
也设置为拖放文件的父文件夹[1].
当您将文件拖放到 .vbs
文件上时,ShellExecuteEx
is used and the lpDirectory
field of the SHELLEXECUTEINFO
结构为 null
,因此,创建的进程会继承父进程的当前活动目录。默认情况下,explorer.exe
进程的当前活动目录是 %systemroot%\system32
,但可以启动具有不同当前活动目录的 explorer
实例,该目录将在此类删除操作中继承.
[1] 如果我们删除多个文件,则使用作为第一个参数传递的文件的路径
注意仅供参考:测试活动目录继承遵循的过程是:
- 打开一个
cmd
实例并将当前活动目录更改为 c:\temp
- 杀死所有
explorer.exe
个实例
- 来自
cmd
实例调用 explorer.exe
。此 explorer
实例将 cmd
window 中的活动目录作为其当前活动目录。
当我试图为我的批处理脚本获得提升权限时,当我发现两个相关的 SO 问题时
- How to request Administrator access inside a batch file
- How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required?
...这导致了部分有效的答案。出于某种原因,我在 VBS 脚本中为包含空格的文件路径参数传递命令行时遇到问题,因此我尝试将解决方案分成 3 个部分并集中在内部 (VBS) 步骤,然后通过调用添加最后一步尽管与 VBS 脚本位于同一文件夹中,但无法找到该 VBS 的批处理。我发现拖放不是 "that easy" 和 当使用 .vbs
而不是 .bat
或 .exe
作为拖放时它是不同的目标。
这是我的实际问题:
如果我将文件拖放到可执行文件(exe) 或批处理文件(bat、cmd) 上,当前工作目录由拖放项目的来源决定。它的目录被设置为处理它的程序或脚本的工作目录。
如果我将文件放到 VBS 脚本上,情况就不一样了。在 Windows 8.1 x64 上,我观察到它是 C:\Windows\System32
,即使参数与 VBS 位于同一文件夹中。
我可以像这样简单地使用批处理文件(作为拖放中继)
my.vbs %*
获得"the normal".bat
行为(drop source dictates CWD),但我也想了解它。
这是错误还是功能?它在所有 Windows 版本中是否一致?
编辑: 为显示我如何到达那里的问题添加了背景(在顶部)(+小更正)
c:\windows\system32\CScript.exe
或 c:\windows\system32\Wscript.exe
是 运行 vbscript 的程序。如您所见,它们位于 system32
.
Windows 使用 ShellExecuteEx 启动程序 - 请参阅 MSDN 的规则:
ShellExecuteEx function (Windows)
ShellExecuteEx 使用CreateProcess (CreateProcessEx) 来实际启动一个程序。 CreateProcess function (Windows)
编辑
CMD 不使用注册表作为它自己的东西。这些注册表项用于 CMD 以外的程序。
CMD 的主要目标是在增强它的同时与 MS Dos 5 兼容,它将正确 运行 大多数 DOS 批处理文件。它是由致力于 OS/2 NOT Windows.
的 IBM 工程师编写的编辑 2
你的问题的核心是你试图编写程序,就好像你是一个打字操作计算机的用户。
作为程序员,您不做假设。不做假设的最简单方法 是指定您想要的内容的完整路径。您的批处理文件不应该关心当前目录是什么。 EG 在 CMD 中,每个驱动器都有一个当前目录,以与 MSDos 5 兼容(控制台中的程序倾向于共享它们,但不是必须的)。在 Windows 中,每个程序有一个当前目录。这些年来默认的当前目录发生了变化。
只有在编写供用户使用的批处理文件时才应使用当前目录。 EG 如果您键入 dir
它会执行当前目录,作为通用命令的批处理文件应该以相同的方式工作。
Arguments
属性 包含脚本中所有掉落物品的完整路径,因此您可以像这样确定每个掉落物品的目录:
Set fso = CreateObject("Scripting.FileSystemObject")
For Each item In WScript.Arguments
WScript.Echo fso.GetParentFolderName(item)
Next
假设工作目录将由拖放到脚本中的内容来定义是一种误入歧途的方法。如果您需要该逻辑,您可以自己在脚本中实现它,例如像这样:
Set fso = CreateObject("Scripting.FileSystemObject")
Set sh = CreateObject("WScript.Shell")
For Each item In WScript.Arguments
sh.CurrentDirectory = fso.GetParentFolderName(item)
'working directory is now the parent folder of the current item
'...
Next
如果您需要工作目录作为 VBScript 文件的父目录,您可以从 ScriptFullName
属性:
Set fso = CreateObject("Scripting.FileSystemObject")
WScript.Echo fso.GetParentFolderName(WScript.ScriptFullName)
如果我在 Windows 注册表中查找文件类型,我会在它们的 Shell\Open\Command
值中看到以下内容:
- 蝙蝠文件:
"%1" %*
- 命令文件:
"%1" %*
- 可执行文件:
"%1" %*
- VBS 文件:
"%SystemRoot%\System32\WScript.exe" "%1" %*
这似乎表明 bat
、cmd
、exe
本身 被视为可执行文件 ,也许是出于历史原因,而 VBS 被认为是一个 普通脚本 ,它只是可执行的,因为它的扩展名注册了一些可执行文件以调用它来解释它。与 Python 或 Perl 几乎相同。
[更新] 实际上我证明了 Python 脚本显示了与我的 VBS 脚本完全相同的行为:从提供参数的命令行调用它保持 CWD ,将文件放在上面会导致 CWD 为 C:\Windows\System32
。所以我的问题似乎有点错误,但最终它帮助人们为我指明了进一步研究的正确方向......
经过一些API监控,这是我看到的
当您将文件拖放到 .exe
文件上时,explorer.exe
使用 CreateProcess
API 函数启动进程,将可执行文件作为 lpApplicationName
,可执行文件和删除文件为 lpCommandLine
。 lpCurrentDirectory
在函数调用中由调用进程设置为包含已删除文件的文件夹[1].
当您将文件拖放到 .cmd
文件上时,explorer.exe
也使用 CreateProcess
API,但在这种情况下 lpApplicationName
是 null
和 lplCommandLine
包含批处理文件和删除的文件。 lpCurrentDirectory
也设置为拖放文件的父文件夹[1].
当您将文件拖放到 .vbs
文件上时,ShellExecuteEx
is used and the lpDirectory
field of the SHELLEXECUTEINFO
结构为 null
,因此,创建的进程会继承父进程的当前活动目录。默认情况下,explorer.exe
进程的当前活动目录是 %systemroot%\system32
,但可以启动具有不同当前活动目录的 explorer
实例,该目录将在此类删除操作中继承.
[1] 如果我们删除多个文件,则使用作为第一个参数传递的文件的路径
注意仅供参考:测试活动目录继承遵循的过程是:
- 打开一个
cmd
实例并将当前活动目录更改为c:\temp
- 杀死所有
explorer.exe
个实例 - 来自
cmd
实例调用explorer.exe
。此explorer
实例将cmd
window 中的活动目录作为其当前活动目录。