Special Edition Using Visual Basic.NET

function OpenWin(url, w, h) { if(!w) w = 400; if(!h) h = 300; window.open(url, "_new", "width=" + w + ",height=" + h + ",menubar=no,toobar=no,scrollbars=yes", true); } function Print() { window.focus(); if(window.print) { window.print(); window.setTimeout('window.close();',5000); } }
Team-Fly    

Special Edition Using Microsoft® Visual Basic® .NET

By Brian Siler, Jeff Spotts

Table of Contents
Chapter 24.  Working with Files

Windows Explorer represents the contents of your hard drive graphically using file and folder icons. Many of the actions that you perform in Explorer (such as copying or renaming files) also can be accomplished in Visual Basic .NET via the System.IO namespace. The System assembly should be automatically referenced in your project, so you just need to add the following Imports statement at the top of your form or module:

Imports System.IO

The most useful classes for performing file operations are

  • FileSystemInfo The FileSystemInfo class represents an entry in the file system, whether it is a file or a folder. You cannot create an instance of a FileSystemInfo object with the New keyword but, as you will see in a moment, it is useful when processing both files and folders at the same time.

  • FileInfo You use the FileInfo class to manipulate operating system files. Each FileInfo object you create represents a file on the disk. This class is inherited from the FileSystemInfo class and contains additional, file-specific members.

  • DirectoryInfo The DirectoryInfo class allows you to control the folders of your file system. Like the FileInfo class, the DirectoryInfo class is inherited from the FileSystemInfo class and contains additional, directory-specific members.

  • File The File class contains static methods for performing operations on operating system files.

  • Directory The Directory class contains static methods for performing operations on operating system folders.

Astute readers may have already noticed there is some duplication in the previous list of classes. For example, both the File and FileInfo classes contain methods for determining whether a file exists, deleting a file, and so on. The reason is that the Directory and File classes contain all the static methods, while the DirectoryInfo and FileInfo classes contain all the instance methods. As you may recall from Chapter 9, a static method is a method of a class that can be called without first creating an object from the class.

For more on methods, p.225

When dealing with files, static methods can be more efficient if you do not need to perform multiple operations on the same file or directory, or access its properties. By avoiding the overhead of declaring a variable and storing an object instance, you use fewer of your system resources.

The terms folder, directory, and subdirectory are often used interchangeably in the PC environment. When DOS was king, subdirectories were created in the root directory (that is, C:\WINDOWS is a subdirectory of C:\). A lot of the DOS commands include directory in their names, such as md, which stands for make directory. Many of these DOS commands are still available in Windows today in the Command Prompt window. However, inside the Windows environment you create and manipulate folders using the Windows explorer. With the introduction of VBScript Microsoft provided a Folder object, so it is interesting to find a Directory object in the System.IO namespace. Perhaps this is a nod to other operating systems such as UNIX, which uses the term directory.

Checking to See Whether a File or Directory Exists

If you try to open a database or access a file that does not exist, an error occurs. However, you can use the Exists method of the File class before opening it to check whether it is really there, as in the following example:

Dim sFileToCheck As String = "C:\data\importantstuff.dat" If Not File.Exists(sFileToCheck) Then MessageBox.Show("Cannot find the file. Please try again!") Application.Exit() End If

The Exists method accepts a file path as a parameter and returns True if the file exists. (The Exists function is also available in the Directory class to check for the existence of a folder.) Some notes regarding the behavior of the Exists function are

  • The Exists function is not case sensitive.

  • The PATH environment setting on your PC has no effect on the Exists method; you must provide a path to the file in the method call.

  • Exists accepts either a full path or one relative to the current directory. (Keep reading for more on paths and the current directory.)

  • Wildcard expressions (that is, *and ?) cannot be used with Exists.

  • UNC paths (that is, \\server\share\directory\filename) can be used with Exists, although if you do not have permission to access the remote file, False will be returned.

Note

The FileInfo and DirectoryInfo classes also contain an Exists property, which can be used if you have an object reference declared.

For more information about providing ways for users to select files, p. 342

Listing the Files and Folders in a Directory

Another frequently needed file system operation is the ability to get a list of filenames. After you have this list, you can present it to the user onscreen, store it in a database, or perform any number of other tasks. For example, the following lines of code use the GetFiles method of the Directory class to populate a listbox with the names of the executable files in the C:\WINNT folder:

Dim sFileList() As String sFileList = Directory.GetFiles("C:\Winnt", "*.exe ") ListBox1.Items.AddRange(sFileList)

The GetFiles method searches a directory for files and returns an array of strings containing the full path to each file. In order to specify the directory to be searched, you must supply at least one parameter, the directory name. If you supply an invalid directory name, an exception will be thrown. The search pattern, which is entirely optional, allows you to filter the filenames returned using wildcard expressions.

In addition to the GetFiles method, the Directory class also contains similar methods, called GetDirectories and GetFileSystemEntries, which can be used to retrieve an array of subdirectories or files and subdirectories, respectively. These static methods are easy to use and relatively efficient; however, they are not suited for more complex operations. For example, you may want to process each file as an object, using its properties such as size or extension to determine your program's course of action. The code in Listing 24.1 uses the GetFileSystemInfos method of the Directory class to retrieve a collection representing the file and directory objects file and directory in the root folder of the C drive.

Listing 24.1 ListFiles.ZIP Enumerating the Contents of a Folder

Dim dirTemp As DirectoryInfo Dim fsiTemp As FileSystemInfo dirTemp = New DirectoryInfo("C:\") lstFiles.Items.Clear() lstFiles.DisplayMember = "Name" For Each fsiTemp In dirTemp.GetFileSystemInfos() lstFiles.Items.Add(fsiTemp) Next

In Listing 24.1, we first create an instance of the DirectoryInfo class, which represents the C:\directory. Next, we call the GetFileSystemInfos method, which returns a collection of objects corresponding to each item contained in the C:\folder. Finally, each FileSystemInfo object is added to the Items collection of a list box. This process is a little more involved than the static methods described earlier, because we are processing files as objects rather than string values. However, the code in Listing 24.1 could easily be expanded to perform additional tasks using object members.

Note

If you want to work with only files or directories, but not both, use the GetFiles or GetDirectories method instead of the more generic GetFileSystemInfos method presented here. All of the previous methods also contain an optional search pattern parameter, as described earlier in this section.

For example, Listing 24.2 shows how to retrieve object properties from the list box when the user double-clicks a filename.

Listing 24.2 ListFiles.ZIP Exploring File and Directory Properties

Private Sub lstFiles_DoubleClick(ByVal sender As Object,_ ByVal e As System.EventArgs) Handles lstFiles.DoubleClick Dim fsiTemp As FileSystemInfo Dim strMessage As String fsiTemp = CType(lstFiles.SelectedItem, FileSystemInfo) If lstFiles.SelectedItem.GetType Is GetType(DirectoryInfo) Then Dim dirTemp As DirectoryInfo dirTemp = CType(fsiTemp, DirectoryInfo) strMessage = "You just clicked on a directory." strMessage &= "The directory's parent is " & dirTemp.Parent.ToString ElseIf lstFiles.SelectedItem.GetType Is GetType(FileInfo) Then Dim filTemp As FileInfo filTemp = CType(fsiTemp, FileInfo) strMessage = "You just clicked on a file." strMessage &= "The size of the file is " & filTemp.Length.ToString strMessage &= "The complete path to it is " & filTemp.FullName End If MessageBox.Show(strMessage) End Sub

The codein Listing 24.2 demonstrates accessing properties specific to both the DirectoryInfo and FileInfo classes, as well as the more generic FileSystemInfo class. Keep in mind that if you do not need to access the entire object, it is more efficient just to store the filenames in the list box as strings. The program in Listing 24.3 scans the C:\Program Files directory and all of its subdirectories, adding the filenames of all the JPEG picture files (those with a JPG extension) that it finds. The user can then click a picture in the list box and it will be displayed on the form, as pictured in Figure 24.1.

Figure 24.1. The file viewer sample program allows you to browse all the pictures on your hard drive.

Listing 24.3 FindPics.ZIP JPEG File Viewer

Private Sub btnSearch_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles btnSearch.Click Dim dirTemp As New DirectoryInfo("C:\Program Files") lstFiles.Items.Clear() Call FindPictures(dirTemp) Me.Text = "Click the picture name to display it." MessageBox.Show("Click the picture name to display it.") End Sub Private Sub FindPictures(ByVal fsiTemp As FileSystemInfo) 'The object passed to this function is either a file or directory. 'If it is a JPG picture file, we will add it to the list box. 'If it is a directory, we use recursion to continue the search. If fsiTemp.GetType Is GetType(FileInfo) Then If fsiTemp.Extension.ToUpper = ".JPG" Then lstFiles.Items.Add(fsiTemp.FullName) End If Else Dim dirTemp As New DirectoryInfo(fsiTemp.FullName) Dim fsiNew As FileSystemInfo Me.Text = "Searching " & dirTemp.Name For Each fsiNew In dirTemp.GetFileSystemInfos Call FindPictures(fsiNew) Next End If End Sub Private Sub lstFiles_SelectedIndexChanged(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles lstFiles.SelectedIndexChanged If Not (lstFiles.SelectedItem Is Nothing) Then Me.BackgroundImage = Image.FromFile(lstFiles.SelectedItem.ToString) End If End Sub

The heart of the code behind Listing 24.3 is the FindPictures subroutine, which employs a technique called recursion. A recursive function calls itself. In our example, the function will call itself in order to process any directories contained within the directory it is working on. By placing a breakpoint in the FindPictures function, you can watch the levels of recursion build using the Call Stack window.

For more on debugging and the Call Stack window, p.717

Changing the Attributes of a File or Directory

By accessing properties and methods of a FileSystemInfo object associated with a file or directory, you can read or change information about the file or directory. For example, if you right-click a file or folder in Windows Explorer and choose Properties, you will see a dialog box similar to the one pictured in Figure 24.2.

Figure 24.2. You can change many file attributes by setting properties of the FileInfo class or calling methods of the File class.

The following code sample will change the Read-Only attribute of a file, which will be reflected in the Windows Explorer Properties dialog box.

Dim MyFile As FileInfo MyFile = New FileInfo("c:\temp\myfile.txt") If MyFile.Attributes And IO.FileAttributes.ReadOnly Then MessageBox.Show("Your file is already set to read-only mode!") Else MessageBox.Show("The read-only flag has been set!") MyFile.Attributes = MyFile.Attributes Or IO.FileAttributes.ReadOnly End If

There are many different attributes of files and directories, such as ReadOnly, Hidden, and Archive. These attributes are stored as individual bits in the Attributes property. Therefore, you have to use bitwise boolean operations to extract or set their values, as demonstrated in the sample code. In addition to changing a file's attributes, you can also determine or set several dates associated with a file, such as when it was created or last modified.

Copying, Renaming, and Deleting

The File class provides methods to copy, delete, and rename files. For example, File.Copy can be used to create a backup copy of a file:

File.Copy("c:\temp\quotes.doc", "C:\temp\quotesbackup.doc", True)

The parameters of the File.Copy method are

  • Source File The source file you want to copy. If the file does not exist, an exception will be thrown.

  • Destination File The path to the destination file you want to create.

  • Overwrite Existing File This parameter is optional and indicates whether you want to overwrite an existing destination file. If this parameter is omitted or set to False and the destination file already exists, the method will throw an exception.

As with many of the shared methods, there is a corresponding method for use with an object instance. If you have already created a FileInfo object, you can use the CopyTo method and simply provide the destination and overwrite parameters:

Dim filTemp As New File("C:\temp\quotes.doc") filTemp.CopyTo("C:\temp\newquotes.doc", False)

Note

The Copy and CopyTo methods will throw an exception if you attempt to overwrite an existing read-only file, even if you have the overwrite parameter set to True. To overwrite an existing read-only file, you first need to turn off its read-only attribute.

The DirectoryInfo class does not have a Copy or CopyTo method. However, you can create a new directory using the Create method and then copy the files within it. As an exercise, adapt the recursive function from Listing 24.3 to copy all the files and subdirectories contained within a given directory.

The rename and delete methods are identical for files and directories:

  • Move Static method to rename a file or directory. Its parameters are a source and a destination.

  • MoveTo Instance method to rename a file or directory.

  • Delete Deletes the specified file or directory. Wildcards are not allowed. If the read-only attribute of the file you want to delete is set, the method call will throw an exception.

Working with Paths

To manipulate a file in Visual Basic, you have to know the file path. The terms filename and pathname are often used interchangeably when referring to files. However, use of the term path usually means that some directory information is included with the filename. Consider the following examples, each of which represents a valid file path that can be used with most of the file functions in this chapter:

C:\MyApplication\Sounds\ding.wav Sounds\ding.wav ding.wav

Suppose your application is installed in the directory C:\MyApplication and needs to access some sound files in the C:\MyApplication\Sounds directory. Each of the previous three examples refers to a sound file named ding.wav. The first line is an example of an absolute path there is absolutely no doubt as to the file's location. However, the other two examples are relative paths. Although the absolute path leaves no room for doubt, hard-coding a drive or folder name within your program is never a good idea. A commercial application such as Microsoft Office lets you change the default installation directory to anything you want. Even if you install the application in an off-the-wall location like D:\JUNK123\, it still can find all its other files (such as document templates and clip art) and function normally.

Locating Files Using Relative Paths

Relative paths are based on a current directory. Think of the current directory as the folder you currently have open in Windows Explorer or the directory in a command prompt window. When your application needs to find other files using relative paths, there are two very useful members available, shown in Table 24.1:

Table 24.1. Determining the Right Path

Property Name

Namespace

Description

Directory.GetCurrentDirectory

System.IO

Sets or retrieves the current directory.

Application.StartupPath

System.Windows.Forms

Retrieves the directory from which the application was started.

When you start a Windows application, the GetCurrentDirectory function and StartupPath property to return the same directory as the application executable. The current directory can be changed as needed but the startup path cannot. Continuing our previous example, suppose your application will be doing a lot of work within its Sounds subdirectory. The following line of code allows any subsequent file functions to access the sound files using only their filename:

Directory.SetCurrentDirectory(Application.StartupPath & "\Sounds"

Note

You also may use a configuration file or registry settings to store the location of other files required by your application.

As you may have noticed, the backslash character (\) is used to separate directory names in a file path, so in the previous line of code we had to add a backslash before the new subdirectory name. Starting in Visual Basic .NET, a Path class has been provided to make parsing file paths easier for the programmer. For example, you can use the Combine method of the Path class, which automatically adds a slash if necessary:

strPath = Directory.Combine("C:\","Program Files") strPath = Directory.Combine(strPath,"Netmeeting") strPath = Directory.Combine(strPath,"blip.wav")

After the three lines of code had executed, strPath would contain the string C:\Program Files\Netmeeting\blip.wav. Keep in mind that the Combine method is only a string helper; it does not check whether the path is valid.

Note

In addition to combining paths, the Path class contains other useful path manipulation functions. For example, you can use methods in this class to extract the file extension or parse a file path in a platform-independent manner.

Finding Special Folders

Among the many folders on your hard drive are some that are used by the operating system for a variety of special purposes. For example, the My Documents folder allows users to consolidate all their documents in a single area. Other examples are the Temp directory, used for storing temporary files, and the Windows directory itself. These folders may have very different paths depending on the operating system. For example, Windows 95 users usually have Windows installed in the C:\Windows directory, and Windows 2000/NT users have a Windows directory called C:\WINNT. In addition, certain folders may be specific to the user who is logged in to the machine. Windows 2000 creates a separate My Documents and Temp directory for each user under the Documents And Settings folder.

If you want to find these special folders, you can use the operating system's environment variables. To see the environment variables, open a command prompt window and enter the command SET. You should see a screen similar to Figure 24.3.

Figure 24.3. Environment variables and command-line arguments can be determined by using methods in the Environment class.

In order to access these variables from code, you use the Environment class, which includes several useful members for dealing with environment variables, as shown in Table 24.2:

Table 24.2. Watching Out for the Environment

Member Name

Description

GetEnvironmentVariable method

Returns the value of a specified environment variable

SystemDirectory property

Returns the path of the Windows System directory

OSVersion property

Can be used to determine the operating system version and service pack level

The following sample code returns the path for a Windows 2000 user's My Documents directory:

strPath = Environment.GetEnvironmentVariable("USERPROFILE") strPath = Path.Combine(strPath, "My Documents")

As an exercise, you could expand the previous code into a generic class that would find special folders on any operating system. To learn the names of all the special folders, see the help file topic "Special Folders."

Watching for File System Changes

Often programmers need to work with files that are delivered by another person or program. For example, a user may want to have an Excel spreadsheet file published on the Web every time he updates it. Another example might be when someone sends you a data file via e-mail or FTP that you must import into a database on a periodic basis. Both of these activities involve waiting for a file to arrive and could be handled by repeatedly calling the File.Exists function. However, the .NET framework provides a class known as the FileSystemWatcher that can notify your application of many different types of file-related changes.

In this section we will build a program that watches a directory for the addition of new files and changes to existing files (see Listing 24.4). When these changes occur, they are noted in a listbox. The program in action is shown in Figure 24.4. Apart from the functions used to accept user input, there are really just three functions that are critical to the program's operation. The SetupWatcher function initiates the monitoring process; the FileChanged and FileRenamed functions receive file change notifications.

Figure 24.4. The FileSystemWatcher class contains functions to monitor file change events either synchronously or asynchronously.

Listing 24.4 FILEWATCH.ZIP File Watcher Example

Private WithEvents fsWatchObj As FileSystemWatcher Private Sub SetupWatcher(ByVal PathToWatch As String) fsWatchObj = New FileSystemWatcher(PathToWatch) fsWatchObj.IncludeSubdirectories = False fsWatchObj.EnableRaisingEvents = True AddHandler fsWatchObj.Created, AddressOf FileChanged AddHandler fsWatchObj.Changed, AddressOf FileChanged AddHandler fsWatchObj.Deleted, AddressOf FileChanged AddHandler fsWatchObj.Renamed, AddressOf FileRenamed End Sub Private Sub FileChanged(ByVal s As System.Object,_ ByVal EventInfo As FileSystemEventArgs) Dim DisplayMessage As String DisplayMessage = "File " & EventInfo.Name & " was " DisplayMessage &= EventInfo.ChangeType.ToString &_ " at " & DateTime.Now.ToString lstFiles.Items.Add(DisplayMessage) End Sub Private Sub FileRenamed(ByVal s As System.Object,_ ByVal EventInfo As RenamedEventArgs) Dim DisplayMessage As String DisplayMessage = "File " & EventInfo.OldName DisplayMessage &= " was renamed " & EventInfo.Name &_ " at " & DateTime.Now.ToString lstFiles.Items.Add(DisplayMessage) End Sub

The following steps describe how to test the sample program. Doing so will give you a good idea of how the FileSystemWatcher class works.

  1. Start a new Windows Application project.

  2. Add the code from Listing 24.4 to the form class.

  3. Add a ListBox control to the form. Set its name to lstFiles.

  4. Add a Button control to the project, named btnStart.

  5. In the Click event for btnStart, enter the following code:

    Call SetupWatcher("C:\temp")

  6. Open Windows Explorer. If there is not already a folder called C:\TEMP, create it.

  7. Start the sample program and click the button.

  8. Arrange your desktop so you can watch the sample program and use Windows Explorer at the same time.

  9. Copy, rename, and delete files in the C:\TEMP directory and note the changes in the listbox.

As you perform file operations in the C:\TEMP folder, the fsWatchObj object raises events, which write information received in the event parameters to the list box. This is a great example of asynchronous processing, because your program does not need to wait in a loop and the user can continue to perform other tasks. The FileSystemWatcher events are fairly straightforward, but note the following special circumstances:

  • The arguments for the Renamed event delegate are different from the other FileSystemWatcher events. This is because it provides both the old and new filenames to the event.

  • By setting the Filter property, you can control which files within the directory are monitored. For example, setting the filter to *.DOC would just raise events for Word documents.

  • The Changed event may be fired several times in a row, because several aspects of a file (size, last write time, and so on) are changed at once during certain operations. This effect can be eliminated to some extent by using the NotifyFilter property, which offers additional control over change notification.

Working with Other Processes

From time to time, you may need to launch another Windows program using Visual Basic code. For example, you could place a button on your form that runs calc.exe to provide users with easy access to Windows' built-in calculator. You might also want to find out the names about the programs that are currently running on a computer, or even terminate a misbehaving program. All these tasks can be accomplished by using the Process class. The Process class, which is part of the System.Diagnostics namespace, provides functionality that required complex API calls in previous versions of Visual Basic.

Launching Another Windows Program

One frequently asked question has always been "How can I run an application from VB and wait for it to finish?" The following code segment shows how easy this is to do in .NET:

Dim procNotepad As Process procNotepad = Process.Start("notepad.exe ") procNotepad.WaitForExit(5000) If procNotepad.HasExited Then MessageBox.Show("Notepad has been closed.") Else procNotepad.Kill() MessageBox.Show("Notepad was still running, so I killed it!") End If

The line following the Dim statement starts the Notepad executable and associates it with the procNotepad object. The parameter passed to the WaitForExit method indicates the number of milliseconds to wait for the process to end. In our example, the If statement will not be executed until the user closes Notepad or 5000 milliseconds (5 seconds) passes, whichever comes first.

Note

Omit the milliseconds parameter completely to specify an infinite wait time.

The previous code sample demonstrates launching another program and waiting for it to finish in a synchronous manner. However, if you want your program to continue executing and be notified when the process exits, you can install an event handler for the Exited event of the Process class:

Dim procNotepad As Process procNotepad = Process.Start("notepad.exe ") procNotepad.EnableRaisingEvents = True AddHandler procNotepad.Exited, AddressOf NotepadExitEvent Private Sub NotepadExitEvent(ByVal sender As Object, ByVal e As EventArgs) MessageBox.Show("Notepad has just exited!") End Sub

The previous lines of code allow the user to continue working with your program after the notepad.exe process starts. When the Exited event fires, the NotepadExitEvent subroutine will be executed. In order to get a Process object to fire the Exited event, you must set its WatchForExit property to True.

Attaching to an Existing Process

You can do a lot of neat things using the Process class. We have already demonstrated killing a process. You also can change its priority or determine CPU and memory usage statistics. Each of these features is easy to use and listed in the help files. However, before you can change process properties, you have to get the process associated with a Process object. As we have demonstrated, this is easy when you start the process from VB, but what about processes that are already running? The Process class provides several useful methods:

  • GetCurrentProcess Returns a Process object for the current process.

  • GetProcessByID Returns a Process object for the specified process id (the PID column listed in the Windows Task Manager).

  • GetProcesses Returns a collection of Process objects; each object represents a process on the system.

  • GetProcessesByName Returns a collection of Process objects that have a specified process name.

Note

Even after a process has ended, the process resources are kept in place for a time, so these methods may return more than just running processes. The HasExited property can be checked to see whether the process has ended.

The following lines of code add the names of system processes to a listbox:

Dim procTemp As Process For Each procTemp In Process.GetProcesses() lstProcesses.Items.Add(procTemp.ProcessName) Next

You also can manipulate processes on remote machines by using the machine name as an optional parameter in the GetProcesses function.


    Team-Fly    
    Top
     

    Категории