Handle system folders event in windows - c#

I am writing some C# code and I need to detect if a specific folder on my windows file system has been opened while the application is running. Is there any way to do it? WinAPI maybe?

There are three API things I think you should check out:
FindFirstChangeNotification() http://msdn.microsoft.com/en-us/library/aa364417%28VS.85%29.aspx
That gives you a handle you can wait on and use to find changes to a file in a particular file, directory, or tree of directories. It won't tell you when a directory is browsed, but it will tell you when a file is saved, renamed, and so on and so forth.
SetWindowsHookEx() http://msdn.microsoft.com/en-us/library/ms644990%28v=VS.85%29.aspx
You can set that up to give you a callback when any number of events occur - in fact I'm pretty positive that you CAN get this callback when a directory is opened, but it will probably be inordinately difficult because you'll be intercepting messages to explorer's window. So you'll be rebooting during debugging.
Windows Shells http://msdn.microsoft.com/en-us/library/bb776778%28v=VS.85%29.aspx
If that wasn't painful enough, you can try writing a shell program.
If you're trying to write a rootkit, I suppose you don't want me to spoil the details for you. If you're NOT trying to write a rootkit, I suggest you look it up - carefully. There are open source rootkits, and they all basically have to monitor file access this way to hide from the user / OS.

Go with the Windows Shell Extensions. You can use Shell Namespace Extensions to make a "virtual" folder that isn't there (or hides a real one), like the GAC (C:\Windows\assembly)
Here are several examples of Shell Extension coding in .Net 4.0.
A Column Handler would let you know when a folder is "Opened", and even let you provide extra data for each of the files (new details columns).

Check out the FileSystemWatcher class.

The closest thing that I can think of, that may be useful to you, is using the static Directory class. It provides methods to determine the last time a file or directory was accessed. You could setup a BackgroundWorker to monitor if the directory was accessed during a specified interval. Keep track of the start and end of the interval by using DateTime, and if the last access time falls between those, then you can use the BackgroundWorker's ProgressChanged event to notify the application.
BackgroundWorker folderWorker = new BackgroundWorker();
folderWorker.WorkerReportsProgress = true;
folderWorker.WorkerSupportsCancellation = true;
folderWorker.DoWork += FolderWorker_DoWork;
folderWorker.ProgressChanged += FolderWorker_ProgressChanged;
folderWorker.RunWorkerAsync();
void FolderWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
while(!worker.CancellationPending)
{
DateTime lastAccess = Directory.GetLastAccessTime(DIRECTORY_PATH);
//Check to see if lastAccess falls between the last time the loop started
//and came to end.
if(/*your check*/)
{
object state; //Modify this if you need to send back data.
worker.ReportProgress(0, state);
}
}
}
void FolderWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Take action here from the worker.ReportProgress being invoked.
}

You could use the FileSystemInfo's LastAccessProperty. The problem though is that it can be cached.
FileSystemInfo: http://msdn.microsoft.com/en-us/library/975xhcs9.aspx
LastAccessTime Property: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.lastaccesstimeutc.aspx
As noted that this can be pre-cached.
"The value of the LastAccessTimeUtc property is pre-cached if the current instance of the FileSystemInfo object was returned from any of the following DirectoryInfo methods:
GetDirectories
GetFiles
GetFileSystemInfos
EnumerateDirectories
EnumerateFiles
EnumerateFileSystemInfos
To get the latest value, call the Refresh method."
Therefore call the Refresh method but it still might not be up to date due to Windows caching the value. (This is according to msdn doc "FileSystemInfo.Refresh takes a snapshot of the file from the current file system. Refresh cannot correct the underlying file system even if the file system returns incorrect or outdated information. This can happen on platforms such as Windows 98." - link: http://msdn.microsoft.com/en-us/library/system.io.filesysteminfo.refresh.aspx

I think the only way you can reliably achieve this is by monitoring the currently running processes and watch closely for new Explorer.exe instances and/or new Explorer.exe spawned threads (the "Run every window on a separate process" setting gets in the way here).
I admit I don't have a clue about how to code this, but that's what I would look for.

Related

How to distinguish System.Diagnostic.Process.Exit events based on the reason for exit for external processes in c#/wpf applications

Within my application the user can select different files and executables via a OpenFileDialog. From the path of the chosen file a ProcessModelis created and added to a ObservableCollection.
The plan is that the user can select different files and programs and add them to a list within the application. The software should then open - and later close - the chosen files as soon as a certain event is fired (in this case a gesture by the user captured with the camera).
The ProcessModel holds several properties for different options, but the ones important for my question are set in the constructor as follows:
public ProcessModel(string path)
{
ProcessName = Path.GetFileNameWithoutExtension(path);
processExtension = Path.GetExtension(path);
ProcessPath = path;
instances = new List<Process>();
}
What I want to do with each ProcessModel is, that in case a certain event in the application is triggered I want to start the associated process. Furthermore, I want to track, how many instances of the same processes have been started and also be able to close them via another event. To achieve this, I listen to the Process.Exited event and handle my list of instances accordingly.
Before I get to the actual issue, here are the methods I use (all within my ProcessModelclass):
creating and starting a new process:
/// <summary>
/// Starts a new instance of the current process
/// </summary>
public void StartNewInstance()
{
try
{
Process newInstance;
if (processExtension == GenDefString.ExecutableExtension)
{
newInstance = new Process();
newInstance.StartInfo.UseShellExecute = false;
newInstance.StartInfo.FileName = ProcessPath;
newInstance.Start();
}
else
{
newInstance = Process.Start(ProcessPath);
}
newInstance.EnableRaisingEvents = true;
newInstance.Exited += OnProcessExited;
instances.Add(newInstance);
UpdateNrOfInstances();
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
stopping the last instance in the list:
/// <summary>
/// stops the last instance int he list
/// </summary>
public void StopLastInstance()
{
if (instances.Count < 1) return;
try
{
var instanceToDelete = instances.Last();
instanceToDelete.Exited -= OnProcessExited;
instanceToDelete.CloseMainWindow();
instanceToDelete.Close();
instances.RemoveAt(instances.Count - 1);
UpdateNrOfInstances();
}
catch(Exception e)
{
MessageBox.Show(e.Message);
}
}
method, which listens to the event of the process being closed (externally):
/// <summary>
/// Trigger Status changed Event was raised
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
public void OnProcessExited(object source, EventArgs e)
{
var process = source as Process;
if (process == null) return;
instances.Remove(process);
UpdateNrOfInstances();
}
Updating the number of current instances to the GUI:
/// <summary>
/// Sets the value for the number of instances (used for GUI update)
/// </summary>
private void UpdateNrOfInstances()
{
NrOfInstancesRunning = instances.Count;
}
As you can see in the StartNewInstance() method, I check if the extension is from an executable or an software associated file (GenDefString.ExecutableExtension is a string holding #".exe"). Everthing is working as intented, however if for example the user puts two different .pdf files, the second process is immediately terminated, because my pdf-viewer has already been started by ProcessModel associated to the first .pdf file.
For this case I need to handle things differently. As far as I can see, I have to ways of doing it:
Forcing each newly started process into it's own Window: I don't
really consider this a good idea additionally to not really knowing
how to achieve that with all different software types.
Informing the user what is happening and why there are 0 instances in the respective item, when clearly the file is opened. I prefer this one and my approach would be to get information from the source argument of the OnProcessExited() method.
So my question: how can I distinguish if the process exit happened because of the described case?
EDIT: My current approach would be to track the process IDs, however I'm wondering if there is a better solution, maybe already implemented to the Process-Class
EDIT2: For better understanding, the complete project can be found here.
You are seeing the behavior of a single instance process. Standard examples of such programs are any Microsoft Office app, browsers like IE, Adobe Reader is likely to be relevant to this question. These are very large processes that consume a lot of system resources, starting them multiple times could bring the machine to its knees. In the olden days anyway.
The underlying mechanism is the same for all of them. When you start the second instance it discovers that the program is already running. It uses a process interop mechanism like a named pipe to pass the command line arguments to the first instance. In this case the path of the file you are trying to open. The first instance creates another window to show the content of the file with its own taskbar button. Pretty indistinguishable from the process running multiple times, other than that you'll see the Exited event fire quickly after starting it. Exactly how long that takes is unpredictable, usually in less than a second but might be many seconds when the machine is loaded.
Not the only quirk, Process.Start() might even return null. In other words, no process created at all. I only know of Explorer.exe behaving that way. A side-effect of its api creating the process (ShellExecuteEx() under the hood) and it recognizing that it is asked to start itself.
Notable is that implementing such a process is directly supported in the .NETFramework. A bit obscure, the namespace is not terribly popular as of late.
There is very little you can do about this. There is no standard mechanism to force a program to not do this, it might have a command line option but that's specific to the program you start. Nothing you can see back in the Process object either, it looks like an entirely normal process termination with the ExitCode property at 0. Other than that it exited a lot quicker than normal, that is the only real cue you have. With the complication that the process might have malfunctioned, albeit that ExitCode ought to be non-zero when that happens. Seeing the first process open the document requires UI Automation, but might not be so easy to make universal. Find sample code in this post.
I would add a List<string> SingleInstance loaded from persistence(see later).
If the processExtension is in the list, launch in new window.
If the process fails for this reason(at start within .5s of launch) add the processExtension to the list and relaunch in a separate window. Save the list to Registry or file to be reloaded on startup.
This procedure only starts new windows for processes not able to open multiple documents as described.
If needed add a dictionary<string,int> to count the existing instances, to allow the in-process load of the first file, and fail-over to external processes.

How to poll a directory in ASP.NET?

I want to poll a directory to check whether new file is added to the directory in ASP.NET web application (C#). If any new file is added I want to read that file.
Can anybody give me an idea how to do that?
Thanks.
Normally you would use the FileSystemWatcher class. However, you have another problem. A web application isn't really suited for background processes. You can get away with it by using a background task and threading in general, but it's probably not a good idea. Always assume that your web application is stateless and can be re-started by the server at any time.
Ask yourself:
What is going to trigger this polling?
How is the application going to respond to this polling?
A web application is essentially a request/response system. Thus, any server-side logic (such as the polling) should be triggered by a request. But once the response is given, what is going to become of the polling? Suppose you fork off a thread in the web application which will poll in the background. What is it going to do when it finds something? There's no request/response interacting with it at that point.
Could this polling perhaps be delegated to another application? Perhaps a Windows Service? Then, in response to finding something during the polling, it can modify values in the web application's database. That way future requests to the web application would see the updated state.
This would more cleanly separate the concerns on an architectural level.
you can use FileSystemWatcher and create the instance in Application_Start event.
Sample code:
protected void Application_Start(
Object sender, EventArgs e)
{
FileSystemWatcher fsw =
new FileSystemWatcher(
Server.MapPath( “.” ) );
Application.Add( “myfsw” , fsw );
// Add event handlers here
fsw.EnableRaisingEvents = true;
}
Dispose this when application ends.
protected void Application_End(
Object sender, EventArgs e)
{
FileSystemWatcher fsw =
(FileSystemWatcher
)Application[“myfsw”];
Application.Remove( “myfsw” );
fsw.Dispose();
}
First after your program loads check the directory content and keep it as a list.After that add a timer. The timer will check the content of the directory and compare the current content with the last logged content. After comparing you can see which files are changed in the directory.
you can change the frequency of the timer based on your needs.
Hope it helps.
edit:
call GetDirectoryContent(); in your program's onload.
FileInfo[] lastUpdatedFies;
FileInfo[] temporaryFiles;
private void GetDirectoryContent()
{
DirectoryInfo di = new DirectoryInfo("c:/mydirectorypath/");
lastUpdatedFies = di.GetFiles(".");
}
private void GetDirectoryContent()
{
DirectoryInfo di = new DirectoryInfo("c:/mydirectorypath/");
lastUpdatedFies = di.GetFiles("*.*");
}
protected void tmrDirectory_Tick(object sender, EventArgs e)
{
DirectoryInfo di = new DirectoryInfo("c:/mydirectorypath/");
temporaryFiles = di.GetFiles("*.*");
foreach (FileInfo f in lastUpdatedFies)
{
//compare the list of files and do whatever you want.
// you can track any kind of data this way.
}
}`
you can also adjust the timer frequency. In this example i just kept track of files.so you will learn only if a file is deleted or added. if you want to keep track of the file size you can also do it in the same way.
Add Filewatcher in global.asmx when the application start event.
It's not clear, what do you want to do with these files. If you want read these files and cache them for future output, you can use ASP.NET Cache with CacheDependency on specific directory and a callback which will re-read the directory and add new file to cache. You should take a look at Cache.Insert method and CacheDependency constructor

FileSystemWatcher getting too many event

I'm trying to implement a file watcher that will raise an event if the file content was changed.
The problem that once the file I am watching was modified, I am getting 2 events.
(I want to get it only once)
_automationStatusWatcher = new FileSystemWatcher(fileInfo.Directory.FullName,
fileInfo.Name);
_automationStatusWatcher.NotifyFilter = NotifyFilters.LastWrite;
_automationStatusWatcher.Changed += OnAutomationStatusChanged;
_automationStatusWatcher.EnableRaisingEvents = true;
The file that i'm watching is not intended to be recreated/moved/deleted/whatever. its purpose is to be an xml database file that i need to monitor it once it changes. i want to get only 1 event when the file is modified and to ignore the other events.
How can I do that?
Manual:
Note
Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
I had to cope with this problem some time ago. I wanted to be notified when a file was created. The problem was that the event is triggered when the file is still empty or still being written to, not when the new file is closed.
The solution was to create the file using a temporary name and when finished renaming the file to its final name. I then watched for the rename-event.
You could name the file to myfile.tmp while writing to it and when finished rename it to myfile.xml and watch the rename-event.
You could attempt to unhook your event handler once you catch a change until you are ready to accept more changes:
_automationStatusWatcher.Changed -= OnAutomationStatusChanged;
But that is not necessarily thread safe and you could get multiple firings anyway.
Another option is to use a semaphore variable to see if you are handling the change:
private bool _inStatusChange = false;
private void OnAutomationStatusChanged(object sender, args...)
{
if (_inStatusChange)
{
return;
}
else
{
_inStatusChange = true;
//do work
_inStatusChange = false;
}
}
You should use appropriate locking to keep access to the semaphore variable thread safe.
i meet this problem too, now i found out this way :
new Thread(() => {
while (true) {
var r = watch.WaitForChanged(WatcherChangeTypes.All);
this.Invoke(new ThreadStart(() => {
listBox1.Items.Add(string.Format("{0} {1} {2}", DateTime.Now, r.Name, r.ChangeType));
}));
}
}) { IsBackground = true }.Start();
its very similar to nio in java
I had a similar problem for checking updates in logging configurations.
I read about the multiple events problem of FileSystemWatcher. So I decided to implement another solution.
I check for every access to my configuration file if it has changed by comparing modified date. Later I added a latence (2 seconds) to avoid too many accesses to filesystem. Maybe you can also use that way.
If you look at the documentation for FileSystemWatcher it says that it will fire multiple times.
Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher.
I would recommend doing some kind of queuing mechanism so that you don't have multiple actions on the same file.

.NET Denying Access to Directories After Copy Operations

I'm performing a "safe" copy of a directory over another directory as follows:
Given the source C:\Source and target C:\Target
Copy C:\Source to C:\Target-incoming
Move C:\Target (if it exists) to C:\Target-outgoing
Move C:\Target-incoming to C:\Target
Delete C:\Target-outgoing (if it exists)
If any of the first three steps fail, I'll attempt to put things back as they were to prevent data loss.
However, the move of C:\Target-incoming to C:\Target fails with "Access to the path C:\Target-incoming is denied" most of the time.
At the moment, inserting Thread.Sleep(100) just before the move operation fixes the problem for me. However, waiting .1 of a second seems ridiculous to me. Thread.Sleep(10) isn't enough to fix it. I also have the sinking feeling that the value I have to wait depends on the speed of disk IO.
So, my questions:
Can I prevent this from happening?
If not, is there a way of finding out when the lock on the directory is released after copying it?
Edit: For clarity, I'm doing all these operations in one method on one thread, and I'm just using Thread.Sleep() to pause code flow for a moment. The moves and copies are being done with standard .NET Directory.Move(), Directory.CreateDirectory() and File.CopyTo() methods. It would appear that the .NET methods are returning before the locks on the respective files are being released, causing the necessity to wait an amount of time before continuing.
What could be happening is probably that your thread is trying to "Move C:\Target-incoming to C:\Target" WHILE the "Move C:\Target to C:\Target-outgoing" is NOT finished YET.
This track is confirmed by the success of your process after short Thread Sleep.
Try to Chain your processes, i.e : Divide each step into specific methods, and call the methods one after the other (sync'ing the start of a method to the end of the previous one)
There are various ways to do that (among others syncing/locking/chaining different threads per process/step)
You could check Thread Synchronization in .NET
But of course, this is not the only possible cause for your problem.
After a bunch of testing, it seems like the very act of trying to move a locked folder gets the OS to hurry up and release the lock, even if the first attempt fails.
I wrote this extension method to DirectoryInfo:
public static void TryToMoveTo(this DirectoryInfo o, string targetPath) {
int attemptsRemaining = 5;
while (true) {
try {
o.MoveTo(targetPath);
break;
} catch (Exception) {
if (attemptsRemaining == 0) {
throw;
} else {
attemptsRemaining--;
System.Threading.Thread.Sleep(10);
}
}
}
}
While debugging the original problem, I settled on waiting for 100ms as anything less seemed to cause exceptions (I tried 10, 25, 50, 75 and 100ms). However, in the method above I wait 10ms before retrying, and I never, ever got more than one exception thrown in each of my hundreds of test runs.
You can always try waiting in a loop, up till a maximum # of tries. You can check to see if the directory is locked by calling CreateFile and checking it's return code. Be sure to read through the "flags" section of the docs because you need to pass in a special flag to open a directory.
Someone else mentioned in a comment that you may want to try Transactional NTFS. If you can, you might want to try that.
check wethere source and target directories exist before copying or moving using io.directory.exists
the access deneied error is caused by either source or target are not found.

What is the proper way of handling logoff / shutdown / restart when the application has unsaved data?

In WPF App.Current.SessionEnding must return in a few seconds, otherwise the "application does not respond" window appears. So the user can't be asked in this event handler to save his data, because the user's response takes longer than a few seconds.
I thought a solution would be to cancel the logoff / shutdown / restart, and resume it when the user answered to the file save dialog.
ReasonSessionEnding _reasonSessionEnding;
App.Current.SessionEnding +=
new SessionEndingCancelEventHandler(Current_SessionEnding);
void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e)
{
if (_dataModified)
{
e.Cancel = true;
_reasonSessionEnding = e.ReasonSessionEnding;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(EndSession));
}
}
void EndSession()
{
if (SaveWithConfirmation()) // if the user didn't press Cancel
//if (_reasonSessionEnding = ReasonSessionEnding.Logoff)
// logoff
//else
// shutdown or restart ?
}
The problem is that ReasonSessionEnding does not tell me if Windows was shutting down or restarting (it does not differentiate between the two).
So, what should my program do on the session ending event ?
Should it even do anything, or doing nothing on this event is the standard ?
The user is asked to save his changes in my main form's OnClosing method, so he does not lose data, but I think that the "application does not respond" window does not suggest a normal workflow.
Canceling the shutdown is not desired I guess, because some of the other programs have been shut down already.
What seems to be the accepted way is to display the save as dialog regardless.
Cancelling the shutdown, then resuming it later is most certainly not an option, for the reason you state and various others.
Since simply discarding the data is unacceptable, there really is no other options.
Well, except to save the data to a temporary file, then automatically restoring them the next time the program is run. Rather like MS Word after it has crashed. Actually, the more I consider it, the better it sounds.
Edit: There's yet another avenue, namely to save continously, the way eg. MS OneNote does. What has struck me before is that, provided you implement decent multilevel undo in your application, the whole manual saving business is actually somewhat dated - an anachronism from the days when disk operations were expensive and error-prone, nowadays mostly old habit.
But I'm digressing. Anyway, it's probably not applicable to your application, since I imagine it needs to be implemented from the beginning.

Categories

Resources