Deborah L. Cooper
September 1996
This article explains how to create in-process OLE servers (that is, classes) in Microsoft® Visual Basic® 4.0 that encapsulate the SHFileOperation application programming interface (API) function. Through the use of the class developed in this article, you will learn how to use the SHFileOperation function to more easily manipulate file system objects. While you are developing code for a class, you need only fully debug and test the class once—when you are initially creating the class modules. In any future Visual Basic application programs you develop, you save the time and the expense of having to code and write the routines a second time. In addition, by developing classes to manipulate files, you don't have to include the API declarations and structures in projects you develop—simply include the class and you're in business.
Under the Windows® 95 operating system, you can use the SHFileOperation function to copy, delete, rename, or move specific files. When the file operation is actually performed, you have the option of displaying a progress dialog box. By using a progress dialog box, you give the user of your application program visual feedback as to what is happening.
To use the SHFileOperation function in a Visual Basic application program, you must include the following Declare statement in a code module:
Private Declare Function SHFileOperation Lib "shell32.dll" Alias "SHFileOperationA" (lpFileOp as SHFILEOPSTRUCT) As Long
As you can see from the Declare statement above, the SHFileOperation function requires only one argument, a pointer to a SHFILEOPSTRUCT structure. This structure contains information needed by the SHFileOperation function to perform the desired file operation.
For Visual Basic, the format of the SHFILEOPSTRUCT structure looks like this:
Private Type SHFILEOPSTRUCT
hwnd As Long
wFunc As Long
pFrom As String
pTo As String
fFlags As Integer
fAnyOperationsAborted As Boolean
hNameMappings As Long
lpszProgressTitle As String
End Type
Each of the eight fields in the SHFILEOPSTRUCT structure should be set as follows:
Field name | Description |
hWnd | The handle of the dialog box that will be used to display the file operation's status. When the fFlags argument is set to a FOF_CREATEPROGRESSDLG value, this is the handle to the parent window for the progress dialog box. |
Wfunc | This field tells the SHFileOperation function what type of operation is to be performed. One of the following values may be used:
|
pFrom | This field points to a buffer containing the name of the source file(s). If more than one filename is specified, a null character must separate each filename. The end of the list of filenames must be terminated by two consecutive null characters. |
Pto | This field points to a buffer containing the name of the destination directory or file. If more than one filename is specified, a null character must separate each filename. The end of the list of filenames must be terminated by two consecutive null characters. Multiple destination filenames can be used only if the fFlags field contains the FOF_MULTIDESTFILES value. |
fFlags | This field tells the SHFileOperation function how to control the file operation. One or more of the following values may be specified:
|
fAnyOperationsAborted | If a file operation was aborted before that operation was completed, this field will be set to TRUE. |
hNameMappings | This field, used only when the fFlags field includes the FOF_WANTMAPPINGSHANDLE value, contains the handle of a filename mapping object. This object contains an array of SHNAMEMAPPING structures where each structure contains both the old and new paths of each file that is copied, moved, or renamed |
lpszProgressTitle | When the fFlags field includes the FOF_SIMPLEPROGRESS value, this field contains a pointer to a string to be given to the title of the progress dialog box. |
After the SHFileOperation function has been executed, it returns a zero value if the file operation was successful. If the file operation was not successful, the SHFileOperation function returns a nonzero value. When researching this article, I discovered a bug in that the SHFileOperation returns a zero value regardless of the outcome of the file operation. To this end, the class in this article uses a DoesFileExist function to first determine that the source file actually exists. By using the DoesFileExist function, we can be assured that the SHFileOperation performs as expected.
The FileOperations class that is included with this article can be used in any application program you develop in Visual Basic 4.0. This file operations class provides an easy mechanism to copy a file from one directory to another, rename a file, delete an existing file from disk, or move a file from one directory to another.
To create a class that can perform all types of file operations, you must first define the properties of this class in the Declarations section. The FileOperations class properties are (usually) the target filename, the source filename, the directory names, and the type of file operation you want performed. These properties should be defined as private and local to this class.
When using the FileOperations class, it is best to not hard-code the source and destination filenames and paths. Instead, you can retrieve these properties from your calling application program.
The FileOperations class includes a method to copy files called (you guessed it!) CopyThisFile. Calling the CopyThisFile method, we can easily copy a file from one directory to another. The calling program must pass two pieces of information to the CopyThisFile method: the name of the file that is to be copied and the name of the destination directory to which the file is to be copied. The CopyThisFile method, shown below, sets the necessary fields in the SHFILEOPSTRUCT structure and then calls the SHFileOperation function to actually begin the copy process.
Public Sub CopyThisFile(File name As String, ToDir As String)
On Error Resume Next
Dim FileStruct As SHFILEOPSTRUCT
Dim X As Long
Dim P As Boolean
P = DoesFileExist(File name)
If P = True Then
FileStruct.pFrom = File name
FileStruct.pTo = ToDir
FileStruct.fFlags = FOF_NOCONFIRMMKDIR + FOF_NOCONFIRMATION + FOF_SILENT
FileStruct.wFunc = FO_COPY
X = SHFileOperation(FileStruct)
Else
Err.Raise Copy_Error, "FileOperations::CopyThisFile", Err.Description
End If
End Sub
The result returned by the CopyThisFile method enables us to determine if the user passed a valid filename to the method. If a valid filename was not found on the disk, then the SHFileOperation would fail. In this case, the error trapping passes the Copy_Error value to the calling program. In our calling program, we can test this value to determine if the CopyThisFile method was successful or not.
The fFlags field has been set to a combination of three values. The fFlags field controls how the file operation is to be carried out. In our case, the FOF_SILENT value tells the SHFileOperation function that we do not want to display a progress dialog box when our file is copied. In our calling program, we only see a dialog box if the source filename is invalid. The user does not necessarily have to see the file operation's dialog box in addition to our own.
The FOF_NOCONFIRMMKDIR value in the fFlags field tells the SHFileOperation function that we do not want to confirm any requests to create new directories if those directories must be created by the SHFileOperation function. Likewise, the FOF_NOCONFIRMATION value tells the function to respond "yes to all" for all dialog boxes that may be displayed. A dialog box might be displayed if the file already existed, for example.
To copy a file, we must set the wFunc field in the SHFILEOPSTRUCT structure to the value of FO_COPY. In addition, we need to tell the SHFileOperation function the name of the file we want to copy. The source name of the file must be placed in the pFrom field of the SHFILEOPSTRUCT structure.
Notice that the above code copies a single file to the directory specified. In some situations, we might want to copy more than one file to several directories. In that case, we would set the fFlags field to a value of FOF_MULTIDESTFILES to tell the SHFileOperation that we want to use multiple destination directories. In addition, we fill the pTo field in the SHFILEOPSTRUCT structure with the names of the destination directories. Each directory name in the pTo field must be terminated by a null character and the entire list of directory names must be terminated by two consecutive null characters.
We have also included a DoesFileExist function in the class module. The DoesFileExist function determines if the to-be-copied file actually exists. If the filename does not exist, then it cannot, obviously, be copied. This precaution was incorporated because the SHFileOperation function always returns a value of zero, regardless if the file operation was actually successful not. The DoesFileExist function lets us confirm that the file actually exists and can be manipulated.
Public Function DoesFileExist(NameOfFile As String) As Boolean
Dim X As Long
Dim wStyle As Long
Dim Buffer As OFSTRUCT
Dim IsThere As Long
If (Len(NameOfFile) = 0) Or (InStr(NameOfFile, "*") > 0) Or (InStr(NameOfFile, "?") > 0) Then
DoesFileExist = False
Exit Function
End If
On Error GoTo NoSuchFile
X = OpenFile(NameOfFile, Buffer, OF_EXIST)
If X < 0 Then
GoTo CheckForError
Else
DoesFileExist = True
Exit Function
End If
CheckForError:
X = Buffer.nErrCode
If X = 3 Then
GoTo NoSuchFile
End If
NoSuchFile:
DoesFileExist = False
ExitFileExist:
On Error GoTo 0
End Function
In this section, we will discuss how to delete a file stored on disk. To delete a file from disk, the wFunc field of the SHFILEOPSTRUCT structure must be set to the FO_DELETE value. In addition, we can tell the SHFileOperation function to preserve undo information by specifying the FOF_ALLOWUNDO value in the fFlags field of the SHFILEOPSTRUCT structure. This value tells the operating system to preserve information about the to-be-deleted file. Later on, we might want to restore the previously deleted file and the system will restore the file by using this undo information. Under the Windows 95 operating system, a file that is deleted is automatically sent to the Recycle Bin.
We must also fill the pFrom field of the SHFILEOPSTRUCT structure with the name of the file we want to delete. In the sample class, we only allow one file to be deleted at a single time. Filenames that contain the wildcard "*" and "?" characters cannot be deleted. If desired, a parsing routine could be included in the class module to allow for multiple file deletions. You would need to fill the pFrom field in the SHFILEOPSTRUCT with the names of all files you want to delete, where each filename is separated by a NULL character and the entire list of filenames is terminated by two consecutive NULL characters.
After calling the DoesFileExist function, we can pass the filename on to the SHFileOperation function to delete it, as shown in the DeleteThisFile routine here:
Public Sub DeleteThisFile(File name As String)
On Error Resume Next
Dim FileStruct As SHFILEOPSTRUCT
Dim X As Long
Dim P As Boolean
P = DoesFileExist(File name)
If P = True Then
FileStruct.pFrom = File name
FileStruct.fFlags = FOF_SILENT + FOF_ALLOWUNDO + FOF_NOCONFIRMATION
FileStruct.wFunc = FO_DELETE
X = SHFileOperation(FileStruct)
Else
Err.Raise Del_Error, "FileOperations::DeleteThisFile", Err.Description
End If
End Sub
Notice that in cases where an error occurs after calling the SHFileOperation function that we return the error (Err.Raise Del_Error) to the calling program. The calling program can then respond by saying the file cannot be deleted.
When it comes to moving files, the code to accomplish this is very similar to the CopyThisFile method we developed earlier. The name of the source file is passed to the MoveThisFile method, along with the target filename and path. The DoesFileExist function is again used to determine if the source filename actually exists on the disk. If it doesn't, the MoveThisFile method returns an error code to the calling program.
Public Sub MoveThisFile(File name As String, DestName As String)
On Error Resume Next
Dim FileStruct As SHFILEOPSTRUCT
Dim P As Boolean
Dim X As Long
P = DoesFileExist(File name)
If P = True Then
FileStruct.pFrom = File name
FileStruct.pTo = DestName
FileStruct.fFlags = FOF_SILENT + FOF_NOCONFIRMATION
FileStruct.wFunc = FO_MOVE
X = SHFileOperation(FileStruct)
Else
Err.Raise Move_Error, "FileOperations::MoveThisFile", Err.Description
End If
End Sub
The final operation you can perform on files with the SHFileOperation function is to rename them. As you can see from the RenameThisFile method below, you must provide both a source filename and a target filename. When you call the SHFileOperation, the file is given the new target name. However, if a file already exists with that target name, the operation will fail. This is why we need to set the fFlags field in the SHFILEOPSTRUCT structure to a value of FOF_RENAMEONCOLLISION. This flag tells the SHFileOperation to give the file a special name if a file with that name already exists.
Because the SHFileOperation returns a value of zero despite the fact that the function may fail, we again make a call to the DoesFileExist function to make sure the source filename actually exists. If the file does not exist, we pass the Rename_Error value back to the calling program.
Public Sub RenameThisFile(File name As String, Target As String)
On Error Resume Next
Dim FileStruct As SHFILEOPSTRUCT
Dim P As Boolean
Dim X As Long
P = DoesFileExist(File name)
If P = True Then
FileStruct.pFrom = File name
FileStruct.pTo = Target
FileStruct.fFlags = FOF_RENAMEONCOLLISION + FOF_SILENT
FileStruct.wFunc = FO_RENAME
X = SHFileOperation(FileStruct)
Else
Err.Raise Rename_Error, "FileOperation::RenameThisFile", Err.Description
End If
End Sub
From the discussion in this article, you can see that manipulating files through the use of a class is quite simple. The FileOperations class included with this article contains all of the methods needed to copy, delete, move, and rename files. In addition, a demonstration program shows how to call each of the FileOperations methods to perform a specific file operation.
SHFileOperation QuickInfo (MSDN Library).
SHFILEOPSTRUCT QuickInfo (MSDN Library).
"Shh! Be Very, Very Quiet, We're Hunting New Functions," Programming the Windows 95 User Interface (MSDN Library).
"Tip 1: Determining If a File Already Exists." (MSDN Library).