[Previous] [Table of Contents] [Next]

Accessing Files and Folders

This section introduces the objects, methods, and properties of the FileSystemObject object that you can use to access files and folders.

Listing All Subfolders in a Folder

If you need a list of all subfolders in a given folder, you can use the Folders collection of the FileSystemObject object. We'll write a simple script that retrieves the subfolders of the Windows \System folder and lists them in a dialog box (Figure 12-4).

Figure 12-4 A list of the \System subfolders in Windows 98

NOTE
You can select any folder for this experiment, but our sample uses the \System folder to restrict the number of subfolders shown in the dialog box. If you select a folder such as the Windows folder, the dialog box becomes larger than the Desktop and the user can't close the dialog box by clicking the OK button. Later in this book and in Advanced Development with Microsoft Windows Script Host 2.0, I'll introduce a more advanced solution that uses a browser window to display file entries.

The path to the target folder is set in a named constant in the program's header:

Const path = "%WINDIR%\System"

To access the folder, you create a FileSystemObject object by using the following statement:

Set fso = WScript.CreateObject("Scripting.FileSystemObject")

You then use the GetFolder method to access the folder. Because the named constant path contains the placeholder %WINDIR%, you must expand this environment variable by using ExpandEnvironmentStrings:

Set oFolders = fso.GetFolder(wsh.ExpandEnvironmentStrings(path))

The GetFolder method of the FileSystemObject object variable fso returns a reference to the current folder as an object. You can then use the SubFolders property of the object variable oFolders to get the subfolders collection:

Set oSubFolders = oFolders.SubFolders

You can retrieve the name of a subfolder by using the Name property of a collection item. The following loop processes all folders in the collection and adds the folder name to the Text variable:

For Each oFolder In oSubFolders         ' All folders
    Text = Text & oFolder.Name & vbCrLf
Next

When the loop terminates, the variable Text contains the names of all subfolders found in the current folder. (Of course, you can use different code within the loop to implement a function to evaluate the folder names. This sample adds the name to a string for simplicity.) The full VBScript program is shown in Listing 12-5.

Listing 12-5 Folders.vbs

'************************************************
' File:    Folders.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Listing all subfolders of a folder by using
' FileSystemObject
'************************************************
Option Explicit

Const path = "%WINDIR%\System"       ' For Windows 95 and Windows 98
' Const path = "%WINDIR%\System32"   ' For Windows NT and Windows 2000

Dim Text, Title
Dim fso, oFolders, oFolder, oSubFolders, wsh ' Object variables

Text = "Folders" & vbCrLf & vbCrLf
Title = "WSH sample - by G. Born"

Set wsh = WScript.CreateObject("WScript.Shell")

' Create FileSystemObject object to access the file system.
Set fso = WScript.CreateObject("Scripting.FileSystemObject")

' Get Folders collection.
Set oFolders = fso.GetFolder(wsh.ExpandEnvironmentStrings(path))  
Set oSubFolders = oFolders.SubFolders

For Each oFolder In oSubFolders     ' All folders
    Text = Text & oFolder.Name & vbCrLf
Next

MsgBox Text, vbOKOnly + vbInformation, Title

'*** End

The JScript implementation

The JScript version has a similar structure. The most important difference is in the way the collection is processed. The following statements create the folder object and then the collection by using the SubFolders property:

var oFolders = fso.GetFolder(wsh.ExpandEnvironmentStrings(path));
var oSubFolders = new Enumerator(oFolders.SubFolders);

You must then use the for loop to process all entries in the collection:

for (; !oSubFolders.atEnd(); oSubFolders.moveNext())   // All folders
{
    var oFolder = oSubFolders.item();
    Text = Text + oFolder.name + "\n";
}

Notice that the code uses the moveNext method to access the next item in the collection. This method is particular to JScript because a construction such as for (oFolder in oSubFolders) doesn't work. You must retrieve an item from the collection by using the item property, as shown in the following statement:

var oFolder = oSubFolders.item();

The name property of this object returns the folder's name. In addition to the folder name, you can access properties such as the file attribute and creation date.

Alternatively, you can retrieve the path to the system folder in such a way that the script is independent of the operating system. The VBScript sample above uses the path constant to define the path to the operating system's system folder. This folder is the subfolder of the Windows folder and is named \System in Windows 95, Windows 98, and Windows Millennium Edition and \System32 in Windows NT and Windows 2000. Instead, you can use the FileSystemObject object's GetSpecialFolder method to retrieve the path to a special folder:

var path = fso.GetSpecialFolder(1);

If the value 1 is passed to the method, GetSpecialFolder returns the path to the system folder. The value 0 returns the path to the Windows folder, and the value 2 retrieves the path to the Temp folder.

The full JScript program is shown in Listing 12-6.

Listing 12-6 Folders.js

//************************************************
// File:    Folders.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Listing the contents of a subfolder
// by using the FileSystemObject
//************************************************

var Text = "Folders\n\n";

var wsh = WScript.CreateObject ("WScript.Shell");

// Create FileSystemObject object to access file system.
var fso = WScript.CreateObject("Scripting.FileSystemObject");

// Retrieve the operating system's system folder.
var path = fso.GetSpecialFolder(1);  // 1 = system folder

// Fetch Folders collection.
var oFolders = fso.GetFolder(wsh.ExpandEnvironmentStrings(path));
// Subfolders collection 
var oSubFolder = new Enumerator(oFolders.SubFolders);  

for (; !oSubFolder.atEnd(); oSubFolder.moveNext())   // All folders
{
    var oFolder = oSubFolder.item();
    Text = Text + oFolder.name + "\n";
}

WScript.Echo(Text);

//*** End

Creating, Moving, Renaming, and Deleting Folders

The next sample creates a folder, shows the subfolders of the parent folder, and removes at least the new folder.

Creating a new folder

In VBScript, you can use the InputBox function to create a new folder. The following lines also ask for the new folder's name (path):

path = InputBox("Enter folder name (e.g. C:\Born).", _
                Title, "C:\Born")
If path = "" Then
    WScript.Quit         ' Canceled by the user.
End If

The code sequence examines the user input. The script terminates if the user clicks the Cancel button. In JScript, you can use the WSHInputBox method to implement the same input dialog box (as shown in previous chapters).

Now you can create the new folder. Our sample also uses the FolderExists method of the FileSystemObject object to check whether the folder already exists:

Set fso = WScript.CreateObject("Scripting.FileSystemObject")
If (Not fso.FolderExists(path)) Then
    
End If

The FolderExists method requests the path to the folder as a parameter. If the folder exists, the method returns the value True; otherwise, it returns False.

NOTE
If the user types an illegal pathname (such as C:\Born/\test, which contains invalid characters), WSH raises a run-time error. I omitted run-time error handling from the sample for simplicity.

If you're sure that the folder doesn't exist, you can use the CreateFolder method to create the empty folder by using the following code:

Set oNewFolder = fso.CreateFolder(path)

The method is applied to the object variable fso, which contains the FileSystemObject object. The method requests a path (which includes the folder name, as in C:\Born) as a parameter. The method creates the folder and returns the object reference (containing the handle) to this folder. You must use this object reference to access the folder.

NOTE
You must be sure that the path (including the folder name) is valid. The target medium must also be writable.

Retrieving the parent folder

A folder usually belongs to a parent folder. You can separate the parent folder name from the path, but the FileSystemObject object provides a method for this purpose:

parent = fso.GetParentFolderName(path)

The GetParentFolderName method requires the path to the current folder as a parameter. By default, the method returns the path to the parent folder. If no parent folder exists (for example, if the path points to the root), the method returns an empty string. You can determine whether a parent folder exists by using the following code:

If (parent = "") Then
    MsgBox "No parent folder"
    
End If

The sample uses this method to retrieve the parent folder and show the path in a dialog box.

NOTE
As mentioned earlier, if you select a parent folder that contains too many subfolders, the dialog box showing the folder can get larger than the Desktop and the user won't be able to use the OK button to close the dialog box. Later in this chapter, I'll introduce a solution that uses a browser window instead of a dialog box to display large amounts of information.

Renaming, copying, or moving a folder

You can use the CopyFolder method of the FileSystemObject object to copy an entire folder with its content:

Object.CopyFolder source, destination[, overwrite]

The Object identifier is the placeholder for the object variable of the FileSystemObject object. The three method parameters specify the source, the destination folder, and the overwrite mode:

You can use wildcard characters only in the last name in the path. For example, you can use this statement:

FileSystemObject.CopyFolder "C:\Document\*", "C:\temp\"

But this command causes a run-time error:

FileSystemObject.CopyFolder "C:\Document\*\*", "C:\temp\"

If the source folder contains wildcard characters or if the path to the target folder ends with a \ character, the method assumes that the target folder exists and that the folder, files, and subfolders should be copied. Otherwise, a new target folder is created.

The success of a CopyFolder call depends on the particular combination of source and target folders. If the target folder doesn't exist, it is created and the content of the source folder is copied. This is the default action. If the destination path points to an existing file, a run-time error occurs. If it points to a folder, the method tries to copy the content of the source folder to the destination folder. If a file from the source folder already exists in the target folder and if the overwrite parameter is set to False, a run-time error occurs. If overwrite is set to True, existing files in the target folder are overwritten. If the Read-only file attribute is set for the target folder, a run-time error occurs unless the overwrite parameter is set to True. A run-time error also occurs if the source folder contains wildcards that don't match a folder in the source path.

IMPORTANT
When the CopyFolder method detects an error, execution terminates. The method doesn't support a rollback feature; files and folders already copied aren't deleted in the target folder. Basically, this method behaves the same way Windows or MS-DOS behaves when copying folders.

You rename and move folders by using the MoveFolder method, which uses the same parameters as the CopyFolder method. The files are moved and the content of the source folder is deleted.

NOTE
You can also set the name attribute of a folder to a new value to rename that folder. Let's say that the statement Set oFolder = fso.GetFolder("C:\Test") is used to retrieve a reference to the given folder. You can rename the folder Test1 by using the command oFolder.name = "Test1". The statement fails if a folder with the new name already exists.

Our sample uses the MoveFolder method to rename a folder. The original folder name is passed in the source path parameter, and the target path parameter must contain the new folder name:

If (MsgBox("Rename folder " & path & " ?", _
           vbYesNo + vbQuestion, Title) = vbYes) Then
    ' Yes
    newpath = path & "New"
    fso.MoveFolder path, newpath

    MsgBox "Folder renamed to " & newpath
End If

Before the folder is renamed, the script asks the user to confirm the action. This request for confirmation prevents the accidental renaming of important folders (such as the Windows folder). The variable path contains the source path, and the variable newpath contains the path to the target folder. (The name of the target folder is created by appending the string "New" to the old folder name.)

TIP
The MoveFolder method terminates if an error occurs. This behavior leaves the source and target folders in an indeterminate state because MoveFolder deletes each item (file or subfolder) from the source folder as it is copied. If you want to restore the source folder, you have to do the rollback manually. Therefore, I recommend using the CopyFolder method instead of MoveFolder. If the content of the source folder is copied successfully, you can delete its content. Using the CopyFolder method ensures that if an error occurs, the content of the source folder is kept intact.

Deleting a folder

You use the DeleteFolder method of the FileSystemObject object to delete a folder. The method requires a string as a parameter; the string must contain the folder name and the path to the folder, as shown here:

If (MsgBox("Delete folder " & newpath & "?", _
           vbYesNo + vbQuestion, Title) = vbYes) Then _
    fso.DeleteFolder(newpath)

The method supports a second (optional) parameter, force, which isn't used in the statement shown above. If this parameter is set to True, files with the Read-only attribute are also deleted. The method deletes the entire folder, including files and subfolders.

Listing 12-7 uses all the methods for manipulating folders that we've covered so far—except the CopyFolder method. The script asks the user for a folder name and then creates the folder. The subfolders of the parent folder are then shown in a dialog box. The folder can be renamed and deleted in later steps. The script creates several user dialog boxes to keep the user informed.

Listing 12-7 Folder1.vbs

'************************************************
' File:    Folder1.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Creating a folder
'************************************************
Option Explicit

Dim isnew
Dim path, newpath, parent
Dim Text, Text1, Title, oFolder
Dim fso, oFolders, oSubFolder, wsh         ' Object variables

Text = "Folders" & vbCrLf & vbCrLf
Title = "WSH sample - by G. Born"

' Query folder name.
path = InputBox("Enter folder name (e.g. C:\Born).", _
                Title, "C:\Born")
If path = "" Then
    WScript.Quit         ' Canceled by the user.
End If

newpath = path          ' Save path (for deletion).

' Create FileSystemObject object to access file system.
Set fso = WScript.CreateObject("Scripting.FileSystemObject")

' Check whether the folder exists.
If (Not fso.FolderExists(path)) Then
    ' Folder doesn't exist; create it.
    Set oFolders = fso.CreateFolder(path)
    MsgBox "Folder " & path & " created"
    isnew = True        ' Keep in mind that the folder is new.
Else
    isnew = False       ' Keep in mind that the folder already exists.
End If

If (MsgBox("List parent folders?", _
           vbYesNo + vbQuestion, Title) = vbYes) Then
    ' Fetch parent folder.
    parent = fso.GetParentFolderName(path)
 
    If (parent = "") Then
        MsgBox "No parent folder"
    Else
        ' Get Folders collection.
        Set oFolders = fso.GetFolder(parent)  
        Set oSubFolder = oFolders.SubFolders

        For Each oFolder In oSubFolder          ' All folders
            Text = Text & oFolder.Name & vbCrLf
        Next

        MsgBox Text, vbOKOnly + vbInformation, Title
    End If
End If

If isnew = False Then
    MsgBox "Attention! The next steps might delete your folder " & _
           newpath & vbCrLf & _
           "Do not delete system folders!", _
           vbYes + vbExclamation, Title
End If 

' Rename folder using the MoveFolder method.
If (MsgBox("Rename folder " & path & " ?", _
           vbYesNo + vbQuestion, Title) = vbYes) Then
    ' Yes, the user agrees.
    newpath = path & "New"
    fso.MoveFolder path, newpath

    MsgBox "Folder renamed " & newpath
End If

' Delete folder.
If (MsgBox("Delete folder " & newpath & " ?", _
           vbYesNo + vbQuestion, Title) = vbYes) Then
    ' User has accepted.
    fso.DeleteFolder(newpath)
End If 

'*** End

The JScript implementation

In JScript, the Popup method has an additional time-out parameter (which isn't present in the VBScript MsgBox function). The full JScript implementation is shown in Listing 12-8.

Listing 12-8 Folder1.js

//************************************************
// File:    Folder1.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Creating a folder
//************************************************

var vbOKCancel = 1;       // Declare variables.
var vbOK = 1;
var vbYes = 6;
var vbCancel = 2;
var vbYesNo = 4;
var vbQuestion = 32;
var vbInformation = 64;

var Text = "Folders\n\n";
var Title = "WSH sample - by G. Born";

// Get a few objects that we need here.
var wsh = WScript.CreateObject("WScript.Shell");
var objAdr = WScript.CreateObject("WSHExtend.WinExt");
var fso = new ActiveXObject("Scripting.FileSystemObject");

// Query folder name.
var path = objAdr.WSHInputBox("Enter folder name (e.g. C:\\Born).",
                              Title, "C:\\Born");
if (path == "")
    WScript.Quit();         // Canceled by the user.

var newpath = path;         // Save path (for deletion).

// Check whether the folder exists.
if (!fso.FolderExists(path))
{
    // Folder doesn't exist; create it.
    var fo = fso.CreateFolder(path);
    WScript.Echo("Folder " + path + " created");
    var isnew = true;   // Keep in mind that the folder is new.
}
else
    var isnew = false;  // Keep in mind that the folder 
                        // already exists.

if (wsh.Popup("List parent folders?", 0, Title,
              vbYesNo + vbQuestion) == vbYes) 
{
    var parent = fso.GetParentFolderName(path) // Parent folder
    if (parent == "")
        WScript.Echo("No parent folder")
    else
    {   // Get Folders collection.
        var oFolders = fso.GetFolder(parent);
        var oSubFolder = new Enumerator(oFolders.SubFolders);
        // All folders
        for (; !oSubFolder.atEnd(); oSubFolder.moveNext())   
        {
            var i = oSubFolder.item();
            Text = Text + i.name + "\n";
        }

        WScript.Echo(Text);
    }
}

if (!isnew)
    WScript.Echo(
        "Attention! The next steps might delete your folder " + 
        newpath + "\n" + 
        "Do not delete system folders!");

// Rename folder using the MoveFolder method.
if (wsh.Popup("Rename folder " + path + " ?", 0, Title,
              vbYesNo + vbQuestion) == vbYes)
{ // User has accepted.
    newpath = path + "New";
    fso.MoveFolder(path, newpath);

    WScript.Echo("The folder was renamed " + newpath);
 }

// Delete folder.
if (wsh.Popup("Delete folder " + newpath, 0, Title,
              vbYesNo + vbQuestion) == vbYes)
    // User has accepted.
    fso.DeleteFolder(newpath);

//*** End

Listing All Files in a Folder

The preceding sample lists all folders in a given folder. You can use the Files collection of the FileSystemObject object to list all files in a given folder in a similar way. The next sample lists all files in the Windows folder \ShellNew in a dialog box (Figure 12-5).

You can specify the path to the target folder in a named constant in the program header:

Const path = "%WINDIR%\ShellNew"

Figure 12-5 A list of files in a given folder

To access the folder's content, you must first create the FileSystemObject object:

Set fso = WScript.CreateObject("Scripting.FileSystemObject")

You can then retrieve an object reference to the folder by using the GetFolder method. In this step, you can submit the path to the method. Because the path defined in the constant path contains the environment variable %WINDIR%, you must expand the expression by using the ExpandEnvironmentStrings method. You can do this with the following nested statements:

Set oFolder = fso.GetFolder(wsh.ExpandEnvironmentStrings(path))

After this line of code, the variable oFolder holds an object reference to the current folder. You can use this object to retrieve the Files property, which returns a collection containing all files in the given folder:

Set oFiles = oFolder.Files

You can extract the name of a file from the collection by using the Name property. The following loop retrieves all filenames and their size from the current folder:

For Each i In oFiles           ' All files
    Text = Text & i.Name & vbTab
    Text = Text & FormatNumber(i.Size, 0) & vbCrLf
Next

When the loop terminates, the variable Text contains the names of all files. To separate the filename and its size, the sample uses the named VBScript constant vbTab. If the text is shown in a dialog box, the filename and size are listed in columns. Unfortunately, the tab stops are set to default locations; I haven't found a way to adjust them (as you can see in Figure 12-6). The full sample is shown in Listing 12-9.

Listing 12-9 Files.vbs

'**************************************************
' File:    Files.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Listing all files in the Windows folder ShellNew
'**************************************************
Option Explicit

Const path = "%WINDIR%\ShellNew"

Dim Text, Title, oFile
Dim fso, oFolder, oFiles, wsh           ' Object variables

Text = "Folder " 
Title = "WSH sample - by G. Born"

Set wsh = WScript.CreateObject("WScript.Shell")

' Create FileSystemObject object to access the file system.
Set fso = CreateObject("Scripting.FileSystemObject")

' Get Folder object.
Set oFolder = fso.GetFolder(wsh.ExpandEnvironmentStrings(path))  

Text = Text & oFolder & vbCrLf & vbCrLf
Text = Text & "Name" & vbTab & vbTab & "Size" & vbCrLf
Set oFiles = oFolder.Files         ' Get Files collection.

For Each oFile In oFiles           ' All files
    Text = Text & oFile.Name & vbTab
    Text = Text & FormatNumber(oFile.Size, 0) & vbTab
    ' List the short filename (but comment out for VBScript 3.1)
    ' Text = Text & oFile.ShortName & vbCrLf
    Text = Text & vbCrLf
Next

MsgBox Text, vbOKOnly + vbInformation, Title

'*** End

The JScript implementation

Listing 12-10 shows the JScript version.

Listing 12-10 Files.js

//**************************************************
// File:    Files.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Listing all files in the Windows folder ShellNew
//**************************************************

var path = "%windir%\\ShellNew";
var Text = "Folder ";
var wsh = WScript.CreateObject ("WScript.Shell");

// Create FileSystemObject object to access the file system.
var fso = WScript.CreateObject("Scripting.FileSystemObject");

// Get Folders collection.
var oFolder = fso.GetFolder(wsh.ExpandEnvironmentStrings(path));
var oFiles = new Enumerator(oFolder.Files);   // Files collection

Text = Text + oFolder + "\n\n";
Text = Text + "Name\t\tSize\n";

for (; !oFiles.atEnd(); oFiles.moveNext())   // All folders
{
    var oFile = oFiles.item();
    Text = Text + oFile.name + "\t";
    Text = Text + oFile.size + "\t";
    // Text = Text + oFile.ShortName + "\n";
    Text = Text + "\n";
}

WScript.Echo(Text);

//*** End

Retrieving File Attributes and Dates

Once you obtain the Files collection, you can extract the file object and its properties, as shown in the previous sample. The Name property of a File object returns the name of a file. The Attributes property returns a binary value indicating the attributes set for the file. The values are shown in Table 12-3.

Table 12-3 Constants for File Attributes

Constant*ValueDescription
Normal 0 Normal file without attributes set.
ReadOnly 1 Read-only attribute is set.
Hidden 2 Hidden attribute is set.
System 4 System file.
Directory 16 Folder or directory. (Attribute can be read-only.)
Archive 32 Archive attribute is set; the file was changed since the last backup.
Alias 1024 Shortcut (.lnk) file.
Compressed 2048 Compressed file (Windows NT and Windows 2000 only).

* These constants are valid for Scrrun.dll as shipped with WSH 2.

You can combine the constant values within the Attributes property. You can obtain the file creation date, file modification date, or file last accessed date by using the DateCreated, DateLastAccessed, or DateLastModified properties, respectively, of the File or Folder objects.

The next sample retrieves some information about a file. It uses the Autoexec.bat file (which is always present in Windows 95 and Windows 98) as the default. Windows NT and Windows 2000 users can use any other text file. The sample supports drag-and-drop, so if the user drags a file's icon to the script file's icon, the script returns information about that file. Figure 12-6 shows the dialog box the script creates. It contains the values (date and attribute information) retrieved from the Autoexec.bat file.

Figure 12-6 Information about Autoexec.bat

The script in Listing 12-11 reads the Autoexec.bat file attributes and the create, modify, and access dates. The script then displays the information in a dialog box.

Listing 12-11 File1.vbs

'************************************************
' File:    File1.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Reading the attributes, creation date, modify
' date, and access date of the Autoexec.bat file 
' (Windows NT users must modify the "file"
' constant.)
'************************************************
Option Explicit

Dim fso, oFile, objArgs, Text, attrib, file

file = "C:\Autoexec.bat"  ' Specify default file.

' Try to get a file as an argument.
Set objArgs = WScript.Arguments    ' Create object.
If objArgs.Count > 0 Then _
    file = objArgs(0)              ' First argument

' Create FileSystemObject object to access the file system.
Set fso = WScript.CreateObject("Scripting.FileSystemObject")

If fso.FileExists(file) Then       ' Check whether file exists.
    Set oFile = fso.GetFile(file)  ' Get file handle object.
    ' Now we try to retrieve the file information.
    Text = "File: " & vbTab & oFile.Name & vbCrLf ' Retrieve filename.

    ' File dates (created, modified, accessed)
    Text = Text & "Created: " & vbTab & oFile.DateCreated & vbCrLf 
    Text = Text & "Modified: " & vbTab & oFile.DateLastModified & vbCrLf
    Text = Text & "Accessed: " & vbTab & _
           oFile.DateLastAccessed & vbCrLf

    ' Decode attributes.
    Text = Text & "Attributes " & vbTab
    attrib = oFile.Attributes

    ' This is the read-only attribute.
    If (attrib And &H01) > 0 Then Text = Text & "r "
    ' This is the hidden attribute.
    If (attrib And &H02) > 0 Then Text = Text & "h "
    ' This is the system attribute.
    If (attrib And &H04) > 0 Then Text = Text & "s "
    ' This is the archive attribute.
    If (attrib And &H20) > 0 Then Text = Text & "a "
    ' This is the compressed attribute in Windows NT.
    If (attrib And &H800) > 0 Then Text = Text & "c "

    WScript.Echo Text           ' Show result.
Else
    WScript.Echo "Error: File " & file & " not found"
End If 

'*** End

The JScript implementation

The JScript version is shown in Listing 12-12. The script reads the attributes and the dates and displays the information in a dialog box (Figure 12-8).

Figure 12-8 File properties displayed by JScript

By default, the script displays the Autoexec.bat properties. In WSH 2, a user can drag a file to the script file's icon to display the file's properties. You'll notice that this script creates a slightly different message box than that shown in Figure 12-6. In particular, the date properties return a value in long format (including the day of the week). Also, the CStr function isn't available in JScript. String concatenation using the + operator causes an automatic type conversion.

Listing 12-12 File1.js

//************************************************
// File:    File1.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Reading the attributes, creation date, modify
// date, and access date of the Autoexec.bat file
// (Windows NT users must modify the "file"
// variable.)
//************************************************

var file = "C:\\Autoexec.bat";  // Specify file.

// Try to retrieve parameters submitted; 
// only one filename is supported.
var objArgs = WScript.Arguments; // Create object.
if (objArgs.length > 0)          // Arguments?
    file = objArgs(0);           // Get first argument.

// Create FileSystemObject object to access file system.
var fso = WScript.CreateObject("Scripting.FileSystemObject");

if (!fso.FileExists(file))
{
    WScript.Echo("Error", file, "doesn't exist");
    WScript.Quit(1);
}

var j = fso.GetFile(file);      // Get file handle object.

// Now we try to retrieve the file information.
var Text = "File: \t\t" + j.name + "\n";   // Filename

// File dates (created, modified, accessed)
Text = Text + "Created: \t\t" + j.DateCreated + "\n";
Text = Text + "Modified: \t\t" + j.DateLastModified + "\n";
Text = Text + "Accessed: \t" + j.DateLastAccessed + "\n"; 

// Decode attributes.
Text = Text + "Attributes\t\t";
var attrib = j.Attributes;

if ((attrib & 0x01) != 0)   // Read-only attribute
    Text = Text + "r ";
if ((attrib & 0x02) != 0)   // Hidden attribute
    Text = Text + "h ";
if ((attrib & 0x04) != 0)   // System attribute
    Text = Text + "s ";
if ((attrib & 0x20) != 0)   // Archive attribute
    Text = Text + "a ";
if ((attrib & 0x800) != 0)  // Compressed attribute (Windows NT)
    Text = Text + "c ";

WScript.Echo(Text);         // Show result.
 
//*** End

TIP
The File1.wsf file on the companion CD uses a reference to the type library. This enables the program to use named constants for attributes such as ReadOnly, Hidden, and System within the code.

Solving the problem of undefined CreationDate entries

I mentioned earlier that on some of my machines the creation date of several files was undefined, causing the script to fail with a run-time error. However, there's a way to get a Files collection and process all files within a loop (to show all file properties within a folder, for example). For each item in the collection, you can access and manipulate several properties, as shown in Figure 12-9. If the date value isn't defined, the script recovers from the run-time error.

Figure 12-9 A file list with undefined file properties

Let's extend the first part of our earlier sample that lists the files of the Windows subfolder \ShellNew so that we also get the attributes and the creation date. After retrieving the files collection of this folder, you can use a simple For Each file In oFiles loop to process all files:

For Each file In oFiles
    Text = Text & file.Name               ' Add filename.
    Text = Text & " " & file.DateCreated  ' Creation date
Next

The statement Text = Text & file.Name uses the Name property to append the filename to a string. The statement Text = Text & " " & file.DateCreated should add the file's creation date to the string. Unfortunately, the code still causes a run-time error ("Invalid procedure call or argument") if the DateCreated value of the file is undefined. You need something to make the file.DateCreated statement tolerant of undefined date values.

The following procedure handles this situation. The DateCreated property is retrieved in a function:

Function DateCreatedEx(obj)
    On Error Resume Next
    Dim datex
    ' Retrieves the file creation date
    datex = i.DateCreated      ' Created 
    If Err.Number <> 0 Then datex = "unknown"
    DateCreatedEx = datex
    On Error Goto 0
End Function

The On Error Resume Next statement catches all run-time errors and causes the interpreter to continue with the next statement. If an attempt to access the DateCreated property fails, Err.Number contains a value not equal to 0. When DateCreated fails, our sample returns the string "unknown," so a run-time error caused by a missing creation date entry isn't possible. The On Error GoTo 0 statement disables inline run-time error handling. The script code is stored in a .wsf file containing a reference to the type library, so we can use named constants (such as ReadOnly, Hidden, and System) within the code to decipher the attribute values. The full implementation is shown in Listing 12-13.

Listing 12-13 File2.wsf

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- 
    File:    File2.wsf (WSH 2 sample) 
    Author:  (c) G. Born

    Reading the attributes, creation date, modify
    date, and access date of the files in ShellNew
-->

<job id="File2">
    <reference guid='{420B2830-E718-11CF-893D-00A0C9054228}'/>

    <script language="VBScript">
    <![CDATA[
        Option Explicit

        Const path = "%WINDIR%\ShellNew"

        Dim Text, attrib
        Dim wsh, fso, oFolder, oFiles, oFile

        Set wsh = WScript.CreateObject("WScript.Shell")

        ' Create the FileSystemObject to access file system.
        Set fso = WScript.CreateObject("Scripting.FileSystemObject")

        Set oFolder = _
            fso.GetFolder(wsh.ExpandEnvironmentStrings(path))  

        Set oFiles = oFolder.Files          ' Get Files collection.

        ' Set header text.
        Text = "File" & vbTab & vbTab & "Creation Date" & vbTab _
               & vbTab & "Attributes" & vbCrLf

        ' Now we try to retrieve the file information.
        For Each oFile In oFiles
            Text = Text & oFile.name & vbTab & " ["    ' Filename
            Text = Text & DateCreatedEx(oFile) & "] "  ' Date created
            Text = Text & vbTab                        ' Tab
            ' Decode attributes.  
            If (oFile.Attributes And ReadOnly) > 0 Then _
                Text = Text & "r "
            If (oFile.Attributes And Hidden) > 0 Then _
                Text = Text & "h "
            If (oFile.Attributes And System) > 0 Then _
                Text = Text & "s "
            If (oFile.Attributes And Archive) > 0 Then _
                Text = Text & "a "
            If (oFile.Attributes And Compressed) > 0 Then _
                Text = Text & "c "
            Text = Text & vbCrLf           ' New line
        Next

        WScript.Echo Text           ' Display results.

 
        ' Helper function for preventing run-time errors
        Function DateCreatedEx(obj)
            On Error Resume Next       ' Error handling on
            Dim datex
            ' Retrieves the file creation date
            datex = obj.DateCreated    'Created 
            If Err.Number <> 0 Then datex = "        unknown      "
            DateCreatedEx = datex
            On Error Goto 0            ' Error handling off
        End Function
    ]]>
    </script>
</job>

Copying and Deleting Files

You can use methods of the File object to copy, move, and delete files. Before you can access the File object, you must create the FileSystemObject object:

Set fso = CreateObject("Scripting.FileSystemObject")

To prevent run-time errors, you must check whether the file to be copied already exists. You can use the FileExists method (which we used in an earlier sample) for this purpose:

If (fso.FileExists(file1)) Then
    

The method returns the value True if the file passed in the parameter file1 exists. The parameter file1 must contain the entire path, including the filename. If you've already checked that the source file exists, you can retrieve a File object reference using the GetFile method of the FileSystemObject object:

Set oFile = fso.GetFile(file1)

The file1 parameter must also contain the path and the source filename. You can use the File object reference, contained in the object variable oFile, to apply the Copy method:

oFile.Copy file2

The Copy method requires at least one parameter containing the name of the target file. An optional second parameter, overwrite, can be set to True or False. If it's set to True, an existing file in the target folder is overwritten during the copy operation. If it's set to False, a run-time error occurs if the target exists.

To move or rename a file, you can use the Move method of the File object. This method has the same syntax as the Copy method. (Unlike the Copy method, it deletes the source file after the copy operation is finished.)

To delete a file, you use the Delete method:

Set oFile = fso.GetFile(file2)
oFile.Delete

The first line retrieves the object reference to the file specified in the parameter file2. This object reference is returned as a File object, so you can apply the Delete method to the oFile object variable to delete the file. The Delete method has an optional parameter, force. If this parameter is set to True, WSH also deletes files with the Read-only attribute set. If force is set to False and if the file is read-only, the Delete method raises a run-time error. Instead of using the two lines given above, you can combine the commands into one line:

fso.DeleteFile file2

NOTE
If you move or delete a file, be aware that the object reference to the original file is no longer valid after the method is executed.

The Copy and Move methods don't support wildcard characters. Nor does Delete, which allows you to delete only one file at a time. However, you can use a collection containing all files in a folder to process multiple files. You can also execute an MS-DOS command by using the Run method to copy, delete, move, or rename several files at a time. This command launches the MS-DOS Copy command, which supports wildcards:

wsh.Run "%COMSPEC% /c Copy C:\Born\Text*.txt C:\Born\Backup"

Setting the bWaitOnReturn parameter in the method to True (see Chapter 7) causes the script to wait until the command is finished and the process terminates. This approach has one disadvantage: the script can't handle errors caused by the commands.

Besides the methods used in the next listing, the FileSystemObject object contains other objects and methods for file handling. CopyFile also supports file copying. The first parameter (which can include wildcards) specifies the source files, and the second parameter specifies the target file.

Listing 12-14 uses these methods in VBScript. The script requires that the folder C:\Born containing the file Test.txt already exists. The file Test.txt is copied within the folder to Test1.txt. On request, the file is also deleted.

Listing 12-14 File3.vbs

'************************************************
' File:    File3.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Copying and deleting files
'************************************************
Option Explicit

Const path = "C:\Born"                 ' Test folder (must exist)
Const file1 = "C:\Born\Test.txt"       ' Source file (must exist)
Const file2 = "C:\Born\Test1.txt"      ' Target file 

Dim Text, Title, i 
Dim fso, oFile, oFolder                ' Object variables

Text = "File copied" & vbCrLf
Title = "WSH sample - by G. Born"

' Create FileSystemObject object to access file system.
Set fso = CreateObject("Scripting.FileSystemObject")

' Check whether file exists.
If (fso.FileExists(file1)) Then
    ' Copy file1 to file2 (or use fso.CopyFile file1, file2).
    Set oFile = fso.GetFile(file1)      ' Get File object.
    oFile.Copy file2, True              ' Overwrite existing target.

    Set oFolder = fso.GetFolder(path)
    Set oFile = oFolder.Files           ' Get Files collection.
    For Each i In oFile                 ' All files
        Text = Text & i.Name & vbCrLf
    Next

    MsgBox Text, vbOKOnly + vbInformation, Title
Else
    WScript.Echo "File " & file1 & " doesn't exist"
    WScript.Quit
End If

' Delete file upon request.
If (MsgBox("Delete file?", vbYesNo, Title) = vbYes) Then
    Set oFile = fso.GetFile(file2)      ' Fetch File object.
    oFile.Delete       ' (or use fso.CopyFile file2)
    WScript.Echo "File " & file2 & " deleted"
End If

'*** End

The JScript implementation

The JScript implementation, shown in Listing 12-15, uses the same methods as the VBScript sample. The Test.txt file in the C:\Born folder is copied to Test1.txt, and then the file is deleted if the user clicks Yes.

Listing 12-15 File3.js

//************************************************
// File:    File3.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Copying and deleting files
//************************************************
var vbYesNo = 4;       
var vbYes = 6;
var vbInformation = 64;
var vbCancel = 2;

var path = "C:\\Born";
var file1 = "C:\\Born\\Test.txt";
var file2 = "C:\\Born\\Test1.txt";

var Text = "File copied\n";
var Title = "WSH sample - by G. Born";

var obj = WScript.CreateObject("WScript.Shell");

// Create FileSystemObject object to access file system.
var fso = WScript.CreateObject("Scripting.FileSystemObject");

// Check whether file exists.
if (fso.FileExists(file1)) 
{
    // Copy file1 to file2.
    var oFile = fso.GetFile(file1);            // Get File object.
    oFile.Copy (file2, true);                  // Overwrite target.

    var oFolder = fso.GetFolder(path);
    var oFile = new Enumerator(oFolder.Files); // Files collection
    for (; !oFile.atEnd(); oFile.moveNext())   // All files
    {
        var i = oFile.item();
        Text = Text + i.Name + "\n";
    }
    WScript.Echo(Text);
}
else
{
    WScript.Echo("File " + file1 + " doesn't exist");
    WScript.Quit(1);
}

// Delete file on request.
if (obj.Popup("Delete file?", 0, Title, vbYesNo) == vbYes)
{
    var oFile = fso.GetFile(file2);    // Get File object.
    oFile.Delete(); 
    WScript.Echo("File " + file2 + " deleted");
}

//*** End

Backing Up Folders

Using a few statements, you can create your own folders for a daily backup. The folder names can include the current date. The next sample, a simple VBScript program, asks whether a folder should be backed up (Figure 12-10, left). The script assumes that the source folder C:\Born exists. If the folder is missing, the script terminates with a message dialog box (Figure 12-10, middle).

Click to view at full size.

Figure 12-10 Dialog boxes of the backup program

If the source folder exists, the script determines whether the folder contains files. If the folder is empty, no backup is necessary and a message box to that effect is shown. If the folder contains files, the script creates the target folder C:\Backup\Copyxx-yy-zzzz, copies the files to that folder, and displays a dialog box confirming the copy (Figure 12-10, right). The characters xx-yy-zzzz are a placeholder for the current date in the format day-month-year. For every day, a target folder is created as a subfolder of C:\Backup. If you do several backups per day, the files are overwritten in the target folder. The script assumes that the files aren't in use; it doesn't include run-time error handling in case the files are in use.

You set the paths for the target and destination folders in the constants path1 and path2 in the program's header, so it's easy to customize this script for your own purposes. The files should be backed up to the target folder whose name includes the current date.

You set the path to the folder by using the following statement:

file2 = path2 & Day(Date) & "-" & Month(Date) & "-" & Year(Date)

The constant path2 is set in the program's header to C:\Backup\Copy. The functions Day(Date), Month(Date), and Year(Date) append the current date to this path.

You might wonder why I didn't use this statement to create the path:

file2 = path2 & Date

This command is short, but it causes a problem: the user can define separators within the date format at the operating-system level, and not all of these separator characters (such as the backslash, \) are valid filename characters. In the previous statement, the Date function doesn't return valid filenames. The path C:\Backup\Copy2\22\1998, for example, would be interpreted as a folder hierarchy. This folder hierarchy would be created automatically, but the result wouldn't be what you'd expect. The statement above extracts the day, month, and year from the date value and inserts the separators manually. It's up to you to combine the date to create a valid directory name. (I write the date in European format, in which the day precedes the month, and merge it into a folder name.)

Next, you must check whether the source folder exists. If it doesn't, the script terminates with an error message. The following sequence uses the FolderExists method for this purpose:

If (Not fso.FolderExists(path1)) Then
    Text = "Folder " & path1 & " doesn't exist."
    MsgBox Text, vbOKOnly + vbInformation, Title
    WScript.Quit
End If

You also need to check whether the source folder is empty. (If it is, you don't need to copy any files.) Applying the CopyFile method to an empty folder causes a run-time error. I tried to use the oFiles.Count item of the collection to get the number of entries, but I got a run-time error. To test whether a folder is empty, you should use the following sequence:

If FolderEmpty(path1) Then 
    

Function FolderEmpty(name)
    Dim fso, oFolder, oFiles        ' Object variables
    Dim i, flag

    Set fso = CreateObject("Scripting.FileSystemObject")
    Set oFolder = fso.GetFolder(path1)    ' Get folder.
    Set oFiles = oFolder.Files            ' All files
    flag = True                           ' Set flag to "no files."
    For Each i In oFiles
        flag = False                      ' File found; toggle flag.
        Exit For
    Next
    FolderEmpty = flag
End Function

The function FolderEmpty requests the folder's name—a string containing a valid path, including the folder name. You use this path to obtain a Files collection. A For Each i In oFiles loop is processed only if the collection contains files. In this case, the variable flag is set to False (indicating that a file was found). If the folder doesn't contain files, the function returns True.

NOTE
The function doesn't check whether the folder exists. You can extend the sample to include this error check as well. I also omitted a test for subfolders because the FileCopy method can be applied only to files in the current folder.

Before doing the backup, you must check whether the target folder exists. If the folder doesn't exist, you must create it. First, you create the root folder C:\Backup. Then you create the new destination subfolder by using CreateFolder:

If (Not fso.FolderExists(file2)) Then
    ' Does parent folder exist?
    If (Not fso.FolderExists(path3)) Then _
        fso.CreateFolder(path3) ' First step

    fso.CreateFolder(file2)     ' Create backup folder.
End If

The FolderExists method of the FileSystemObject object requires the folder name as a parameter. The method checks whether the folder is available in the given (valid) path and returns the value True for an existing folder. After these checks, you use this command to copy the files:

fso.CopyFile path1 & "\*.*", file2

The CopyFile method is applied to the FileSystemObject object. The first parameter passed to the method contains the source (which in this sample is the path to the source folder, ending with wildcard characters). This parameter ensures that all files in the source folder are copied. Be aware that the method doesn't process subfolders. Also, the source folder must contain at least one file. The mandatory second parameter specifies the target. If this parameter contains a filename appended to the path, the method renames the copied files.

To execute the script, you must create a folder C:\Born that contains at least one file. After the script runs, your hard disk should contain a new folder named Backup that contains a subfolder with the current date in its name (Figure 12-11).

Figure 12-11 Source and target folders

The full VBScript implementation is shown in Listing 12-16.

Listing 12-16 Backup.vbs

'****************************************************
' File:    Backup.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Searching the folder C:\Born and, if the folder 
' contains files, backing it up to the target folder
' C:\Backup\Copyxx-yy-zzzz (where xx-yy-zzzz stands 
' for the current date)
'****************************************************
Option Explicit

Const path1 = "C:\Born"
Const path2 = "C:\Backup\Copy"
Const path3 = "C:\Backup\"

Dim Text, Title,  destination 
Dim fso                     ' Object variable
Dim flag

Text = "Backup folder " & path1 & "?"
Title = "WSH sample - by G. Born"

If (MsgBox(Text, vbYesNo + vbQuestion, Title) = vbNo) Then
    WScript.Quit         ' Cancel selected.
End If

' Create the name of the destination folder. Because
' localized versions of Windows can use illegal filename
' characters (such as \ and :) in the date format,
' create the name manually from the date.
destination = path2 & Day(Date) & "-" & Month(Date) & _
              "-" & Year(Date)

' Create FileSystemObject object to access the file system.
Set fso = CreateObject("Scripting.FileSystemObject")

' Test whether the source folder exists.
If (Not fso.FolderExists(path1)) Then
    Text = "Folder " & path1 & " not found!"
    MsgBox Text, vbOKOnly + vbInformation, Title
    WScript.Quit
End If

' Check whether there are files in the source folder. If
' not, the copy operation causes a run-time error.
' We don't need to create the destination folder if the
' source is empty.
If FolderEmpty(path1) Then 
    Text = "Nothing to save in " & path1
    MsgBox Text, vbOKOnly + vbInformation, Title
    WScript.Quit
End If

' Check whether the target folder exists.
If (Not fso.FolderExists(destination)) Then
    ' Does parent folder exist?
    If (Not fso.FolderExists(path3)) Then _
        fso.CreateFolder(path3)    ' First step

    fso.CreateFolder(destination)  ' Create backup folder.
End If

' Copy files from source to target.
fso.CopyFile path1 & "\*.*", destination

Text = "Folder " & path1 & " is backed up to " & destination 
MsgBox Text, vbOKOnly + vbInformation, Title

' Check for an empty folder.
Function FolderEmpty (name)
    Dim fso, oFolder, oFiles         ' Object variables
    Dim i, flag

    Set fso = CreateObject("Scripting.FileSystemObject")
    Set oFolder = fso.GetFolder(path1)  ' Get folder.
    Set oFiles = oFolder.Files          ' Get Files collection.
    flag = True                         ' No files
    For Each i In oFiles                ' A file is found if
        flag = False                    ' the loop is processed.
    Next
    FolderEmpty = flag                  ' Return result.
End Function

'*** End

I'll leave the JScript implementation up to you as an exercise.

NOTE
As I mentioned earlier, the FileSystemObject object has several methods and subobjects that you can use to copy files or folders. Keep in mind that the Copy method provided by the subobjects requires only the target as a parameter—the source is implied by the subobject. If a method raises a run-time error during a copy or move operation, you should check whether a backslash character terminates the folder's path, even if the folder and files exist.