FileSystemWatcher - Recognize files in deleted folder - c#

I have another problem with my FileSystemWatcher ;)
Simply, what I want to do:
I want to watch specific folders, in order to add the paths of the files to a List(Of String) and set an integer-variable for each new file.
So:
I create the file "Test.png" and it does that:
(List) categoryFilesList.Add(e.FullPath)
(Integer-variable) counter += 1
Well, this is working well.
Now, if a file is deleted again, it should remove the path from the list and set the integer-variable down again. (-= 1).
This is working well, too, but now I have a problem, I am not able to find a solution for.
I have IncludeSubdirectories set to true to scan the subdirectories for files. On adding this works fine, but on removing it does not.
That is because I do NOT add folders to the list, only files. So, if I delete a folder, I am not able to know what content it was containing and how much files it were, because it is already gone.
But I would want to know, how many files there were in and what their paths were to remove them again from the list and set the integer-variable down again with the amount of deleted files.
I though about saving it anyway on changing, but I do not know how exactly and if this is a good idea.
Is there a better solution?
If something is unclear, then I am sorry, ask then.
I also accept C#-answers, VB.NET is just because my friend wants that.
Thanks!

My solution to a similar problem is as follows:
Ensure your FileSystemWatcher is configured to track subfolders. Once you do that, you should get a Delete event any time a folder or file is deleted. In the case where a folder is deleted, simply iterate through your collection and remove any items that contain the path of the folder.
For example:
int numRemoved = categoryFilesList.RemoveAll(
delegate(String s)
{
return s.Contains(<DeletedDirectoryPath>);
}
);
This would remove any items in your list which contain . You can then use numRemoved to maintain your file count. Alternatively, you could simply use the count of the list instead of maintaining your own copy of that data via the Count property on your list.
Additionally you should be sure that you handle the cases where items and directories are renamed so that your list of filenames is always up to date.

I am afraid that this isn't really possible to do, because of the way the deletion of a directory works in a system. The directory is deleted, but the files recursively are just part of the directory.
This was quite well described here on MSDN forums :
I you observe the behavior of the trash on your desktop, you will see
that each time you delete a folder, you can see that folder in the
trash but you cannot see the elements within that dropped folder. The
only way to see those elements is to recover the folder from the
trash.
I think it happens the same thing with the FSW class. When you delete
a folder inside a watched directory you only have the event of the
deleted folder because the folder and its contents is not really
deleted but only moved to the trash. This is why you never receive the
deleted events for the included files because they are still somewhere
on your system
According to this question here on SO, the same problem occurs when a folder is moved into the structure (and the question also shows the workaround to the moving issue).
Possible workaround for the deletion problem would be browsing the structure in advance and saving the amount of files in the directories into a tree-like structure, but it will definitely be much more complex than this. It would look like this:
public class DirectoryFiles
{
public int Count {get; set;}
public string FullPath {get; set;}
public List<DirectoryFiles> Subdirectories {get; set; }
}
private DirectoryFiles Initialize(string fullPath)
{
if (Directory.Exists(fullPath))
{
var toReturn = new DirectoryFiles { Subdirectories = new List<DirectoryFiles>() };
foreach (string directory in Directory.GetDirectories(fullPath))
{
toReturn.Subdirectories.Add(this.Initialize(directory));
}
toReturn.Count = toReturn.Subdirectories.Sum(x => x.Count) + Directory.GetFiles(fullPath).Count();
return toReturn;
}
else
{
throw new DirectoryNotFoundException(String.Format("Directory {0} does not exist", fullPath));
}
}
and in the class where you are counting:
private int GetCountOfFiles(DirectoryFiles files, string fullPath)
{
if (files.FullPath.Equals(fullPath, StringComparison.InvariantCultureIgnoreCase))
{
return files.Count;
}
foreach (var subdir in files.Subdirectories)
{
if (this.GetCountOfFiles(subdir, fullPath) != -1)
{
return subdir.Count;
}
}
return -1;
}
This may need improvements for:
Permissions - it will throw an exception if you don't have access to the files,
The performance is not perfect, on every subfile or subdirectory deletion you'll need to rebuild the whole structure. There can be some optimizations created for that as well, but should work quite well for smaller subdirectories. If you need performance improvements, I'll leave that to you. For that, consider adding DirectoryFiles Parent to DirectoryFiles and recount the directories on the way up.

Related

Unity/C# Savegame Migration

I've written a SaveLoad class, which contains a Savegame class that has a bunch of ints, doubles, bools but also more complex things like an array of self-written class objects.
That savegame object is being created, serialized and AES encrypted on save and vice versa on load - so far, so good.
The problem I'm facing now is that if there are new variables (in a newer version of the game) that have to be stored and loaded, the game crashes on load, because the new variables can't be loaded correctly (because they are not contained in the old save file). E.g. ints and doubles contain the default 0 while an array is not initialized, thus null.
My current "solution": For each variable that is being loaded, check if it doesn't contain a specific value (which I set in the Savegame class).
For example: In Savegame I set
public int myInt = int.MinValue;
and when loading, I check:
if(savegame.myInt != int.MinValue){
//load successful
}else{
//load failed
};
This works so far for int and double, but once I hit the first bool, I realized, that for every variable I have to find a value that makes "no sense"(not reachable usually), thus was a failed load. => Shitty method for bools.
I could now go ahead and convert all bools to int, but this is getting ugly...
There must be a cleaner and/or smarter solution to this. Maybe some sort of savegame migrator? If there is a well done, free plugin for this, that would also be fine for me, but I'd prefer a code-solution, which may also be more helpful for other people with a similar problem.
Thanks in advance! :)
Your issue is poor implementation.
If you are going to be having changes like this, you should be following Extend, Deprecate, Delete (EDD).
In this case, you should be implementing new properties/fields as nullables until you can go through and data repair your old save files. This way, you can check first if the loaded field is null or has a value. If it has a value, you're good to go, if it's null, you don't have a value, you need to handle that some way.
e.g.
/*We deprecate the old one by marking it obsolete*/
[Obsolete("Use NewSaveGameFile instead")]
public class OldSaveGameFile
{
public int SomeInt { get; set; }
}
/*We extend by creating a new class with old one's fields*/
/*and the new one's fields as nullables*/
public class NewSaveGameFile
{
public int SomeInt { get; set; }
public bool? SomeNullableBool { get; set; }
}
public class FileLoader
{
public SavedGame LoadMyFile()
{
NewSaveGameFile newFile = GetFileFromDatabase(); // Code to load the file
if (newFile.SomeNullableBool.HasValue)
{
// You're good to go
}
else
{
// It's missing this property, so set it to a default value and save it
}
}
}
Then once everything has been data repaired, you can fully migrate to the NewSaveGameFile and remove the nullables (this would be the delete step)
So one solution would be to store the version of the save file system in the save file itself. So a property called version.
Then when initially opening the file, you can call the correct method to load the save game. It could be a different method, an interface which gets versioned, different classes, etc but then you would require one of these for each save file version you have.
After loading it in file's version, you could then code migration objects/methods that would populate the default values as it becomes a newer version in memory. Similar to your checks above, but you'd need to know which properties/values need to be set between each version and apply the default. This would give you the ability to migrate forward to each version of the save file, so a really old save could be updated to the newest version available.
I'm facing the same problem and trying to build a sustainable solution. Ideally someone should be able to open the game in 10 years and still access their save, even if the game has changed substantially.
I'm having a hard time finding a library that does this for me, so I may build my own (please let me know if you know of one!)
The way that changing schemas is generally handled in the world of web-engineering is through migrations-- if an old version of a file is found, we run it through sequential schema migrations until it's up-to-date.
I can think of two ways to do this:
Either you could save all saved files to the cloud, say, in MongoDB, then change their save data for them whenever they make updates or
You need to run old save data through standardized migrations on the client when they attempt to load an old version of the save file
If I wanted to make the client update stale saved states then, every time I need to change the structure of the save file (on a game that's been released):
Create a new SavablePlayerData0_0_0 where 0_0_0 is using semantic versioning
Make sure every SavablePlayerData includes public string version="0_0_0"
We'll maintain static Dictionary<string, SavedPlayerData> versionToType = {"0_0_0": typeof(SavablePlayerData0_0_0)} and a static string currentSavedDataVersion
We'll also maintain a list of migration methods which we NEVER get rid of, something like:
Something like
public SavablePlayerData0_0_1 Migration_0_0_0_to_next(SavablePlayerData0_0_0 oldFile)
{
return new SavablePlayerData0_0_1(attrA: oldFile.attrA, attrB: someDefault);
}
Then you'd figure out which version they were on from the file version, the run their save state through sequential migrations until it matches the latest, valid state.
Something like (total pseudocode)
public NewSavedDataVersion MigrateToCurrent(PrevSavedDataVersion savedData)
{
nextSavedData = MigrationManager.migrationDict[GetVersion(savedData)]
if (GetVersion(nextSavedData) != MigrationManager.currentVersion) {
return MigrateToCurrent(nextSavedData, /* You'd keep a counter to look up the next one */)
}
}
Finally, you'd want to make sure you use a type alias and [Obsolete] to quickly shift over your codebase to the new save version
It might all-in-all be easier to just work with save-file-in-the-cloud so you can control migration. If you do this, then when a user tries to open the game with an older version, you must block them and force them to update the game to match the saved version stored in the cloud.

Alternate path in Orchard

I've created my own class which adds alternates to the relevant collection:
ShapeMetadata.Alternates.Add("DansForm");
This works fine, but only when DansForm is in the Views root.
I want to keep all the DansForm alternates in a directory.
How can I reference a path?
What I've tried:
ShapeMetadata.Alternates.Add("Forms/DansForm");
ShapeMetadata.Alternates.Add("/Forms.DansForm");
ShapeMetadata.Alternates.Add("Forms.DansForm");
ShapeMetadata.Alternates.Add("Forms\\DansForm");
ShapeMetadata.Alternates.Add("Forms__DansForm");
ShapeMetadata.Alternates.Add("Forms_DansForm");
Doesn't find any of them.
You cant organise shape templates into directories. Orchard has a hardcoded list of directories it searches for shape templates:
public IEnumerable<string> SubPaths() {
return new[] { "Views", "Views/Items", "Views/Parts", "Views/Fields" };
}
I guess this is for performance reasons. I suppose you could write an additional implementation of IShapeTemplateHarvester to find shapes in your own folders, but seems like overkill just to make it a little more organised. I think you'll just have to suffer the mess of views. Sorry!

List of files in a project visual studio 2010

I'm working with the Visual studio 2010 SDK, developing an extension. I'm trying to get a list of all the files in a project, however currently I cannot get the list of files within a filter. All that I can see returned is the filter name, and the number of files inside that filter.
I'm currently using the ProjectItem interface, specifically the FileNamesProperty. However, in their documentation for this they say:
When the project item's ProjectItems property has a value, and the ProjectItem object represents a filter folder on the disk, then the FileNames property returns only the name of the filter folder.
Is there another method that I can use to list the files inside a filter, or am I tackling this the wrong way?
So, I've made enough progress on this to continue. I only need to use this extension in a Visual C++ project, so I was able to use the VCProject Interface to get a list of projects, and the VCProject Files property to get the list of files from the projects.
Something like this
VCProject prj;
int count = _applicationObject.Solution.Projects.Count;
for(int i = 1; i <= count; i++ )
{
//Start counting at one for some reason
prj = (Microsoft.VisualStudio.VCProjectEngine.VCProject)_applicationObject.Solution.Projects.Item(i).Object;
foreach (VCFile item in prj.Files)
{
//Item is the file, do whatever you want with it here
}
}
I would still like to know if there is a way to do this for all projects(not just VisualC++).

Looping through a set of image resources (.resx)

I've got a 3 sets of 9 images in seperate .resx files, and I'm trying to figure out how to loop a single set into 9 static picture boxes.
Loop through all the resources in a .resx file
I've looked through some of the solutions in the above link, like using ResXResourceReader, but it comes up with a parsing error when I use the GetEnumerator method.
When I use the ResourceSet resourceSet = MyResourceClass.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); line, there's no definition for the ResourceManager within the Form class, or a GetResourceSet method when I create my own ResourceManager.
There is actually a method called CreateFileBasedResourceManager which I've dabbled in, but truth be told I don't understand the parameters it needs too well aside from the directory.
I've also looked at some of the solutions involving assemblies and retrieving the executing image assembly at runtime, but I think that's a little out of my depth at the moment.
Can anyone tell me what I'm doing wrong with the first two methods or maybe something entirely different?
Looking at MSDN, you should be able to iterate the values from a RESX file like so:
string resxFile = #".\CarResources.resx";
// Get resources from .resx file.
using (ResXResourceSet resxSet = new ResXResourceSet(resxFile))
{
// Retrieve the image.
Object image = resxSet.GetObject("NAMEOFFILE", true);
}
If you wanted to iterate all objects in the RESX file, you could do something like this:
using (ResXResourceReader resxReader = new ResXResourceReader(resxFile))
{
foreach (DictionaryEntry entry in resxReader) {
// entry.Key is the name of the file
// entry.Value is the actual object...add it to the list of images you were looking to keep track of
}
}
More can be found here.
I known that this is a old question, but today I got the same problem, and solve setting the BasePath property, like this:
oResReader = new ResXResourceReader(strInputFile);
oResReader.BasePath = Path.GetDirectoryName(strInputFile);
I found this solution here

fastest way to search and delete files inside a directory

I've got a array of class in which one member is the full path to a file. I need to delete all those files from the directory which is not included in the array. As Usual, I am using the convential compare and delete method. I need to know if there any fast way to accomplish this.
I heard it can be done using Linq, but i dont have knowledge on linq.
My class struct is like below.
Class ImageDetails
{
public string Title;
public Boolean CanShow;
public String PathToFile;
}
I have an array of ImageDetails. The PathToFile contains full path
}
You can use Except() to handle this:
var filesToDelete = Directory.GetFiles(Path.GetDirectoryName(yourClass.FilePath)).Except(yourClass.TheArray);
Why do you need to compare? If you have the full file name, then
File.Delete(fileName);
is all you need. The file IO is likely to be the slowest part of this, so I don't think Linq will make much difference to the performance.
If the file may not exist, then check for that first:
if (File.Exists(fileName))
{
File.Delete(fileName);
}
Edit: I see you mean that you want to delete the file if it is not in the array. I read your question to mean that the directory is not included in the array.
Still, the actual file deletion is likely to be the slowest part of this.

Categories

Resources