C# .NET: Copy, Move and Delete File Progress - c#

Problem
I need to make a dialog, in which there are two progress bars: One for total progress (if multiple files copied / moved / deleted) and one for the current file's progress.
I basically need to have some way of copying, moving and deleting files and know the progress of the corresponding process in order to update my visuals.
I already found a question about the copying problem here, but this does not really work for moving, because if you move files around on the same disk, they usually do not get copied but rather the directory of them gets changed. (Moving to other disk results in copying and deleting) Also I do not know how to achieve the deleting process.
What I have already tried
Well, given that I posted a link to a solution for the copying process, I already solved the copying part (kind of) by modifying the code so that it suits my needs.
What is asked for?
A way to know the process of copying, moving or deleting one file. If I know this, I can figure the rest of it out.
Thanks

I once had the need to show a progress bar for a mix of copy, move and delete. For copying computing progress is easy. You base the computation on bytes. But what about moves and deletes?
I based progress on "synthetic bytes". The cost for a move or a delete comes mainly from the disk seeks required to address the relevant directory entries. I therefore counted them as a synthetic number of bytes that was roughly equivalent to one disk seek. For a magnetic disk a seek is ~10ms. At 100MB/sec that's a synthetic cost of 1MB. But I think multiple moves can be processed more efficiently by the OS so, if I recall correctly, I just made the cost of a move/delete 64KB.
The progress bar seemed fairly reasonable and the concept is very extensible to new operation types and new disk types (SSD).
I hope this applies to your case.

Related

What's a fast way to scan for directory changes without monitoring?

I'm writing an application in which I have to detect file changes inside a directory. New files, missing files, and so on. Each scan relative to the previous.
I'm currently doing a recursive scan to retrieve all the paths and comparing to the previous list in my database. The problem with this is that some clients require scanning for millions of files. This makes the system consume a lot of resources (cpu and memory). I'm even getting SystemOutOfMemory exceptions.
So I'm wondering if there's a better way to find those changes, maybe without a full scan.
Important note: I can't "monitor" for events since I have to detect changes that happened between scans, no matter if the system was running. I can't afford to lose a single change. So, unless it can catch changes made while it wasn't running I can't use FileSystemWatcher for this.
Without knowing how the Directory you are scanning is structured im assuming you have something simmilar to following example.
000/
001/
002/
With each directory having subdirectories simmilar to the top level directory.
This would allow you to build up an index like git does internally.
Storing identifiers for each tree item (subdirectories, Files) for easy comparrison.
You should then be able to counter the SystemOutOfMemoryException through splitting up the task into multiple sub tasks.
Regarding the runtime i see no possibility to lower it as one component of your system will always have to watch or compare each Item.
If when the files are written the modify date of the directories ist reliably updated you could use this as part of your comparrison.

Best way to track files being moved (possibly between disks), VB.NET (or C#)

I am developing a "dynamic shortcutting" application which creates special shortcut files which point to a registry entry rather than an actual file/executable. The registry entry contains the path of the desired file. I want to have a daemon running which watches the linked-to files and updates their registry entries if they are moved or renamed. Renamed I can handle using System.IO.FileSystemWatcher, but what is the best way to handle moved files?
I know this is beyond the basic functions of FSW (despite being a low-level file-system operation). The question is, what is the best way of doing it?
Most posts/articles I have read suggest ways that feel altogether "hacky", which basically involve looking for a delete followed by a create in a new place of a file, and connecting the two by file size, meta-data, time between the delete/create triggers, hashes, etc. This may well be the method I have to resort to, setting up FSWs on all drives. However, I am hoping there might be a better way.
Is it possible to either:
2.1. Listen in to the shell and "hear" move operations?
2.2 Or (even more radical) replace or add something to the shell move operation that either triggers some sort of event or performs the registry-updating task itself, precluding the need for the daemon?
I have a feeling that everyone is going to tell me that 1. is the only course, but I look forward to your suggestions. (answers in VB.NET preferred, but can translate from C# if necessary).
[I'm not sure if this should be appended as an "update" to my original post or posted as a separate answer]
To sum up (all two of) the answers plus my own experimenting (to try to give a definitive answer to this question):
It seems the only high-level (.NET) solution is to use the FileSystemWatcher which does not detect "move" out-of-the-box (despite it being a low-level command). The FSW approach is non-trivial, comparably resource-expensive, sloppy in places (i.e. using timers) and has its limitations and caveats. Nor does it provide a true reflection of "move" - it merely infers it from symptoms that are very likely to be a move (and have the same effect on the file-system in any case) but could theoretically be produced by non-move actions. Also, it appears you have to know what files you want to watch for moves in advance of the move happening, there's no-way of telling as it occurs.
On a lower-level (which would involve C++), one could hook API calls to get a faithful picture of when "moves" are called. This has the advantage that you don't have to decide to watch files in advance, and is also less resource-expensive than listening to "deletes" and "creates" and trying to compare them.
On a systems-programming level (which would involve C++ and could easily break your computer if you didn't know what you were doing) one could build a filesystem filter driver: this would take the concept of detecting moves to a truly anal level, detecting re-allocation of filesystem resources performed even without the kernel.
After some experimenting, here is the general structure of how the FileSystemWatcher approach (or at least the most obvious one to me) works, its quirks and its limitations. [no code atm, it's all pretty integrated into my application and I'm yet to optimise it, but I might add some snippets in here later].
The FileSystemWatcher method (to detect when files are moved or renamed):
.1. FileSystemWatchers.
You will need to create one FSW for each highest-level directory you want to monitor (for example, one for each writable logical drive).
.2. Renamed.
Straightforward renaming of the file is trivially handled.
.3. Moved.
This part is very far from trivial; it basically involves comparing files in three different scenarios.
3.0.1. Deciding if a deleted/moved-from file is the same as a created/moved-to file.
For determining whether a deleted and a created file are a match, filename is useless (can be changed during a move). You could use a mixture of file size and attributes like time created, or even a hash of the entire file. In my particular solution I only needed to watch the movement of specific files "registered" before load-time, so I was able to give these files a unique fingerprint as metadata that I could then use to compare files (this works fine in real-world scenarios, but is easy to break maliciously in testing, which disappoints me as a perfectionist.)
3.0.1.1. When to read filesize/attributes/take hash?
Before I came up with the static fingerprint idea, I was testing my code with a simple filesize + creation date validation check. I quickly realised though that I had to have a note of the filesize and creation date (or hash or whatever else you want to use) of the deleted file BEFORE it signals as "deleted", because you can't check the size of a file that doesn't exist. If (like me) you know the files you want to watch in advance, then you need to read in those values before you enable the FileSystemWatchers; you also need to listen for "change" events on those files to update the values of filesize and creation date, take a new hash etc. This then begs the question: what do you do if you DON'T know what files you are interested in watching to see if they move? What if you only know you are possibly interested in knowing if they've moved when they "delete"? That, unfortunately, is beyond me (it wasn't something I had to deal with.) Unless you can come up with a solution to this problem, there is zero point in continuing with the FileSystemWatcher approach. Furthermore, I would conjecture (though could very easily be wrong) that there is no high-level solution that will meet your needs. If you do however come up with a solution (please post it below/comment on this post/edit it in here on this post), I have made the rest of this compatible.
3.1. Scenario 1: Direct moving of the file itself.
Upon the "delete" of a specific file being detected, you need to start listening for a "create" of a congruous file. Rather than listening indefinitely for the matching "create" of a file that might just have been deleted (which in reality involves inspecting every file created in the directory), you can use a timer to start and stop a "listening" flag (practical, but from a purist point of view a little arbitrary), deciding that after e.g. 1000ms with no appropriately matching create it's likely there won't be one.
3.2.0. A common misconception.
A lot of people seem to be under the impression, after glancing at the docs, that moving or renaming a folder triggers a rename for all their subfiles and subfolders rather than a delete and a create. In actual fact what the docs say is:
If you cut and paste a folder with files into a folder being watched, the FileSystemWatcher object reports only the folder as new, but not its contents because they are essentially only renamed.
(i.e. only the top folder throws rename or create/delete and the subfiles/subfolders throw NOTHING). Meaning if you want to know when and where a certain file is moved, you have to listen out for each and every of its ascendent folders as well.
3.2.1. Scenario 2: Renaming of a containing folder.
In my solution, because I knew all the files I was watching, whenever one of my FileSystemWatchers reported a rename of a folder rather than a file (the portion of the string after the last "/" will contain no ".") I checked each of my watched files to see if their paths were in that directory and if so, changed the beginning of the filepath to the path of the new directory et voila!, I knew where my files had been moved to. If you do not now in advance what files you are looking for, then you will have to recursively search through everything in every folder that throws a "rename".
3.2.2. Scenario 3: Moving of a containing folder.
This one feels like a slap in the face: in order to build your move-detection routine, you have to be able to detect moves. Here folders will throw a "delete" followed by a "create". In my case the solution just recycles the techniques in 3.1 and 3.2.1: when a folder "delete" is detected, I check to see if it contains any of my watched files. If it does, I set a "listen" flag (and a timer to snuff it) and check the subdirectory path of my file in the old folder against every new folder "create" that is detected to see if it points to a file with the desired fingerprint. If it does, I now have the old and new paths of the file and have detected the move. If you don't know what files to watch for, you may have to validate folder moves by comparing size on disk and number of subfiles/subfolders between "deleted" folder and "created" folders to confirm a folder has moved first, then search the folder recursively for the files you're interested in.
3.3. FURTHER COMPLICATION: Cross-drive moving of large files.
This is a problem I fortunately didn't run into (because I was only comparing fingerprint metadata, and didn't need access to files); however moving large files between drives (which transfer in stages, triggering a create event then a series of change events) can cause real headaches.
3.3.1. Headache 1: The "create" fires when the destination file is incomplete.
This means comparing its size to a "deleted" file will produce a false negative. You can't even take a hash of the first part of the file to indicate to your program that this "might" be the deleted file, because the move operation will have the file access permissions locked down. You just have to try and tell if the created file might still be moving and wait for it to finish.
3.3.2. Headache 2: No sure way to "tell" that the created file is still being moved.
Some have suggested checking the file access permissions on the created file, but they might be indistinguishable from those on a file created and still in use by any random application. Others have suggested setting short time-limited listen flags for "changes" on the file, but again this is indistinguishable from a file being modified by an application. In fact if the file happened to be a log file constantly and rapidly being updated by some process, then waiting for "changes" to the file to timeout might never end.
3.3.3. Headache 3: (UNTESTED) possibly these sort of moves "delete" the file after "creating" the destination file*.
It makes sense that this would be the case, though I haven't tested it. [if anyone does know, feel free to edit (or delete) this section appropriately]
3.4. A philosophical quandry: are two identical files the same?
This is a very pedantic and arbitrary thought-experiment, but say you have two drives, each with an identical copy of File.txt. You run a batch file that deletes the copy on the first drive then immediately makes a copy of the file on the second drive into the same folder on the second drive and names it Copy of File.txt. Unless you are using fingerprints, your code will identify a delete and then a create of an identical file and be unable to distinguish what happened from a move (with renaming) of the file from the first drive to the second. The final state of the filesystem is identical in both cases so it shouldn't cause your application to behave unexpectedly, but art thou really content to call that a "move" based purely on isomorphism? (especially when you know the kernel sees it differently)?
Using high-level unrestricted api provided by C# - no, you cant. Use FileSystemWatcher.. On same drive operation of moving file is not "delete and create" - it's "rename".
If you can/want to go into lower-level, then you can hook MoveItem and MoveItems of IFileOperation shell's interface, and MoveFile from Kernel32.dll... It will work with most of apps, but require expansion for security rights for your application, that mostly unacceptable in corporative environment..
The task has two flaws that make it hard to implement: (a) move operation across the disks is actually a sequence of read/write operations followed by deletion rather than move. And during those read/write operations there can be some transformation of data in place ; and (b) moving can be performed not by just a shell.
What you can do is employ a filesystem filter driver to intercept file operations right when they take place. Then you need to detect the sequence of read and write operations performed by the same process over your file. I.e. if your code detects, that the file is read sequentially (NOTE: some copying tools can read the file in multiple threads in parallel) and then write similar blocks of data to the other file AND after reading everything the source file is deleted AND the complete file contents have been written to the other place, then you can guess that you have come over file move operation.
Bump & update: This may well be against the rules of StackOverflow, but I would like to point out to the many people landing on this page (and the myriad similar questions on SO) that I have started a feature request on MicroSoft UserVoice to add MOVE detection to FileSystemWatcher. The best solution in the long term, rather than trying to work around the problem, might be to petition MicroSoft to fix it. If you have come here because you too need a solution to this problem, please consider clicking here and voting for this feature.

AntiVirus ProgressBar

I have to add a progress Bar in my AV and the problem is that I don't know in advance how many files to scan.So I don't know the total count.If I count the files to be scanned,then the soft pauses for a while since it has to go through many files and in case of Full System Scan,this pause if of long duration which is not desired.Hence I dropped that code.I heard that avast dynamically changes progress bar value i.e. if the value approaches 100 and more files are found to scan then it pushes the value to like 50.Coding is in C#.
It would be foolish to try to get a list of all the files in one pass, before updating the UI at all.
Use threads; one to locate files and add their locations to a queue, and update a total file count.
Another to do the scanning, and update the count of scanned files.
and the UI thread will update itself based on that; so the upper bound will constantly be increasing... or, becoming more accurate.
Just count the folders 5-level deep, and calculate the progress based on how many of those folders were scanned. Increase dept to increase accuracy (at the cost of pre-calculation time). Use this article to learn how to enumerate directories as fast as possible: MSDN How to: Enumerate Directories and Files
Since AV scans should happen on a regular basis can you just remember how many files were in the last scan and then pad it by 5%? That would give the user a rough idea of how long it would take without having to do an up to date full scan right now. In order to get an accurate number for the first scan you would have to do a full directory search and suffer the pause that you described. As for custom individual folders you could either store the # of files for the first 2-3 levels of folders and then just do a directory scan below that as there would be a reduced number of files in each sub-directory.
I would recommend checking how much occupied space there is on the disk and evaluating number of files basing on density: files per GB. At first You have to make an assumption how much files do get stored in one GB (for my system it is 3860) During the scan you will know what is real density of scanned hard drive so you can adjust number of files.
Adjusting total number of files during the scan is the best method I can think off, and being user of you app I would like it much more, than waiting until you count all the files on the machine.

Getting number of files in a partition

I wrote a code in C++ CLI which can loop through all files in the system. I want to know the number of files are existing in the system programmatically without counting the files one by one!
Also...Would that number be useful in scanning progress bar?
If you are using NTFS, you can call the FSCTL_GET_NTFS_VOLUME_DATA IoControl to get a whole bunch of data for your volume. The number of files will be at most MftValidDataLength / BytesPerFileRecordSegment. Keep in mind that this is a maximum number of files that can be on the volume, so the actual number will always be less.
I would also hope that you'd be using FSCTL_ENUM_USN_DATA to read the files rather than walking the directory tree (which is inaccurate and error-prone). This thread has an example of somebody doing it in C#: http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/c1550294-d121-4511-ac32-31551497f64e/
That would depend highly on the filesystem in use (and the administrative privileges held by the impersonated user :))
You might find something here or you can enhance your question with more details

Bypassing Windows Copy design

I have been trying to figure out a way to streamline copying files from one drive (network or external in my case) to my main system drives. Short of creating a program that I will have to activate each time then choosing all the info in it, I have no really good design to bypass it.
I have been wondering if there was a way to intercept a straight-forward windows copy with a program to do it my way. The basic design would be that upon grabbing the copy (actually for this to be efficient, a group of different copies), the program would organize all the separate copies into a single stream of copies.
I've been wanting to do this as recently I have been needing to make backups of a lot of data and move it a lot as all my drives seem to be failing the past few months.
EDIT
For the moment, let's assume I am copying from a fully defragmented external drive (because I was). Let's also assume I am copying to a 99% defragmented drive (because I was). If I attempt to move every file at once, then the Windows copy method works great as it only needs to stream a contiguous file from one drive to the other (likely keeping the copy contiguous). This is basically best case, I need everything from there to move to here. So the copy method gets to grab the files in some algorithmicly logical order (typically by filename) and read then write the data to the new location.
Now for the reality of the situation, I instead copy a bunch of files from different locations (let's still assume fully defragmented as this is the case of my external), to different file locations on the second drive. I don't wish to wait for each one to finish before starting the next one, so I go through the folders picking and choosing what goes where and soon my screen is covered in COPY windows displaying the status of all the copies attempting to run at the same time. This presents a slight slowing down of the copy as a whole as each copy progresses independently of the others. That means that instead of a single contiguous file taking the total bandwidth available, all these files share the bandwidth approximately equally (this also adds in read and write delays to the disks as now the system has to grab a little from each file before circling back to the first). Still assuming best case scenario, this slower than if all the files were in one location and moved to one location.
What I want to do is intercept all the separate downloads and organize them into a list of single downloads that would happen one after another.
The only way you can intercept nicely is the FileSystemWatcher.
This is probably what you are looking for
detect Windows Explorer copy operation
You need to create a Shell Extension according to this post.

Categories

Resources