When I was trying to get elevated rights for my batch script, when I found two related SO questions
- 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?
...that led to answers that worked partially. For some reason, I had issues with command line passing for file path arguments containing spaces within the VBS script, so I tried to break the solution into 3 parts and concentrated on the inner (VBS) step, then adding the last step by calling a batch from that VBS which could not be found, despite in the same folder as the VBS script. I found that drag & drop isn't "that easy" and that it's different when using .vbs
instead of .bat
or .exe
as drop targets.
Here is my actual question:
If I drag a file and drop it onto an executable (exe) or batch file (bat, cmd), The current working directory is determined by the source of the dragged item. Its directory is set as the working directory for the program or script that processes it.
If I drop a file onto a VBS script, it's different. On Windows 8.1 x64 I observe it to be C:\Windows\System32
even if the argument resides in the same folder as the VBS.
I can simply use a batch file (as drag'n'drop relay) like this
my.vbs %*
to get "the normal" .bat
behaviour (drop source dictates CWD), but I also want to understand it.
Is it a bug or a feature? Is it consistent over all Windows versions?
edit: added the background (on top) for the question showing how I got there (+minor corrections)
After some API monitoring, this is what I see
When you drop a file over a .exe
file the explorer.exe
uses CreateProcess
API function to start the process, passing the executable as lpApplicationName
, and the executable and dropped file as lpCommandLine
. The lpCurrentDirectory
is set in the function call by the caller process to the folder containing the dropped file[1].
When you drop a file over a .cmd
file the explorer.exe
also uses CreateProcess
API, but in this case the lpApplicationName
is null
and the lplCommandLine
contains the batch file and the dropped file. lpCurrentDirectory
is also set to the parent folder of the dropped file[1].
When you drop a file over a .vbs
file, ShellExecuteEx
is used and the lpDirectory
field of the SHELLEXECUTEINFO
structure is null
, so, the process created inherits the current active directory of the parent process. By default the current active directory of the explorer.exe
process is %systemroot%\system32
, but it is possible to start a explorer
instance with a different current active directory that will be inherited in this kind of drop operations.
[1] If we drop more than one file, the path of the file passed as first argument is used
note just for information: to test the active directory inherit the process followed was:
- Open a
cmd
instance and change the current active directory toc:\temp
- Kill all
explorer.exe
instances - From the
cmd
instance callexplorer.exe
. Thisexplorer
instance has the active directory in thecmd
window as its current active directory.
The Arguments
property holds the full paths to all items dropped on the script, so you can determine the directory of each dropped item like this:
Set fso = CreateObject("Scripting.FileSystemObject")
For Each item In WScript.Arguments
WScript.Echo fso.GetParentFolderName(item)
Next
Assuming that the working directory would be defined by what is dropped onto a script is a misguided approach. If you require that logic you can implement it in the script yourself, though, e.g. like this:
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
If you need the working directory to be the parent directory of the VBScript file you can derive that from the ScriptFullName
property:
Set fso = CreateObject("Scripting.FileSystemObject")
WScript.Echo fso.GetParentFolderName(WScript.ScriptFullName)
If I lookup the file types in the Windows registry, I see the following in their Shell\Open\Command
values:
- batfile:
"%1" %*
- cmdfile:
"%1" %*
- exefile:
"%1" %*
- VBSFile:
"%SystemRoot%\System32\WScript.exe" "%1" %*
This seems to suggest that bat
, cmd
, exe
are treated as executable on their own, maybe for historical reasons, whereas VBS is considered an ordinary script, that is only executable because its extension is registered with some executable to be called to interpret it. Pretty much the same as Python or Perl.
[Update] Indeed I proved that a Python script shows exactly the same behaviour as my VBS script: calling it from the command line providing arguments keeps the CWD, dropping a file on it causes the CWD to be C:\Windows\System32
. So my question seems to be sort of wrong, but finally it helped people to point me into the right direction for further research...
c:\windows\system32\CScript.exe
or c:\windows\system32\Wscript.exe
are the programs that run vbscript. As you can see they are in system32
.
Windows uses ShellExecuteEx to start programs - see it's rules at MSDN:
ShellExecuteEx function (Windows)
ShellExecuteEx uses CreateProcess (CreateProcessEx) to actually start a program. CreateProcess function (Windows)
Edit
CMD doesn't use the registry for it's own stuff. Those registry entries are for programs other than CMD.
CMD main goal is to be MS Dos 5 compatible while enhancing it, it will correctly run most DOS batch files. It was written by IBM engineers working on OS/2 NOT Windows.
Edit 2
The core of your problem is that you are attempting to write programs as if you are a user typing to operate a computer.
As a programmer you don't make assumptions. The easiest way to not take assumptions is to specify full paths to what you want. Your batch file shouldn't care what the current directory is. EG In CMD there is a current directory per drive for compatibility with MSDos 5 (and programs in a console tend to share them but don't have to). In Windows there is one current directory per program. The default current directory has changed over the years.
The only time you should work with the current directory is if you are writing a batchfile for a user to use. EG If you type dir
it does the current directory, a batchfile meant to be a general command should work the same way.
来源:https://stackoverflow.com/questions/42057121/current-working-directory-in-a-vbscript-invoked-by-a-drag-drop-operation