3

Consider this example VBScript fragment:

Dim fs
Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FileExists("D:\Folder\File*.ext") Then ' Finds nothing!
  fs.CopyFile "D:\Folder\File*.ext", "D:\OtherFolder\"
  fs.Deletefile "D:\Folder\File*.ext"
End If

The FileExists method turns out not to support wildcards (* and ?). Not does FolderExists. I expected wildards to just work because they work fine for all similar methods in the FileSystemObject: CopyFile, CopyFolder, MoveFile, MoveFolder, DeleteFile, DeleteFolder and the Get* filename handling methods like GetAbsolutePathName.

Of course there are ways to work around this, like GetFolder and iterating over its files. But FileExists would have been much more readable, convenient, natural and consistent.

The fs.FileExists inconsistency feels like an API design problem. What could be the reason? Is there some idea behind it?

Michel de Ruiter
  • 7,131
  • 5
  • 49
  • 74

2 Answers2

6

Only someone from the team that designed the Microsoft Scripting Runtime API (scrrun.dll), which these functions are a part of, can answer this question for sure.

But my guess is that FileExists is nothing but a wrapper for the CreateFile Windows API function with the dwCreationDisposition parameter set to OPEN_EXISTING ("Opens a file or device only if it exists."). This Windows API function does not support wildcards, so FileExists can't, either.

When the file does not exist, the system will respond with error 2 ("The system cannot find the file specified.") and FileExists will return False.

The above is based on using Process Monitor to inspect the behavior of a FileExists call.

It would be moot to discuss whether this is an API design oversight and whether it should be any different.


That being said, there is no reason for an "exists" check in the code you show.

If you want to move files from location A to location B, just do that.

If there is something to move, it will be moved. If there is nothing to move, there will be an error you can inspect. The "exists" check provides no extra information whatsoever.

Dim fs, source
Set fs = CreateObject("Scripting.FileSystemObject")

On Error Resume Next

fs.MoveFile "File*.ext", "D:\OtherFolder\"

If Err.Number = 0 Then
  MsgBox "Done"
ElseIf Err.Number = 53 Then ' File not found
  MsgBox "Nothing to do"
ElseIf Err.Number = 76 Then ' Path not found
  MsgBox "Target path not found"
Else
  MsgBox "Unexpected Error " & Err.Number & " - " & Err.Description
End If

On Error Goto 0

For convenience I would wrap that in a Sub so that I can re-use it and the On Error Resume Next won't leak into the rest of my code.

It's also worth noting that within the same volume, MoveFile will be way faster than copy-and-delete.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 2
    @MicheldeRuiter I would recommend the On Error approach too because there is a lot more that can go wrong such as read-only permissions, no permission, locked files, path too long, etc. – Slai Nov 14 '16 at 11:33
  • `FileExists` before shows my intent much better than `Err.Number = 53` after the fact. Of course I do have error handling. `CopyFile` plus `DeleteFile` was only to show examples of both. – Michel de Ruiter Nov 14 '16 at 16:13
  • 2
    Yes, but since your process is not the only one on this computer, the file could be gone in the very millisecond after a successful "file exists" check (imagine two instances of your script running at the same time for some stupid reason, or a host of other situations). The point is that unless you keep a file handle open (and thus prevent anyone from taking away your file before you are done with it), a file exists check really means nothing. It's *a lot* more robust to try whatever operation you were going to do and handle the errors than to rely on a file exists check and handle no errors. – Tomalak Nov 14 '16 at 16:23
  • 1
    I tend to define constants for all the magic numbers in my scripts. `Const FSO_FILE_NOT_FOUND = 53` etc makes your error handlers become self-documenting. – Tomalak Nov 14 '16 at 16:25
  • @Tomalak discouraging such race situations could be the exact reason! – Michel de Ruiter Feb 01 '17 at 22:54
-1

Why don't run DIR thru WSShell.Exec and capture its output?

set ows=createobject("Wscript.shell")
path="C:\windows\system32\"
wild="*.exe"
recurse="/S"  ' or ""
Set oExec=ows.Exec("%comspec% /c dir /b " & recurse &" "& chr(34) & path & wild & chr(34) )
s= oExec.StdOut.ReadAll()

'using the result
if s =vbnullstring then
   Wscript.echo "No files found" 
else
   s=split(s,vbcrlf) 
   wscript.echo "Files found " & ubound(s)
   for each i in s
      wscript.echo i
   next
   wscript.echo "End of list"
end if
Antoni Gual Via
  • 714
  • 1
  • 6
  • 14