current working directory in a vbscript invoked by a drag & drop operation

北城以北 提交于 2019-12-10 11:23:09

问题


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)


回答1:


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 to c:\temp
  • Kill all explorer.exe instances
  • From the cmd instance call explorer.exe. This explorer instance has the active directory in the cmd window as its current active directory.



回答2:


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)



回答3:


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...




回答4:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!