Introduction
Visual Studio .NET 2003 and its earlier versions are great, and are becoming the first choices for .NET application developers. You create a new project, and then develop and compile the code. But, there is a pain when you want to deploy your working file to production. Although you can create a deployment project to build the MSI installation, in many cases, you don't want to build the MSI. Instead of a bunch of junk files mixed with your working files in your project folder, you simply want to have a clean folder containing only the working folders/files and hand it over to your clients.
When you create a project with Visual Studio .NET 2003, it will generate several files automatically:
xxx.csproj
xxx.csproj.user
xxx.sln
xxx.suo
AssemblyInfo.cs
...
If your project is linking with SourceSafe, you will have more junk files. These files normally need not be included in your application when you ship your application to clients.
Suppose you have a very big project that consists of several projects; to separate the working files from these projects/solutions is a big pain.
This article tries to demonstrate how to solve this issue. I have built a file watcher object to monitor the project root folder; whenever a file/folder created, deleted, renamed, or hanged, it will move this file/folder to a separate destination (project release folder).
In this case, the working files/folders will be moved to the destination while your are developing. At the time your development is done, your clean working folder has been created. You just copy the whole destination folder and deploy it to your client. Sounds good?
Background
The general ideas to solve this issue are:
- Define two folders:
- Create a configuration file indicating where and how to process your project. The configuration file is in XML format as follows:
<Watchers>
<Watcher>
<Source>c:\filewatcher\testfolder\from</Source>
<ProjectDestination>c:\filewatcher\testfolder
\to\Bin</ProjectDestination>
<EngineDestination>c:\filewatcher\testfolder
\to\Engine</EngineDestination>
<FileExtensions>dll,pdb,ascx,xml,gif,jpeg,dis,js,xsl,
xls,htc,txt,config,asp,aspx,asmx</FileExtensions>
<Events>Created,Changed,Renamed,Deleted</Events>
<Trace>yes</Trace>
</Watcher>
</Watchers>
- Build a file watcher to monitor the FROM folder; based on the configuration file, it moves/changes the files/folders from the source folder to the destination folder.
Using the code
There is only one class to perform the file monitoring job: FileWatcher
. There are three important methods in this class: ProcessWatcher
, ProcessTask
, and CopyDeleteFile
. I will describe these methods one by one.
ProcessWatcher
creates the instance of the watcher object. It creates four events: FileChanged
, FileCreated
, FileReNamed
, and FileDeleted
. In the configuration file, you specify which event you want to track.
private void ProcessWatcher()
{
sourcePath = GetWatcherInfo("Source");
pDestinationPath = GetWatcherInfo("ProjectDestination");
eDestinationPath = GetWatcherInfo("EngineDestination");
watchFileExtension = GetWatcherInfo("FileExtensions");
trace = GetWatcherInfo("Trace").ToLower();
string eventName = GetWatcherInfo("Events");
string [] arrEvents = eventName.Split(",".ToCharArray());
int i = 0;
pDest = pDestinationPath.Split(";".ToCharArray());
WatchFile = new FileSystemWatcher(sourcePath, "*");
WatchFile.IncludeSubdirectories = true;
WatchFile.NotifyFilter = NotifyFilters.Size |
NotifyFilters.LastWrite |
NotifyFilters.FileName |
NotifyFilters.DirectoryName;
for (i=0;i<arrEvents.Length;i++)
{
switch(arrEvents[i])
{
case "Changed":
WatchFile.Changed += new
FileSystemEventHandler(FileChanged);
break;
case "Created":
WatchFile.Created += new
FileSystemEventHandler(FileCreated);
break;
case "Renamed":
WatchFile.Renamed += new
RenamedEventHandler(FileReNamed);
break;
case "Deleted":
WatchFile.Deleted += new
FileSystemEventHandler(FileDeleted);
break;
}
}
WatchFile.EnableRaisingEvents = true;
}
Whenever the watcher fires an event, it then goes here to check what kind of process to execute.
private void ProcessTask(FileSystemEventArgs e,
string checkType, string fileOrFolder)
{
string fileName = string.Empty;
fileName = GetNameOnly(e.Name);
switch(checkType)
{
case "Created":
CheckFolder(e.Name, fileName, fileOrFolder);
CopyDeleteFile(e.FullPath, e.Name, fileName,
"", fileOrFolder, false);
break
case "Changed":
CopyDeleteFile(e.FullPath, e.Name, fileName,
"", fileOrFolder, false);
break
case "Deleted":
if (fileOrFolder == "file")
{
for (int i=0;i<pDest.Length;i++)
{
File.Delete(pDest[i] + "\\" + fileName);
File.Delete(pDest[i] + "\\" + e.Name);
}
}
else
{
Directory.Delete(eDestinationPath +
"\\" + e.Name, true);
}
break
default:
break
}
if (trace == "yes")
WriteLog(e, checkType);
}
Finally, the execution method CopyDeleteFile
: if it's a rename request, this method deletes the mapping file in the destination folder first, then copies the renamed file to the destination mapped folder. If the file type is a folder, it does the same behavior as for a file.
private void CopyDeleteFile(string source, string watchedNamePath,
string fileName, string oldName,
string fileOrFolder, bool isRename)
{
if(fileName.IndexOf(".dll") > 0 || fileName.IndexOf(".pdb") > 0)
{
for (int i=0;i<pDest.Length;i++)
{
File.Delete(pDest[i] + "\\" + fileName);
File.Copy(source, pDest[i] + "\\" + fileName, true);
File.SetAttributes(pDest[i] + "\\" + fileName,
FileAttributes.Normal);
}
}
else
{
//Copy to engine directory. Only copy when file changed
if (oldName == "" && fileOrFolder == "file")
{
File.Copy(source, eDestinationPath + "\\" +
watchedNamePath, true);
File.SetAttributes(eDestinationPath + "\\" +
watchedNamePath, FileAttributes.Normal);
}
else
{
if (fileOrFolder == "file")
{
File.Delete(eDestinationPath + "\\" + oldName);
if (isRename)
File.Copy(source, eDestinationPath +
"\\" + watchedNamePath, true);
}
else if (fileOrFolder == "folder" && oldName != "")
{
Directory.Move(eDestinationPath + "\\" + oldName,
eDestinationPath + "\\" + watchedNamePath);
}
}
}
}
To Run the Demo Program
Download the demo Zip file and unzip it to your c: drive. Open the root FileWatchDemo folder, you should see the following files and folders:
c:\FileWatcherDemo\TestFolder
C:\FileWatcherDemo\CustomWatcher.config
C:\FileWatcherDemo\FileWatcher.exe
Double click the FileWatcher.exe, and copy a DLL to the C:\FileWatcherDemo\TestFolder\From folder. You should see that this DLL has been added into the C:\FileWatcherDemo\TestFolder\To\bin folder.
Copy a GIF file to the C:\FileWatcherDemo\TestFolder\From folder, you should see this GIF file has been added into the C:\FileWatcherDemo\TestFolder\To\engine folder.