Iterating files in a directory without using FindFirstFile - windows

On the Windows operating system, how do you iterate the files in a given directory without the use of third-party libraries or the use of FindFirstFile, FindNextFile, etc?

Given that FindFirstFile is the main file enumeration method in Windows, what else do you expect to get? There are NT API functions (ZwQueryDirectoryFile and similar), which are called by FindFirstFile, but they are more complicated and don't give real benefit.

If you are able to use managed code the programming is a little easier with Directory.EnumerateFiles.
Returns an enumerable collection of
file names in a specified path.

The only additional way which i know to list the files in a directory is using the Scripting.FileSystemObject object which is part of the Windows Scripting library. you can use this object in C++, delphi or any language which supports access to COM.
check this sample code in vbscript
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "C:\test"
Set objFolder = objFSO.GetFolder(objStartFolder)
Set colFiles = objFolder.Files
For Each objFile in colFiles
Wscript.Echo objFile.Name
Next

Related

VBScript get temp folder

fso.GetSpecialFolder(2) correctly returns the temp folder for me, when run from a VBS file. However, when run from within an Autodesk Revit journal file, which has historically been VBS compliant, I get back the proper path and a GUID after Temp. I had never seen this before, and I am unsure if this is perhaps a known issue in newer builds of Windows 10 (it's been about three years since I tested this), or is this more likely an issue with Autodesk's implementation of VBScript support? I suspect the latter.
Which then raises the question, is there another good way to get the full temp path in VBScript?
I could use
Dim strUser : strUser = CreateObject("WScript.Network").UserName
"C:\Users\" & strUser & "\AppData\Local\Temp"
It's just been so long since I played with VBS, I'm not remembering if there is a better answer, or this is the consistently workable way to go. And, more than anything I want to know if .GetSpecialFolder(2) is broken in some way in Windows, or just by Autodesk.
Isn't it logical to have different values on different environments / scripting hosts?
GetSpecialFolder(2) simply returns the process environment variable named TMP. Any change on that variable -which is quite legal-, affects the value that GetSpecialFolder(2) returns.
GetSpecialFolder method
Constant: TemporaryFolder
Value: 2
The Temp folder is used to store temporary files. Its path is found in the TMP environment variable.
Since GetSpecialFolder(2) will always return an existing directory path I probably would use it with the thought that this is intended by the environment; the Autodesk Revit.
Other than that, I'd use something like below if I want the usual temporary path because even they are rare, there are installations where system drive is not C:. Relying on %localappdata% makes more sense in that manner.
Set WshShell = CreateObject("Wscript.Shell")
TempPath = WshShell.ExpandEnvironmentStrings("%localappdata%\Temp")

Get File Path of Selected File

I have a VBScript which opens an excel file and takes data from it. The path to the excel file is hard coded so the file must be named the same and must be in the same location.
Set objWorkbook = objWbs.Open("C:\Users\name\Desktop\form.xls")
I want to make it so that when the script is run it shows an explorer where the used can then choose the file that they want to use and it would take the path of that file and use that path instead of the hard coded one.
I searched before asking the question but could not find a solution. Is this possible? or a similar solution and how would I go about doing it?
Thank you.
This does not work for all file types, but curiously .xls is one of the supported files.
Set objShell = CreateObject("Shell.Application")
On Error Resume Next
Set objFile=objShell.BrowseForFolder(0, "Choose a XLS file:", &h4001&, "c:\")
If Not objFile is Nothing Then
WScript.Echo objFile.Title
WScript.Echo objFile.self.Path
End If

XOJO simple file copy

Very basic thing I am trying to accomplish.
A have a source of a file (image) stored as string (simple path).
I want to copy that file to custom destination. to be more precise to a folder name image which is located in the application root. I have checked documentation, and all of them refer to FolderItem class, which unfortunately, I cant figure out.
Any Ideas?
The FolderItem class has a built-in FileCopy method and I'd recommend learn FolderItem because it makes file handling so much easier in the long run because it's really the only way to do it in Xojo/Real Studio.
Generally the folderitem class is initialized by using the GetFolderItem method:
dim f as folderitem = GetFolderItem("somefile.pdf")
This basic function looks for the pdf file in the same directory as the executable. If the file is somewhere else you can use the Absolute Path like "C:/SomeFolder/somefile.pdf".
There are some proscribed locations that are meant to be accessed a lot (Application Data, Preferences, etc) and the easiest way to get to them is use the SpecialFolders object. If your files was in the Application Data folder you would access it:
dim f as folderitem = SpecialFolder.ApplicationData.child("somefile.pdf")
SpecialFolder.ApplicationData returns a folderitem and child looks for the file. Folderitem child and parent methods are very important to learn.
There are many examples of how to use GetFolderItem at https://docs.xojo.com/index.php/GetFolderItem
SpecialFolder explained at https://docs.xojo.com/index.php/SpecialFolder
If you want video training, subscribers can get over 40 hours of Real Studio and Xojo training at http://www.bkeeney.com/XojoTraining/xojotraining.cgi
If you are not used to object-oriented syntax, just think of FolderItem as the thing where the copy command is. So below, we make two FolderItem objects: one for the source file, and one for the destination folder. Once that is done, we can use the CopyFileTo() method of FolderItem to copy the file:
dim s as String
dim source as FolderItem
dim dest as FolderItem
s="C:\test.jpg"
source=GetFolderItem(s)
dest=GetFolderItem("C:\image")
source.CopyFileTo(dest)
The FolderItem class can represent any file or folder on the machine. To create a FolderItem instance for a particular absolute path, pass the path to the GetFolderItem method and store the result:
Dim SourceFile As FolderItem
SourceFile = GetFolderItem("C:\ExampleFolder\ExampleFile.txt", PathTypeAbsolute)
Once you've constructed the FolderItem you can modify its properties and call its methods to affect changes to the underlying file or directory.
To copy or move a file to another directory, you need to acquire a FolderItem representing the destination directory. Depending on the destination, you can use one of several methods to acquire the destination FolderItem.
For example,
Dim destination As FolderItem
destination = GetFolderItem("C:\DestinationExample\", PathTypeAbsolute)
or, using the SpecialFolder module:
destination = SpecialFolder.Desktop 'the user's desktop directory
or, using the parent folder of the ExecutableFile property of the App class:
destination = App.ExecutableFile.Parent 'your app's directory
Once you have both the source and destination FolderItems set up, simply call the CopyFileTo or MoveFileTo methods of the source FolderItem:
Dim SourceFile As FolderItem
SourceFile = GetFolderItem("C:\ExampleFolder\ExampleFile.txt", PathTypeAbsolute)
Dim destination As FolderItem
destination = GetFolderItem("C:\DestinationExample\", PathTypeAbsolute)
SourceFile.MoveFileTo(destination)
' or
SourceFile.CopyFileTo(destination)
Note that the CopyFileTo and MoveFileTo methods can't be used to move or copy directories, only files.
Folderitems are a way to represent a path, independently of the OS particulars. It is important if you plan on creating apps for Windows and Mac or Linux, for instance.
In Windows, a typical path is expressed as
C:\Users\MitchMatch\Desktop\myPicture.png
In Mac OS X or Linux, the same path will be :
C:/Users/MitchMatch/Desktop/myPicture.png
FolderItem also provides ways to directly access the desktop :
Dim f as folderItem = SpecialFolder.Desktop.Child("myPicture.png")
To copy a file, you can use Xojo built in FolderItem.CopyFileTo method, or shell to the system, and use a command line.
On Windows for instance, you can use
Dim s as new shell
s.execute("Copy c:\Users\MitchMatch\Desktop\myPicture.png c:\Users\MitchMatch\Pictures")
On Mac OS X and Linux, the command is CP. Note that contrary to the Xojo CopyFileTo command, the system function is able to copy an entire directory.

How can I use the common Save As dialog from VBScript?

I'd like to have my VBScript display the Windows Save As dialog box, but I could not find out how to do it.
Using this code:
Dim sfd
Set sfd = CreateObject("UserAccounts.CommonDialog")
sfd.ShowOpen
I can get an Open dialog, but there is no ShowSave method for this object (as there seems to be for a similar object in Visual Basic non-script).
I searched StackOverflow and googled for "[vbscript] save dialog" (and with "Windows Script Host"), but I only found threads about accessing common dialogs from web pages and a solution for the BrowseForFolder dialog and nothing really about calling the Save dialog.
Actually, I can use the Open dialog for my purpose, because all I need is a file name... but as I'd like to save something to the selected path, a "Save As" in the title bar of the dialog would be more appropriate.
The secret to using the common dialog from VBScript (or VBA or JScript, for that matter) is that you have to have its license installed on your machine. Certain development tools, such as Visual Basic 6, will install the license, but it's also installed by the free Microsoft HTML Help Editor (this is a pretty old app). The interesting thing is that if you install and then uninstall the HTML Help Editor, it leaves the Common Dialog License in place. For this reason I would consider the license to be freely available and so will include the registry entry it creates here in my answer:
In HKLM\Software\CLASSES\Licenses\4D553650-6ABE-11cf-8ADB-00AA00C00905, set the (Default) entry to gfjmrfkfifkmkfffrlmmgmhmnlulkmfmqkqj.
Once that's in place, you can create these dialogs from within a VBScript using code like this:
Set objDialog = CreateObject("MSComDlg.CommonDialog")
To launch a file save dialog, use the ShowSave method as in this code:
objDialog.ShowSave
Of course this object has a bunch of other methods and properties, and you'll probably want to configure the appropriate properties before launching the dialog. For example, you can set the file filter so only certain file extensions are shown in the dialog. There's a nice reference to the control on the MSDN site here: http://msdn.microsoft.com/en-us/library/aa259661%28v=vs.60%29.aspx.
Hope this helps. Let me know if you have any questions.
I can definitively say that there is no solution to show a Save As dialog box from VBScript under versions of Windows other than XP without relying on some external dependencies that you must install and register yourself. Aside from the obvious interference this causes with regards to an easy, drag-and-drop deployment of your script, it also brings up a whole host of other issues related to security and permissions, particularly by-passing UAC on the client's machine to install and register a dependency DLL.
The solutions that have been proposed so far rely either on a DLL file that just so happens to be included with Windows XP, invoking the Save As dialog box from Windows XP's User Accounts control panel, and/or installing a piece of software only for it to leave behind the MSComDlg DLL after it is uninstalled that you can then use from a VBScript. None of these solutions truly satisfy the above requirements, and none of the provided answers has even considered the possible security roadblocks that would arise with their proposed solutions, as I alluded to above.
And since you can't make calls directly to the Windows API (which ever-so-conveniently includes just such a Save As dialog) from VBScript (not only because it would pose a security risk, but also because of VBScript's loose [lack of?] typing), that pretty much leaves anyone wanting to do this out in the cold. As well, the inability to make API calls also precludes the use of any hacks like calling SetWindowText to change the caption of the Open dialog, as suggested in the question.
I realize that this is not the answer everyone was wanting. It's not even the answer I was wanting. But alas, it's the correct answer.
That being said, here are a couple of possible workarounds:
If you're leaning towards accepting any of the already-suggested answers, you've already decided to introduce an external dependency on a DLL file to your VBScript deployment. Once you've made that leap, why bother with "borrowing" or otherwise hijacking a DLL from some other source? Just make once yourself. It's trivial to wrap the native common dialog functions provided by the Windows API into an ActiveX DLL using Visual Basic 6, which can then be called by your VBScript. The risks are minimal, since almost any modern version of Windows can be expected to already have the Visual Basic run-time installed, and since you presumably already know VBScript, banging out some code in VB 6 shouldn't be a very difficult undertaking. You can include whatever custom functionality that you want, and most importantly, you'll be in complete control. You won't have to worry about other application's uninstallers removing the DLL that your script requires, you won't have to futz with installing and uninstalling some random, deprecated application, and you won't have to just cross your fingers and hope. We know, as programmers, that's never a good option.
And yes, I recommend actually wrapping the common dialog functions exposed by the Windows API, rather than relying on the common dialog OCX (comdlg32.ocx) provided by Visual Basic. It has its share of problems in Windows 7, and it's not going to get you the gorgeous new dialogs that the later versions of Windows now provide. An excellent article explaining everything you need to know about the Open and Save Common Dialog APIs and how to use them in VB 6 is available here on VBnet. Of course, if you really want to go all out, there's loads of interesting stuff you can do with common dialogs, all documented (with code!) here on VB Accelerator.
But now that I have you all convinced to write an ActiveX DLL in VB 6 that wraps the common dialog functionality to use in your VBScript, I have to ask the question: Why stop there? Once you've made the leap to writing some code in VB 6, why not move all of your code into VB 6? Sure, it's a "dead" language and all, but it's not like VBScript is terribly active either. As I mentioned before, the difference in syntax is virtually nil, and the learning curve for a VBScript developer is about as shallow as one could expect. Plus, you get all of the benefits of a full IDE, static typing, (slightly) better error handling, blah blah blah. Oh yeah, and being able to make direct calls to the Windows API functions. The only real benefit to VBScript is its ubiquity, but it's been years since you could find a computer without the VB runtime installed. Not to mention, if you're writing an application that requires common dialog boxes, you're probably engaging in a dialog with your users: The forms capability of full VB might begin to come in handy at that point. But perhaps the biggest and most important advantage of choosing to go this route is that you eliminate any need to register (or include) an external "satellite" DLL—a simple VB 6 application will run with only the EXE on any computer that has the VB run-time installed, which is included at least up through Windows 7.
And finally, in case you're all sorts of excited about moving up from the lowly VBScript to the full-featured VB 6, I feel compelled to throw another wrench into the equation: Why not move all the way up to a language like VB.NET? Again, there are all sorts of new features offered in VB.NET thanks to the .NET Framework, but it shouldn't take more than a few weeks for a decent VB/VBScript developer to begin to feel comfortable writing apps in VB.NET. They probably won't have a full understanding of the .NET Framework, and they certainly won't have developed good object-oriented design practices, but at least they will be moving in the right direction. Almost anything that you can do in VBScript (or even VB 6), you can do in VB.NET. And generally, it requires even less fuss than before, thanks to the immense functionality exposed by the .NET Framework. The drawback, of course, is that your app now requires the .NET Framework be installed on the user's computer, which isn't quite as ubiquitous as the VB 6 run-time (although it's much more common now than even just a few years ago).
So I hear you saying those weren't the workarounds you were hoping to hear? Yeah, me neither. I'm not that guy who tells people to drop everything and learn a new language. If VBScript continues to work for you, go for it. But if you're at that point where you start to strain at its limitations, it's probably time to make the leap.
If you have some degree of control over the systems on which you'll be deploying this, and can be reasonably certain that they have either Visual Studio or Microsoft HTML Help installed, you can use code like the following:
function filedialog(filt, def, title, save)
set dialog = CreateObject("MSComDlg.CommonDialog")
dialog.MaxFileSize = 256
if filt = "" then
dialog.Filter = "All Files (*.*)|*.*"
else
dialog.Filter = filt
end if
dialog.FilterIndex = 1
dialog.DialogTitle = title
dialog.InitDir = CreateObject("WScript.Shell").SpecialFolders("MyDocuments")
dialog.FileName = ""
if save = true then
dialog.DefaultExt = def
dialog.Flags = &H800 + &H4
discard = dialog.ShowSave()
else
dialog.Flags = &H1000 + &H4 + &H800
discard = dialog.ShowOpen()
end if
filedialog = dialog.FileName
end function
Also, adapting one of the other answers to this question into VBScript code (thanks #oddacorn!), you should add this function if you aren't reasonably certain that your users will have VS or HTML Help. Call this function on program startup. Don't worry if you already have the key; in that case, this has no effect. This should work on a standard user account without admin rights.
'Make the MSComDlg.CommonDialog class available for use. Required for filedialog function.
function registerComDlg
Set objRegistry = GetObject("winmgmts:\\.\root\default:StdRegProv")
objRegistry.CreateKey &H80000001, "Software\CLASSES\Licenses\4D553650-6ABE-11cf-8ADB-00AA00C00905"
objRegistry.SetStringValue &H80000001, "Software\CLASSES\Licenses\4D553650-6ABE-11cf-8ADB-00AA00C00905", "", "gfjmrfkfifkmkfffrlmmgmhmnlulkmfmqkqj"
end function
Note that I adapted the filedialog function from the "View Source" of the VBScript code in the HTML here; on modern web browsers, it appears that the HTML they use to render the code samples doesn't display correctly (tested on IE 8 and Chrome). But fortunately the code is still there in the View Source.
I found one thing that was critical to making this work on Windows 7 (SP1, fully patched); you must set dialog.MaxFileSize = 256 or you will get a run-time error.
That is, the following code fails on Windows 7 SP1, but probably works on older versions of Windows:
Set x = CreateObject("MSComDlg.CommonDialog")
x.ShowSave
On
http://blogs.msdn.com/b/gstemp/archive/2004/02/18/75600.aspx
there is a way descibed how to show an Save As dialog from VBScript.
Note that according to
http://www.eggheadcafe.com/software/aspnet/29155097/safrcfiledlg-has-been-deprecated-by-microsoft.aspx
SAFRCFileDlg has been deprecated by Microsoft.
I just made a shell, linked it to a asp website, made the website read a directional tag - which i loaded the file location into, and the asp page opens up the file dialog immediate within that file location, with the filename also specificed through directional tags. Once saved, the shell disappears.
If it's a limitation of the website direcitonal tags ie (blah.com/temp.aspx?x=0&y=2&z=3)
store the information in a SQL db, or flat files, there are a ton of workarounds, but what above is said is true. VBS won't cut it internally.
After looking for ages, I've found jsShell - Shell Component at jsware.net. The zip file contains jsShell.dll 176 kB, a vbscript to register the dll basically regsvr32.exe jsShell.dll, demo scripts and clear documentation.
The DLL works well in Windows 7 and provides several useful methods, including the Open/Save dialog:
Dim jsS, sFileName
jsS = CreateObject("jsShell.Ops")
' Save as dialog
sFileName = jsS.SaveDlg("<title>", "exe") ' Example: Filter by exe files
sFileName = jsS.SaveDlg("<title>", "") ' Example: No extension filter
' Open dialog
' Example: Filter by exe, initial dir at C:\
sFileName = jsS.OpenDlg("<title>", "exe", "C:\")
When no file is selected, sFileName is an empty string.
Private Sub cmdB1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles cmdB1.Click
Dim objExec, strMSHTA, wshShell, SelectFile
SelectFile = ""
' For use in HTAs as well as "plain" VBScript:
strMSHTA = "mshta.exe ""about:" & "<" & "input type=file id=FILE>" _
& "<" & "script>FILE.click();new ActiveXObject('Scripting.FileSystemObject')" _
& ".GetStandardStream(1).WriteLine(FILE.value);close();resizeTo(0,0);" & "<" & "/script>"""
wshShell = CreateObject("WScript.Shell")
objExec = wshShell.Exec(strMSHTA)
SelectFile = objExec.StdOut.ReadLine()
Me.txtT0.Text = SelectFile
objExec = Nothing
wshShell = Nothing
strMSHTA = Nothing
End Sub
I just found a solution in this site
It is fully commented and works well in Windows 10
Here is the code that returns the folder as a string (I tried in three different start folders):
Option Explicit
WScript.Echo BrowseFolder( "C:\Program Files", True )
WScript.Echo BrowseFolder( "My Computer", False )
WScript.Echo BrowseFolder( "", False )
Function BrowseFolder( myStartLocation, blnSimpleDialog )
' This function generates a Browse Folder dialog
' and returns the selected folder as a string.
'
' Arguments:
' myStartLocation [string] start folder for dialog, or "My Computer", or
' empty string to open in "Desktop\My Documents"
' blnSimpleDialog [boolean] if False, an additional text field will be
' displayed where the folder can be selected
' by typing the fully qualified path
'
' Returns: [string] the fully qualified path to the selected folder
'
' Based on the Hey Scripting Guys article
' "How Can I Show Users a Dialog Box That Only Lets Them Select Folders?"
' http://www.microsoft.com/technet/scriptcenter/resources/qanda/jun05/hey0617.mspx
'
' Function written by Rob van der Woude
' http://www.robvanderwoude.com
Const MY_COMPUTER = &H11&
Const WINDOW_HANDLE = 0 ' Must ALWAYS be 0
Dim numOptions, objFolder, objFolderItem
Dim objPath, objShell, strPath, strPrompt
' Set the options for the dialog window
strPrompt = "Select a folder:"
If blnSimpleDialog = True Then
numOptions = 0 ' Simple dialog
Else
numOptions = &H10& ' Additional text field to type folder path
End If
' Create a Windows Shell object
Set objShell = CreateObject( "Shell.Application" )
' If specified, convert "My Computer" to a valid
' path for the Windows Shell's BrowseFolder method
If UCase( myStartLocation ) = "MY COMPUTER" Then
Set objFolder = objShell.Namespace( MY_COMPUTER )
Set objFolderItem = objFolder.Self
strPath = objFolderItem.Path
Else
strPath = myStartLocation
End If
Set objFolder = objShell.BrowseForFolder( WINDOW_HANDLE, strPrompt, _
numOptions, strPath )
' Quit if no folder was selected
If objFolder Is Nothing Then
BrowseFolder = ""
Exit Function
End If
' Retrieve the path of the selected folder
Set objFolderItem = objFolder.Self
objPath = objFolderItem.Path
' Return the path of the selected folder
BrowseFolder = objPath
End Function
Set objDialog = CreateObject( "SAFRCFileDlg.FileSave" )
' Note: If no path is specified, the "current" directory will
' be the one remembered from the last "SAFRCFileDlg.FileOpen"
' or "SAFRCFileDlg.FileSave" dialog!
objDialog.FileName = "test_save.vbs"
' Note: The FileType property is cosmetic only, it doesn't
' automatically append the right file extension!
' So make sure you type the extension yourself!
objDialog.FileType = "VBScript Script"
If objDialog.OpenFileSaveDlg Then
WScript.Echo "objDialog.FileName = " & objDialog.FileName
End If

16-Bits BMP Validation

I'm developing a script using VBScript and I need to validate the input file as a 16-Bits BMP.
At the time my script is like this:
Const OverwriteExisting = TRUE
Set objFSO = CreateObject("Scripting.FileSystemObject")
objFSO.CopyFile "C:\16bmp.bmp" , "D:\test.bmp", OverwriteExisting
But How can I validate the input file as a 16-Bits BMP?
PS: Remember that I need this to be compatible with my site and Windows CE(I will develop a program for it using NSBasic).
I'm not sure I got you right (English being my second language), but if you need to check if a file is a 16-bit BMP image (and not verify the actual pixels), you can make use of the Windows Image Acquisition (WIA) scripting objects. Here's an example:
Const wiaIDUnknown = "{00000000-0000-0000-0000-000000000000}"
Const wiaFormatBMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}"
Set oImg = CreateObject("Wia.ImageFile")
On Error Resume Next
oImg.LoadFile("C:\image.bmp")
If oImg.FormatID = wiaIDUnknown Then
' The file isn't an image file
Else
Log.Message "Is BMP: " & (oImg.FormatID = wiaFormatBMP)
Log.Message "Color depth: " & oImg.PixelDepth
End If
This script requires that you have the wiaaut.dll library installed and registered on your computer; if you don't have it, you can download it as part of the WIA SDK.
See also WIA documentation on MSDN.
You would have to read the file data and compare it to the BMP format specification.
There are three ways I know of to work with binary files in VBScript:
Using the ADODB component. This method is kind of limited. You can find an article about it at VBScript Read Binary File.
You could write your own COM component and call it from the script. I did a quick Google search and found some ready-made components that offer this functionality.
You could also install ImageMagick and use it to identify the BMP. That might be overkill for your purposes though.

Resources