Executive Summary:
ScriptRoundUp.vbs finds and copies scripts to a central location, making it easy to back them up and retrieve them. ScriptRoundUp.vbs uses Windows Management Instrumentation (WMI) to find all the VBScript files and HTML Application (HTA) files on the local C and D drives. You can easily modify ScriptRoundUp.vbs to search different local drives. In addition, you can easily modify it to look for different types of scripts (e.g., PowerShell scripts, JScript scripts) and even other types of files (e.g., Microsoft Word documents, Microsoft Excel spreadsheets).

I have to admit that after many years of scripting I have scripts all over the place on my computer. They're in a variety of folders on different drives. Some are well organized, and some are not. Some I forgot about, while others have been hiding out in inconspicuous locations for a very long time.

To make matters worse, PCs aren't backed up where I work. Needless to say, if I were to have a disk go bad and lose all my scripts, I would be quite upset.

Instead of trying to hunt down all my scripts and copy them to a USB drive or a network location that is backed up, I decided to round them all up with a script. ScriptRoundUp.vbs uses Windows Management Instrumentation (WMI) to find all the .vbs and .hta files on my local C and D drives. The query used in the script can easily be modified to look at different local drives and to look for other file extensions, so you could round up Windows PowerShell scripts, Microsoft Word documents, or Microsoft Excel spreadsheets by making just a slight modification. Let's look at how to use ScriptRoundUp.vbs and how it works.

How to Use the Script
Locating files using WMI is nothing new to most scripters, so what sets ScriptRoundUp.vbs apart from the many WMI scripts that you might already have? The main difference is that ScriptRoundUp.vbs makes copies of all the files meeting the specified criteria and places those copies in a centralized location so that you have all your scripts in one location. You can then copy them from the centralized location to a USB drive or network location in one fell swoop. This centralized location is hardcoded in the script as C:\Scripts\AllScripts\ScriptFiles. The script doesn't create this folder, so you must create it prior to running the script.

If you want to store the copies in a different folder, you just need to find the code

ColPath = "C:\Scripts\AllScripts\"                              DestRoot = ColPath & "ScriptFiles"

at the beginning of ScriptRoundUp.vbs. (You can download this script by clicking the Download the Code Here link at the top of the page.) ColPath is the collection path where the script creates and stores an .xml database that contains information about all the files returned by the WMI query. In this case, the C:\Scripts\AllScripts folder contains the .xml database. DestRoot specifies the destination root folder where all the scripts will be stored—in this case, it's the ScriptFiles folder. You can change this subfolder by modifying the second line, but be sure to leave the variable names ColPath and DestRoot intact. Once again, you need to create the subfolder before running the script.

VBScript files and HTML Applications (HTAs) usually don't take up a lot of space. (I have well over a thousand scripts and HTAs and they consume only about 56MB total.) However, if your C drive is extremely low on disk space, you might want to change the ColPath value to another folder location on a different drive.

The only other code you might want to modify is the WMI query

strQuery = "Select Drive,Name,Extension,Path from CIM_DataFile " & _                                "Where (Drive='c:' OR Drive='d:') AND (extension='vbs' OR extension='hta')"

This query looks for VBScript and HTA files on the C and D drives. You can easily modify this code to look for different types of files on different local drives. For example, if you want to find JScript and PowerShell files, you'd modify the statement to look like

strQuery = "Select Drive,Name,Extension,Path from CIM_DataFile " & _                                "Where (Drive='c:' OR Drive='d:') AND (extension='js' OR extension='ps1')"

After running the script (which could take a while), open the destination root folder. You should find a folder for each of the hard drives listed in the query. If you open these folders, you should see a myriad of subfolders that contain your VBScript scripts and HTAs.

Before running ScriptRoundUp.vbs a second time, I suggest that you delete all the files and folders from the C:\Scripts\AllScripts folder after you've copied them to a safe place. If you leave them on your hard drive and run the script again, you'll end up collecting those files as well, virtually doubling the time it takes to run the script and doubling the amount of space used to house the files.

I should also point out that I chose to use a database as an interim holding tank because using a database makes the process much cleaner than copying files directly from WMI collections. Plus, if I decide to extend this script's functionality (e.g., have it look for duplicate files), I would have a means to do so.

How the Script Works
As Listing 1 shows, ScriptRoundUp.vbs is relatively short and not too complex. I'll describe how it works, summarizing certain sections and elaborating on a few areas.

After setting up the reference variables and query string, the script checks to make sure both the C:\Scripts\AllScripts and C:\Scripts\AllScripts\ScriptFiles folders exist. If they don't, a message lets you know that one or both folders are missing and the script terminates.

Next, at callout A in Listing 1, the script creates a Folder object named RootFolder that's bound to the destination root folder. (This object is part of the Windows Shell API for scripting.) Using the Folder object's NewFolder method, the script can easily add subfolders to the destination root folder. The subfolder names are derived from the actual folder names that house the .vbs and .hta files.

At callout B, the script sets up an ADO database to store the file information returned by the WMI query. The database is composed of four fields, each relating to specific WMI properties:

  • The Drive field, which contains the file's drive letter (e.g., C)
  • The Extension field, which contains the filename extension (e.g., vbs)
  • The Name field, which contains the complete path and filename (e.g., C:\Scripts\RoundUp\ScriptRoundup.vbs)
  • The Path field, which contains just the path with leading and ending backslashes (e.g., \Scripts\RoundUp\)

Shortly, you'll see how these four fields play an important part in constructing the subfolder names and copying the files.

In callout C, the script executes the WMI query and stores the files that meet the criteria in a collection. A VBScript For Each…Next statement cycles through the collection. Each file's drive, extension, name and path are retrieved and stored in the appropriate fields in the database. After all the file information has been added to the database, the record pointer is positioned at the first record.

The Do…Loop statement in callout D is the heart of the script—it's where all the subfolders get created and all the files get copied. Keep in mind that the subfolders are all created in the destination root folder. So, for example, the C:\Scripts\RoundUp\ScriptRoundup.vbs file would be copied to C:\Scripts\AllScripts\ScriptFiles\C\Scripts\RoundUp\ScriptRoundup.vbs.

Let's take a close look at the Do…Loop statement. The first line

FolderPath = Replace(DRS.Fields.Item("Drive"),":","") & DRS.Fields.Item("Path")

constructs the FolderPath variable's value from the drive letter (with the colon stripped out) and the path. The resulting value would look something like C\Scripts\RoundUp\. The FolderPath variable will be used to create the subfolder to house the file. However, before that occurs, the script tests for the existence of the subfolder in FolderPath. That way, if it already exists, an attempt won't be made to create it again. The line

Dest = DestRoot & "\" & Replace(DRS.Fields.Item("Drive"),":","") & DRS.Fields.Item("Path")

sets the Dest variable, which is used for the existence test. The Dest variable's value is created basically the same way as the FolderPath variable's value, except the Dest variable's value is preceded with DestRoot (C:\Scripts\AllScripts\ScriptFiles) and would look something like C:\Scripts\AllScripts\ScriptFiles\C\Scripts\RoundUp\.

After the Dest variable is set, the existence test takes place using the code

If Not fso.FolderExists(Dest) Then                                RootFolder.NewFolder(FolderPath)                              End If

If the subfolder doesn't exist, the statement RootFolder.NewFolder(FolderPath) uses the name stored in the FolderPath variable to create the subfolder under the destination root folder.

Next, the lines

Set sourcefile = nothing                              Set sourcefile = fso.getFile(DRS.Fields.Item("Name"))                              sourcefile.Copy Dest

disassociate the sourcefile variable with any object, then set that variable to a Scripting Runtime Library File object that's bound to the file associated with the value stored in the Name field of the database. Remember that the Name field contains the full path and filename of the file that was initially returned by the WMI query. With the File object created, it's simply a matter of calling that object's Copy method to copy the .vbs or .hta file specified in the Name field into the subfolder specified in the Dest variable. This process is repeated for each record in the database until reaching the end of file, after which the database is saved as C:\Scripts\AllScripts\Scripts.xml.

That's it! All you have to do now is copy the files that you rounded up to wherever you want. Don't forget to delete them from the collection area if you plan on running the script again using the same criteria.

Listing 1: ScriptRoundUp.vbs

Const adPersistXML  = 1                              Const adFldIsNullable = 32                              Const adLongVarChar = 201                              ColPath = "C:\Scripts\AllScripts\"                              DestRoot = ColPath & "ScriptFiles"                              strQuery = "Select Drive,Extension,Name,Path from CIM_DataFile " & _                                "Where (Drive='c:' OR Drive='d:') AND (extension='vbs' OR extension='hta')"                              Set fso = CreateObject("Scripting.FileSystemObject")                              If Not fso.FolderExists(ColPath) Or Not fso.folderExists(DestRoot) Then                                Msgbox "Collection Folder " & ColPath & " or " & DestRoot & " does not exist... Terminating Script"                                WScript.Quit                              End If                              strMessage = "A message box will appear when process is complete."                              strMsgTitle = "Script Round-up"                              CreateObject("WScript.Shell").Popup strMessage,10,strMsgTitle,vbInformation                              
' Begin Callout A                              Set objShell = CreateObject("Shell.Application")                              Set RootFolder = objShell.NameSpace(DestRoot)                              ' End Callout A                              
' Begin Callout B                              Set DRS = CreateObject("ADODB.Recordset")                              DRS.Fields.Append "Drive",adLongVarChar,256,adFldIsNullable                              DRS.Fields.Append "Extension",adLongVarChar,256,adFldIsNullable                              DRS.Fields.Append "Name",adLongVarChar,256,adFldIsNullable                              DRS.Fields.Append "Path",adLongVarChar,256,adFldIsNullable                              DRS.Open                              ' End Callout B                              
' Begin Callout C                              Set objWMIService = GetObject("winmgmts:" _                                & "\{impersonationLevel=impersonate\}!\\.\root\cimv2")                              Set colFiles = objWMIService.ExecQuery(strQuery)                              For Each objFile In colFiles                                DRS.AddNew                                DRS("Drive") = objFile.Drive                                DRS("Extension") = objFile.Extension                                DRS("Name") = objFile.Name                                DRS("Path") = objFile.Path                              Next                              DRS.MoveFirst                              ' End Callout C                              
' Begin Callout D                              Do While Not DRS.EOF                                FolderPath = Replace(DRS.Fields.Item("Drive"),":","") & DRS.Fields.Item("Path")                                Dest = DestRoot & "\" & Replace(DRS.Fields.Item("Drive"),":","") & DRS.Fields.Item("Path")                                If Not fso.FolderExists(Dest) Then                                  RootFolder.NewFolder(FolderPath)                                End If                                Set sourcefile = nothing                                Set sourcefile = fso.getFile(DRS.Fields.Item("Name"))                                sourcefile.Copy Dest                                DRS.MoveNext                              Loop                              ' End Callout D                              If fso.FileExists(ColPath & "Scripts.xml") Then                                fso.DeleteFile(ColPath & "Scripts.xml")                              End If                              DRS.Save ColPath & "Scripts.xml",adPersistXML                              DRS.Close                              MsgBox "Done"