(Print this page)

Folders synchronization using the System.IO namespace
Published date: Wednesday, March 1, 2006
On: Moer and Éric Moreau's web site

I really like to backup my precious data. You can think of my articles, the lines code I put together to create a solution, my e-mails, my contacts, some utilities written by friends, my tax files! I don’t know about you but I lost 2 laptop’s hard drive without being able to recover much from them. Since the second crash, I wouldn’t say that I can’t sleep without taking a backup but I feel better and safer. I like to take a backup at least once a week.

I take 2 sets of backup. The first kind backup is about the projects that belong to my clients, I simply copy the files to their LAN. The second kind of backup is all my own data for which I copy many folders to an external hard drive (and in addition to that I sometime burn down a set of CDs that I keep outside of my house (I know I am a bit freaky). It takes longer as the time goes because the number of files continues to grow at a fast pace.

A large part of these files remained untouched for years but are inside folders that I want to keep safe. I think that manipulating these files takes most of time I allocate to do my backups.

Lately, I started creating an application that will help me synchronize the content of a source folder to a destination folder. By synchronizing, I mean 3 operations:

  • Copy files that were modified since the last backup.
  • Ignore files that remained unchanged.
  • Delete files from the destination that do not exist anymore from the source.

I know there are many tools out there that could do that but none would let me write something down interesting for you!

Creating the core of the application

What control represents the hierarchy of folders and files you have on your drives the best? You are right: the TreeView control. I will use this control to load the structure of both the source and the destination.

I have created a small application that counts a great number of Panel controls in order to be able to also use Splitter controls and be able to resize everything properly.

The application shows 2 TreeView controls letting you search your hierarchy. Major properties of the selected folder or file are shown under the TreeView controls. A listbox at the bottom will show the details of the synchronization. Because the TreeViews are not automatically refreshed like Windows explorer does, you have 2 buttons to refresh the contents. Finally, a Sync button will do the actual process of synchronization.

I strongly suggest that you download the sample application coming with this column as I won’t explain how to create the GUI.

Figure 1: The demo application

The System.IO namespace

The System.IO namespace contains a great number of classes providing basic drives, folders and files operations. It also let you read and write to files and streams.

You may surely guess that we will you a variety of the properties and methods of this namespace through this article. It is by no mean a complete reference to this namespace.

Getting the list of drives

With the large drives we are having today, it would take a very long time to fill the 2 TreeView controls with all the folders and the files from drives when the application starts. Many items would never be used (just think to the Program Files folder or the Windows folder – you will surely not backup these 2!).

Instead, the 2 TreeView controls are filled with available drives when the application starts. This is done using the GetLogicalDrives of the Directory class. This method returns an array of string in the form ":\". You will find the code for that in the TVW_FillBase method. If you look at this method, you will find a loop that goes through each item of the GetLocicalDrives array adding each element to the TreeView. You also see that a “Dummy Entry” node is added to each drive. This dummy entry is only used to have the TreeView control display a small plus sign. This node will never be seen by the user. It will be replaced later when the actual content of the folder will be read.

Here is the code that loops through the array of drives and fill the TreeView control:

With pTreeview
    .Nodes.Clear()
    For Each strX As String In Directory.GetLogicalDrives
        With .Nodes.Add(strX)
            .Nodes.Add("DUMMY ENTRY")
        End With
    Next
End With

Expanding a folder

When you select one of the nodes to open it, the BeforeExpand event of the TreeView control is raised. This event is trapped in order to get the content of the selected folder. The code of this event mainly calls 2 methods: TVW_AddFolders and TVW_AddFiles.

The first method (TVW_AddFolders) loops through the array returned by the call to the GetDirectories method of the Directory class and add them to the TreeView control. That method list the sub-directories included of the specified directory.

Here is the important part of this method:

For Each strDir As String In Directory.GetDirectories(pNode.FullPath)
    With pNode.Nodes.Add(Path.GetFileName(strDir))
        .Tag = enuItemType.Directory
        .Nodes.Add("DUMMY ENTRY")
    End With
Next

The second method (TVW_AddFiles) loops through the array returned by the call to the GetFiles method of the Directory class and add them to the TreeView control. That method list the files included of the specified directory.

Here is the important part of this method:

For Each strFileName As String In Directory.GetFiles(pNode.FullPath)
    With pNode.Nodes.Add(Path.GetFileName(strFileName))
        .Tag = enuItemType.File
    End With
Next

Before looping through the folders array, the current content is removed to delete the dummy entry that may be there (or to refresh the content with the current one).

Here is the important part of this method:

e.Node.Nodes.Clear()
If TVW_AddFolders(e.Node) Then
    TVW_AddFiles(e.Node)
End If

Selecting a folder or a file

If you simply select a folder, some properties are displayed in the label under the TreeView control. This happens because the AfterSelect event of the TreeView control is handled.

In the code of this event, the Tag property of the selected node is checked to discover if the node contains a folder or file. This value is set by the TVW_AddFolders and TVW_AddFiles methods.

This is a portion of the code you will find in this event handler:

Case enuItemType.File
    Dim objFileInfo As New FileInfo(.FullPath)
    lblOutput.Text = "File = " & objFileInfo.FullName & _
                     Environment.NewLine & _
                     "Length = " & objFileInfo.Length.ToString & _
                     Environment.NewLine & _
                     "Last write = " & objFileInfo.LastWriteTime.ToString & _
                     Environment.NewLine & _
                     "Attributes = " & objFileInfo.Attributes.ToString

Case enuItemType.Directory
    Dim objDirInfo As New DirectoryInfo(.FullPath)
    lblOutput.Text = "File = " & objDirInfo.FullName & _
                     Environment.NewLine & _
                     "Last write = " & objDirInfo.LastWriteTime.ToString & _
                     Environment.NewLine & _
                     "Attributes = " & objDirInfo.Attributes.ToString
As you can see here, an instance of the FileInfo class or the DirectoryInfo class in initialized with the FullPath property found in the selected node. These 2 classes give us access to properties and methods related to the current object. Some properties are shown here. For example, I have used the LastWriteTime and the Attributes properties for both classes. I have also displayed the Length of the file (which is its size) but sadly the DirectoryInfo class doesn’t give us this property (we would have to loop recursively through the children to sum the Length of each file which can take a long time when you have to deal with some folders like Program Files).

These 2 classes are also giving us a bunch of methods that are not shown here. For example, the FileInfo class offers methods like AppendText, CopyTo, MoveTo, Delete, Open, OpenRead, OpenText, OpenWrite, … The DirectoryInfo class offers some methods like CreateSubDirectory, Delete, GetDirectories, GetFiles, MoveTo, …

Synchronizing folders

The last but not the least option implemented in this article’s demo application is the synchronization of 2 folders.

When you click the synchronize button, the Click event first ensure that both source and destination folders are valid in the TreeView controls. This is done by ensuring that the Tag property of the SelectedNode contains a Directory enumeration value and nothing else using this code:

Case enuItemType.Directory
    objDISource = New DirectoryInfo(.FullPath)
Case Else
    MessageBox.Show("The source folder is not valid")
Another validation is done to ensure that the source is different then the destination by comparing the FullName property of the DirectoryInfo class instance:
ElseIf objDISource.FullName = objDIDestination.FullName Then
    MessageBox.Show("The source and the destination are the same", _
                    "Invalid selection", _
                    MessageBoxButtons.OK, MessageBoxIcon.Error)
Then the Sync method is called with the 2 directory as arguments. This method scans the source directory for each files and folders. The possible actions are:
  • If the file does not exist in the destination folder, the file from the source folder is simply copied.
  • If the file exists in the destination folder but is older, the file in the destination folder is replaced with the one from the source folder.
  • If a directory is found in the source directory, the Sync method is called recursively to process the sub-directory.
  • Finally, all the folders and files from the destination that do not exist anymore in the source directory are deleted from the destination folder.

A status for each file is written in the ListBox control at the bottom of the form so you can see the activity.

Conclusion

This application can be used as the basis of something much more complete. Some features I will probably add in a near feature include:

  • Add checkboxes to the source treeview to be able to select more then one folder and process the selected folders.
  • Persist those selected folders.
  • Process command line arguments so I can schedule the tasks with a set of arguments.
  • Copy directly to CDs and/or DVDs.

I don’t know if you take backups of your personal data at regular interval but I seriously hope you do!

I hope you appreciated the topic and see you next month.


(Print this page)