.NET filesystemwatcher - was it a file or a directory? - c#

Is there a way to determine with the FSW if a file or a directory has been deleted?

Here's a simplified and corrected version of fletcher's solution:
namespace Watcher
{
class Program
{
private const string Directory = #"C:\Temp";
private static FileSystemWatcher _fileWatcher;
private static FileSystemWatcher _dirWatcher;
static void Main(string[] args)
{
_fileWatcher = new FileSystemWatcher(Directory);
_fileWatcher.IncludeSubdirectories = true;
_fileWatcher.NotifyFilter = NotifyFilters.FileName;
_fileWatcher.EnableRaisingEvents = true;
_fileWatcher.Deleted += WatcherActivity;
_dirWatcher = new FileSystemWatcher(Directory);
_dirWatcher.IncludeSubdirectories = true;
_dirWatcher.NotifyFilter = NotifyFilters.DirectoryName;
_dirWatcher.EnableRaisingEvents = true;
_dirWatcher.Deleted += WatcherActivity;
Console.ReadLine();
}
static void WatcherActivity(object sender, FileSystemEventArgs e)
{
if(sender == _dirWatcher)
{
Console.WriteLine("Directory:{0}",e.FullPath);
}
else
{
Console.WriteLine("File:{0}",e.FullPath);
}
}
}
}

I temporary use the "Path" function initially, but later in case of not delete I fix it by Directory.Exists.
However that doesn't fix the Delete case
bool isDirectory = Path.GetExtension(e.FullPath) == string.Empty;
if (e.ChangeType != WatcherChangeTypes.Deleted)
{
isDirectory = Directory.Exists(e.FullPath);
}

Your question only makes sense if there could be a file and a directory with the same name at the same path. e.g. If you have filenames without extension or directories with extension.
If your directories and files follow the usual conventions, just checking for the presence of an extension in the full path(bool iSDirectory = Path.GetExtension(e.FullPath).Equals("");), which works whether the file/directory exists or not, because the method just parses the path given and has no connection to the file whatsoever.
If you have to deal with the non-conventional issues I mentioned in the beginning, you could check whether a directory or a file exists at that location. If neither does, you treat them as if both were deleted. If one of them does exist, you treat the other as if it was deleted.
Your inquiry implies that you keep a list of the files and directories somewhere, so, checking against that list, you can make your decision about handling.
I think that this approach is better than the solution given that uses two filesystem watchers in order to tell the difference.

You could interrogate the FileSystemEventArgs.FullPath property to tell if it is a directory or a file.
if (Path.GetFileName(e.FullPath) == String.Empty)
{
//it's a directory.
}
To check if it is a file or directory.

Related

Persisting file path between runs in c#

I have an app in which the user needs to access certain files in a user set and selected folder.
The folder and files paths need to be easily accessed (short simple path).
I use the Properties Settings to hold the Folder and File paths, but for some reason each time I re-start the program the Folder and File paths are lost.
I have followed and checked the program and all seems to be OK (except something I am missing, apparently).
I attach here the program snippet in two parts: The search for path and the setting in case path / file not found. (removed exception handling to save on lines)
public Main() //part of Main, stripped off exception handling)
{
//..........
dataFolder = Properties.Settings.Default.dataFolder;
if (!Directory.Exists(dataFolder))
{
SetDataFolder();
}
configFile = Properties.Settings.Default.configFile;
if (!File.Exists(configFile))
{
SetConfigFile();
}
dataFile = Properties.Settings.Default.dataFile;
if (!File.Exists(dataFile))
{
SetDataFile();
}
loadParamsFromFile(configFile); //Load the previously saved controls.
public String SetDataFolder()
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
DialogResult folder = dialog.ShowDialog();
if (folder == DialogResult.OK)
{
dataFolder = dialog.SelectedPath;
Directory.CreateDirectory(dataFolder);
dataFolder = Path.GetFullPath(dataFolder);
Properties.Settings.Default.dataFolder = dataFolder;
Properties.Settings.Default.Save();
return dataFolder;
}
else return null;
}
private string SetDataFile()
{
dataFile = $"{dataFolder}\\{textBoxSampleID.Text.Replace("/r", "").Trim()}.txt";
File.Create(dataFile).Close();
Properties.Settings.Default.dataFile = dataFile;
Properties.Settings.Default.Save();
return dataFile;
}
private string SetConfigFile()
{
configFile = $"{dataFolder}\\electroplating.cfg";
File.Create(configFile).Close();
Properties.Settings.Default.configFile = configFile;
Properties.Settings.Default.Save();
return configFile;
}
Check out this question:
How to change application settings (Settings) while app is open?
I would suggest using Path.Combine() for the construction of the file paths.
If it still doesn't work, you could also try using the registry for storing the values.
string dataFilePath = Path.Combine(dataFolder, textBoxSampleID.Text.Replace("/r", "").Trim());
RegistryKey key = Registry.LocalMachine.CreateSubKey(#"SOFTWARE\Company");
if (key != null)
{
key.SetValue("dataFilePath", dataFilePath);
}
You could then use string dataFilePath = (string)key.GetValue("dataFilePath") to get the value out of the registry.

Refreshing FileVersionInfo in Program Files directory

I have a program with a FileSystemWatcher which watches for itself to be updated to a new version by an external program (which involves renaming the current executable and copying a new one in its place).
The problem is, when the file it's watching is in the Program Files directory, the FileVersionInfo.GetVersionInfo() doesn't get the new version information, it returns the same thing it got the first time. So if it updated from 1.1 to 1.2, it would say "Upgraded from 1.1 to 1.1" instead of "Upgraded from 1.1 to 1.2". It works correctly in the debug directory, but under Program Files, it won't get the correct value.
Here's the essence of what it's doing, without all the exception handling and disposing and logging and thread invoking and such:
string oldVersion;
long oldSize;
DateTime oldLastModified;
FileSystemWatcher fs;
string fullpath;
public void Watch()
{
fullpath = Assembly.GetEntryAssembly().Location;
oldVersion = FileVersionInfo.GetVersionInfo(fullpath).ProductVersion;
var fi = new FileInfo(fullpath);
oldSize = fi.Length;
oldLastModified = fi.LastWriteTime;
fs = new FileSystemWatcher(
Path.GetDirectoryName(fullpath), Path.GetFileName(file));
fs.Changed += FileSystemEventHandler;
fs.Created += FileSystemEventHandler;
fs.EnableRaisingEvents = true;
}
void FileSystemEventHandler(object sender, FileSystemEventArgs e)
{
if (string.Equals(e.FullPath, fullpath, StringComparison.OrdinalIgnoreCase))
{
var fi = new FileInfo(fullpath);
if (fi.Length != oldSize
|| fi.LastWriteTime != oldLastModified)
{
var newversion = FileVersionInfo.GetVersionInfo(fullpath).ProductVersion;
NotifyUser(oldVersion, newversion);
}
}
}
How do I make GetVersionInfo() refresh to see the new version? Is there something else I should be calling instead?
I'm answering my own question because there doesn't seem to be much interest. If anyone has a better answer, I'll accept that instead...
As far as I can tell, there is no way to make it refresh. Instead I worked around the issue:
return AssemblyName.GetAssemblyName(fullpath).Version.ToString();
Combined with code that makes sure it only gets called once, it seems to work just fine.

Create directory

I need to create a directory, but, the directory when I need to create is inside of another directory. Something like this:
Directory.CreateDirectory(#"teste\teste\teste\teste\");
basically, this directory does not exist ( of course ), but, the CreateDirectory(...) not support this string style, how I can make to create this directories ?
My way to make this is that:
private void createdir(string _path)
{
string path = string.Empty;
string[] dir = _path.Split('\\');
for(int i=0;i<dir.Length;i++)
{
path += dir[i] + "\\";
Directory.CreateDirectory(path);
}
}
But, I want to know, if have a more better way to make this ( a more legible ) more rapid.
Directory.Create("c:\teste\teste\teste\teste"); should workt
according to MSDN, you can nest the directory . CreateDirectory
Directory.CreateDirectory("Public\\Html");
Directory.CreateDirectory("\\Users\\User1\\Public\\Html");
Directory.CreateDirectory("c:\\Users\\User1\\Public\\Html"); // using verbatim string you can escape slashes
if(System.IO.Directory.Exists(yourPath))
{
Directory.CreateDirectory(yourPath);
}
Directory.CreateDirectory() can be used to create directories and subdirectories as specified by the path.
Here’s an example:
static void Main(string[] args)
{
try
{
Directory.CreateDirectory(#"D:\ParentDir\ChildDir\SubChildDir\");
Console.WriteLine("Directories Created");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Source
My chosen method would be:
DirectoryInfo di = new DirectoryInfo(#"teste\teste\teste\teste\");
di.Create();
Your way is too complicated for this process. You don't have to use Split() method also to create this kind of directories.
You can use it like;
string tempFolderAbsolutePath = #"C:\Temp";
string subFolderRelativePath = #"teste\teste\teste\teste\";
DirectoryInfo tempFolder = new DirectoryInfo( tempFolderAbsolutePath );
DirectoryInfo subFolder = tempFolder.CreateSubdirectory( subFolderRelativePath );
As you can see, this process creates nested subdirectories.
If your current directory is (say C:\) and you want to create a directory as C:\A\B\C, then I think the best way is using
Directory.CreateDirectory(#"\A\B\C");
If you need a directory in another root (say, D:\) then you need to give the full path as
Directory.CreateDirectory(#"D:\A\B\C");
You do not need to have a for loop to create each directory as CreateDirectory does it for you.

Check if DirectoryInfo.FullName is special folder

My goal is to check, if DirectoryInfo.FullName is one of the special folders.
Here is what I'm doing for this (Check directoryInfo.FullName to each special folder if they are equal):
DirectoryInfo directoryInfo = new DirectoryInfo("Directory path");
if (directoryInfo.FullName == Environment.GetFolderPath(Environment.SpecialFolder.Windows) ||
directoryInfo.FullName == Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles ||)
...
...
)
{
// directoryInfo is the special folder
}
But there are many special folders (Cookies, ApplicationData, InternetCache, etc.). Is there any way to do this task more efficiently?
Thanks.
Try this following code :
bool result = false;
DirectoryInfo directoryInfo = new DirectoryInfo("Directory path");
foreach (Environment.SpecialFolder suit in Enum.GetValues(typeof(Environment.SpecialFolder)))
{
if (directoryInfo.FullName == Environment.GetFolderPath(suit))
{
result = true;
break;
}
}
if (result)
{
// Do what ever you want
}
hope this help.
I'm afraid the answers given seem to be the only way, I hate the special folders because what ought to be a very simple function -
void CollectFiles(string strDir, string pattern) {
DirectoryInfo di = new DirectoryInfo(strDir);
foreach(FileInfo fi in di.GetFiles(pattern) {
//store file data
}
foreach(DirectoryInfo diInfo in di.GetDirectories()) {
CollectFiles(diInfo);
}
}
Becomes ugly because you have to include
Check If This Is A Special Folder And Deal With It And Its Child Folders Differently ();
Fair enough Microsoft, to have a folder that could exist anywhere, on a remote PC, on a server etc. But really what is wrong with the UNIX/Linux way, use links to folder and if the destination physical folder has to move, alter the link. Then you can itterate them in a nice neat function treating them all as if ordinary folders.
I don't have enough reputation to add a comment so as a +1 to BobRassler's answer, string comparisons might be more useful.
bool isSpecialFolder = false;
DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(tbx_FolderName.Text, fileName));
foreach (Environment.SpecialFolder specialFolder in Enum.GetValues(typeof(Environment.SpecialFolder)))
{
if (directoryInfo.FullName.ToString()
.ToLower() ==
Environment.GetFolderPath(specialFolder)
.ToLower())
{
isSpecialFolder = true;
break;
}
}
if (isSpecialFolder)
{
// something
}
else
{
// something else
}
Use a reflection to get all values from that enum, like here http://geekswithblogs.net/shahed/archive/2006/12/06/100427.aspx and check against collection of generated paths you get.
I ended up using it this way:
public static bool IsSpecialFolder(DirectoryInfo directoryInfo, out Environment.SpecialFolder? _specialFolder) {
bool isSpecialFolder = false;
_specialFolder = null;
string directoryInfo_FullPath = directoryInfo.FullName;
foreach (Environment.SpecialFolder specialFolder in Enum.GetValues(typeof(Environment.SpecialFolder))) {
var specialFolder_FullPath = Environment.GetFolderPath(specialFolder);
if (string.Equals(directoryInfo_FullPath, specialFolder_FullPath, StringComparison.OrdinalIgnoreCase)) {
isSpecialFolder = true;
_specialFolder = specialFolder;
break;
}
}
return isSpecialFolder;
}
If handling strings from dubious sources (the user :-) ), there are three caveats to keep in mind:
Path.Combine vs. Path.Join, since they handle absolute paths (or paths that look like absolute paths) differently.
Path.GetFullPath, which takes a string an produces the full and normalized version of it.
GetFolderPath can return an empty string, which generates a System.ArgumentException: 'The path is empty. (Parameter 'path')' when used for creating a DirectoryInfo.
I like to keep this logic outside the method, but I am not sure if the OrdinalIgnoreCase or any other normalization is still necessary. I guess not.
P.S.: I think in modern lingo the method should be called TrySpecialFolder or something :-)

How to check if a specific file exists in directory or any of its subdirectories

In C#, how do I check if a specific file exists in a directory or any of its subdirectories?
System.IO.File.Exists only seems to accept a single parameter with no overloads to search subdirectories.
I can do it with LINQ and System.IO.Directory.GetFiles using the SearchOption.AllDirectories overload, but that seems a bit heavy handed.
var MyList = from f in Directory.GetFiles(tempScanStorage, "foo.txt", SearchOption.AllDirectories)
where System.IO.Path.GetFileName(f).ToUpper().Contains(foo)
select f;
foreach (var x in MyList)
{
returnVal = x.ToString();
}
If you're looking for a single specific filename, using *.* is indeed heavy handed. Try this:
var file = Directory.GetFiles(tempScanStorage, foo, SearchOption.AllDirectories)
.FirstOrDefault();
if (file == null)
{
// Handle the file not being found
}
else
{
// The file variable has the *first* occurrence of that filename
}
Note that this isn't quite what your current query does - because your current query would find "xbary.txt" if you foo was just bar. I don't know whether that's intentional or not.
If you want to know about multiple matches, you shouldn't use FirstOrDefault() of course. It's not clear exactly what you're trying to do, which makes it hard to give more concrete advice.
Note that in .NET 4 there's also Directory.EnumerateFiles which may or may not perform better for you. I highly doubt that it'll make a difference when you're searching for a specific file (instead of all files in the directory and subdirectories) but it's worth at least knowing about. EDIT: As noted in comments, it can make a difference if you don't have permission to see all the files in a directory.
The alternative is to write the search function yourself, one of these should work:
private bool FileExists(string rootpath, string filename)
{
if(File.Exists(Path.Combine(rootpath, filename)))
return true;
foreach(string subDir in Directory.GetDirectories(rootpath, "*", SearchOption.AllDirectories))
{
if(File.Exists(Path.Combine(subDir, filename)))
return true;
}
return false;
}
private bool FileExistsRecursive(string rootPath, string filename)
{
if(File.Exists(Path.Combine(rootPath, filename)))
return true;
foreach (string subDir in Directory.GetDirectories(rootPath))
{
if(FileExistsRecursive(subDir, filename))
return true;
}
return false;
}
The first method still extracts all of the directory names and would be slower when there are many subdirs but the file is close to the top.
The second is recursive which would be slower in 'worst case' scenarios but faster when there are many nested subdirs but the file is in a top level dir.
To Check for file existing in any specific directory do the following
Note: "UploadedFiles" is name of the folder.
File.Exists(Server.MapPath("UploadedFiles/"))
Enjoy Coding
It is a recursive search on the filesystem. You have some functional examples in CodeProject:
Simple File Search Class (by jabit)
Scan directories using recursion using events (by Jan Schreuder)
This is a recursive search function that will break out as soon as finds the file you've specified. Please note the parameters should be specified as fileName (eg. testdb.bak) and directory (eg. c:\test).
Be aware that this can be quite slow if you do this in a directory with a large quantity of subdirecories and files.
private static bool CheckIfFileExists(string fileName, string directory) {
var exists = false;
var fileNameToCheck = Path.Combine(directory, fileName);
if (Directory.Exists(directory)) {
//check directory for file
exists = Directory.GetFiles(directory).Any(x => x.Equals(fileNameToCheck, StringComparison.OrdinalIgnoreCase));
//check subdirectories for file
if (!exists) {
foreach (var dir in Directory.GetDirectories(directory)) {
exists = CheckIfFileExists(fileName, dir);
if (exists) break;
}
}
}
return exists;
}

Categories

Resources