How to implement filesystem watcher? - c#

I am having a situation like:
Suppose there is one folder "D:\Main" which can contain any word file. On adding any file/files to the folder I need to parse the file and based on some keywords I need to transfer the file to some other sub folders as well as some entry to the database also.
I know something about FileSystemWatcher. If there would have been a button like "Check and Move" then I know that on the button click event I can do something but how to do it automatically. I mean if I add file to the folder manually also it should do the same(files may be uploaded via web as well as manually).I am currently working in web application with asp.net and c# and I have a little knowledge about windows application. Can any one suggest me how to proceed with this? Thanks.

You will need to create either a windows app or a windows service app. The FileSystemWatch won't survive on a web application. The reason is because web application functions by following the steps:
the webserver thread loads the application
your application starts the watcher and then finally output the web response.
After some idle time, the thread terminates the application, and thus your watcher.
When you have your windows app, you can add the CanRaiseEvents = true and w.Created += new System.IO.FileSystemEventHandler(w_Created); to your watcher. Do your processing in
void w_Created(object sender, System.IO.FileSystemEventArgs e)
{
// here
}

I just happened to be researching something very similar.
But you answered your own question. FileSystemWatcher is likely what you want.
Lower level Win32 APIs information is here:
Obtaining Directory Change Notifications
and here:
FindFirstChangeNotification

Related

Where to define scripts that deletes files older then 7 days

I need to write script that delete files older than 7 days in MVC .Net application. I'm going to use this code
List<string> DeletePath = new List<string>();
DirectoryInfo info = new DirectoryInfo(Server.MapPath("~\\TempFiles"));
FileInfo[] files = info.GetFiles().OrderBy(p => p.CreationTime).ToArray();
foreach (FileInfo file in files)
{
DateTime CreationTime = file.CreationTime;
double days = (DateTime.Now - CreationTime).TotalDays;
if (days > 7)
{
string delFullPath = file.DirectoryName + "\\" + file.Name;
DeletePath.Add(delFullPath);
}
}
foreach (var f in DeletePath)
{
if (File.Exists(F))
{
File.Delete(F);
}
}
But i don't know where to define this and how to call. Do i need to create new Controller or something similar?
wrap your code in a static method and use hangfire
http://docs.hangfire.io/en/latest/background-methods/performing-recurrent-tasks.html
RecurringJob.AddOrUpdate(() => myCleanup.CleanupOldFiles(), Cron.Hourly)
Well you need to create a window service which will run in background on the server. Window services are the normal application which run automatically without any user event. Create a window service, then you can write the same code there wrapped in a timer tick event. There you can set the time when you want to execute this code.
Usually window service once deployed start executing your code every second. So you need to set the clock. With the Timer, you can execute your code as per your requirement, everyday at particular time, once in a week, once in a month or so on so forth.
Read more about window services here...
and let me know if you need code for this.
Mvc is not the job for this. It's like asking how to install tires on a train so that it can drive up a private street.
In order for Mvc or any web application for that matter to do something, a request must come in from a client. If no request comes in, IIS does nothing and just "listens" for incoming connections. To do this the "correct" way, you have two options:
Create a batch file or Powershell script or even a C# console application that deletes the files, then setup that batch file or script or program to run in Windows Task Scheduler.
If you need more logic to determine when the files should be deleted or you need customized schedules, then you should make a Windows Service Application. You can make this in C# and you can pretty much have it do anything you want, so long as it doesn't need a GUI. If you do need a GUI, perhaps to configure the service, then you can make a separate WinForms or WPF application that can configure the service. There are several ways to implement GUI/Service communication. Some of the more popular ones are WCF communication, database configuration or even INI files.
Hope this helps!

Issue with windows service waiting for a named event, using EventWaitHandle.

I'm currently developing a windows service with c# and .net framework 4.5 to extend the functionality of an existing propietary application, this service blocks on an EventWaitHandleClass (msdn link) waiting for a named event signaled from the main application. Something like this:
bool boolWithFalse = false;
string eName = "notification_event";
string usr = Environment.UserDomainName + "\\" + Environment.UserName;
EventWaitHandleSecurity security = new EventWaitHandleSecurity(); //*
EventWaitHandleAccessRule rule = new EventWaitHandleAccessRule(usr, EventWaitHandleRights.Synchronize, AccessControlType.Allow);
security.AddAccessRule(rule);
EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset, eName, out boolWithFalse, security);
//... some non relevant code lines here
//This is where the it locks waiting for the named event
var w = EventWaitHandle.WaitAny(new[] { handle }, Timeout.Infinite);
*: EventWaitHandleSecurity MSDN
Now this works like a charm if i execute my program as a console application, i can easily catch events from the main application and handle them as i intend, BUT, when i install the application as a service it locks waiting for this same named event but never receive the signal. The service is set to run using the NT AUTHORITY\LOCALSERVICE account, i've already tried using my own account (wich the progam uses while running as a console application) and yet nothing happens.
If it helps the application that originates the signal is running under my own account. I appreciate any help as i'm a complete beginner developing desktop applications for windows.
You got lost in the security hoopla. Two problems. The first one is that somebody has to create the event and somebody else has to open it so both components share the same event object. One of them has to use the EventWaitHandle constructor, the other has to call the EventWaitHandle.OpenExisting() method. The normal way is for the service to create the event and for the UI program to open it.
Next problem is the event object visibility. Windows implements namespaces for named operating system objects, pretty similar to how you use namespaces in the C# language. The root namespace is named by the session. And a service runs in a different session than the user's desktop programs. To get the desktop session program to see the event created in the service session, you have to use a "global" event name. Which looks like this:
string eName = "Global\\notification_event";
Do be careful of how you name your globally visible named event. There's another programmer somewhere someday that thinks that "notification_event" is a good choice for a name. You don't want to meet him. A {guid} is a good name, you get one from Tools + Create GUID. It is unique in the known universe, possibly beyond.

Use external xml file after publishing the c# desktop application

I have developed a c# desktop application that needs to be hosted on a server and will be scheduled to fetch data based on queries stored in XML files. while developing, I was using the following code to read XML files:
var query = new XPathDocument(#"C:\\Documents and Settings\\XYZ\\Desktop\\productplanningquery.xml");
as you can see I had put the XML file conveniently on my desktop and it worked fine while development. What I want to do now, is to give at a path such that where ever I host the application plus the XML files, it does not throw an exception. One way i thought could be to have a folder in a directory where the application will be installed but for that i will have to figure out the path to current directory dynamically (which i could not figure out).
Please help.
You could pass the location of you XML using args this way you're code would look like so:
var query = new XPathDocument(args[0])
You can also use relative path. Make sure that when deploying your code you keep the location of the file in the same relative location. For example if you place the XML in the same directory as the application
var query = new XPathDocument("productplanningquery.xml")
I am not sure what you want to achieve whether you want to read xml using winform app and do some operation and then pass it web app or some thing else. But here is my understandings:
Case 1: If you need to create XML outside the IIS and that XML will be consumed by ASP.Net app, then :
For using a desktop application with IIS server , you need to have full administrative access to the Live Machine. If its not then you should consider building windows services to operate on the XML files or any task that will run behind the scenes to decrease the load of the asp.net app.
Still in this case if you dont own a server then you need some Virtual Private Hosting or similar kind of hosting where you have almost all previleges to access the system. Then deploy the Windows Service, set the output path in such a manner so that it can be accessed by asp.net app too. And do whatever you want.
Case 2: If you want o read XML in ASP.Net Solely, then
In this case you case read it easily by using XDocument.But note, XML should in the same application directory or under the reach of the ASP.Net app
MSDN Article for Web-Windows Services with ASP.Net
You can use something like this:
var path = string.Format("{0}\\{1}", Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "productplanningquery.xml");
var pathForCurrentApp = string.Format("{0}\\{1}", Environment.CurrentDirectory, "productplanningquery.xml");

DLL doesn't store string C#

I have an application that can be launched from the explorer context menu for a Windows drive. When you click the menu item the drive letter is passed to the new instance. I want to make sure that any old instance is closed.
I also want to make sure that the last drive that was selected is persisted, so that when I start the application again from the start-menu, it will remember the drive I originally selected.
It would be best if the already running application would receive an event so that it can update without having to kill and restart.
I tried the following, but that doesn't seem to be working:
This is my Class library method(it is just a line that define a variable so just i have a DLL that there a variable in it and no more)
namespace Dispatch
{
public class cls_get_drive_letter
{
public static string drive_letter;
}
}
This is my loading form code: (Here i will fill the DLL's variable)
private void Frm_loading_Load(object sender, EventArgs e)
{
Dispatch.cls_get_drive_letter.drive_letter = "XXX";
Process currentProcess = Process.GetCurrentProcess();
if (Process.GetProcessesByName(currentProcess.ProcessName, currentProcess.MachineName).Length >1)
{
currentProcess.Kill();
}
}
So when i run this for first time the "XXX" will be stored in DLL but when the current instance of application is running and i am going to run next instance of it the application will be closed because of this code:
Process currentProcess = Process.GetCurrentProcess();
if (Process.GetProcessesByName(currentProcess.ProcessName, currentProcess.MachineName).Length >1)
{
currentProcess.Kill();
}
So when closing code occurs with this code the new "XXX" will not stored in DLL and the last string will be in dll.
All variables, in this case a static field, will only remain the same for the running instance of your application.
When the application is started again the field is empty
A static variable is not stored 'inside a dll' but is stored inside the memory of the application that loads the dll. When a second instance of the application starts, it will have its own memory space and it will have its own version of the string variable. The variable is also not kept between instances, so as soon as you start a new instance it will have its own, empty string variable.
When the application is stopped, the memory for that instance is released and the variable is 'forgotten'.
If you want to share state between applications, there are all kinds of solutions, one could be the System.Configuration.Settings API, a file somewhere, a memory mapped file shared between multiple processes, a Named Pipe, a Kernel Semaphore. Options aplenty.
Until we understand exactly what it is you're trying to accomplish with this shared state, we can't provide you with a better alternative than the explanation that what you're doing right now, will not work do to the way static variables work.
Update based on new information:
You can store your currently selected drive in a Settings file for your project. You can add such file from the project properties in Visual Studio. There's a tab called settings. Create a new setting for "Selected Drive" and make it a User setting (that way you can update it without Admin rights).
To communicate a new drive letter to your already running application, you have a number of options.
For one, you could check whether your executable is already running (like you're doing now) and in that case update the settings file and exit the new instance. Your already running instance could periodically refresh the settings to pick up new values.
When your application starts, you can open a named pipe on your machine on which you listen for drive changes. When the 2nd instance starts, it can detect that the pipe is already there, write the new drive to the pipe and close. The already running application can pick up this message and change its configuration.
You can send a WindowMessage to the other application
You can host a simple WCF service to receive the notification
You can write the new drive letter to a file stored in a known location and have the other instance use a FileSystemWatcher to detect the changes to that file.
As I said the possibilities are endless.
If I were you I'd first make sure that the value is persisted between relaunches by implementing the Settings file in your application. Then investigate the options I described above, do some experimentation and then ask new questions when you cannot figure out how to make it work.

How to update your exe from remote host - c# 4.0 - wpf application

I want to update my exe from remote server. So when the button clicked on my wpf application it will download the remote and also a remote txt file and replace the current ones in the same folder that exe running. So it will overwrite the current txt and and exe file while my exe is running. How can i achive this ?
Remote host is url like www.mydomain.com/MyAPP.exe
wpf application , c# 4.0
The way that we resolved this issue was to create a shell exe that as the one that was installed and deployed initially to the client machines.
The "real" executable program is stored in a subdirectory of this initial app. When the shell app is launched, after it has downloaded and installed any updates for the real app, it launches the real app's executable in a separate AppDomain.
Here is the core of the "real" app launching from within the shell app:
System.AppDomainSetup oSetup = new System.AppDomainSetup();
string sApplicationFile = null;
// Use this to ensure that if the application is running when the user performs the update, that we don't run into file locking issues.
oSetup.ShadowCopyFiles = "true";
oSetup.ApplicationName = sAppName;
// Generate the name of the DLL we are going to launch
sApplicationFile = System.IO.Path.Combine(sApplicationDirectory, sAppName + ".exe");
oSetup.ApplicationBase = sApplicationDirectory;
oSetup.ConfigurationFile = sApplicationFile + ".config";
oSetup.LoaderOptimization = LoaderOptimization.MultiDomain;
// Launch the application
System.AppDomain oAppDomain = AppDomain.CreateDomain(sAppName, AppDomain.CurrentDomain.Evidence, oSetup);
oAppDomain.SetData("App", sAppName);
oAppDomain.SetData("User", sUserName);
oAppDomain.SetData("Pwd", sUserPassword);
oAppDomain.ExecuteAssembly(sApplicationFile);
// When the launched application closes, close this application as well
Application.Exit();
Note that in our version, the shell app collects the user name and password from the user in order to access the update web site correctly. This data is then passed to the "real" app through the SetData method on the AppDomain.
The solution depends on your particular case. But there's no straight solution, because you can't update assemblies while they are loaded into memory and being used. I can propose 2 solutions: using shadow copying and using some sort of helper executable. I've used both of them.
Shadow copying.
The obvious way is to make your main executable to be shadow copied, replace it while your app is running and then restart the app. But you can't make your default app domain to be shadow copied, only secondary app domains can be. But you still can move all your code into another assembly (say, MainAppLib.dll) and rewrite your main app executable (MainApp.exe) so that it contains only "loader code". This loader code has to create another app domain, set it to be shadow copied and then run your program logic in the secondary app domain. Beware not to have any direct references from your main app domain into MainAppLib.dll because then this assembly will be loaded into your main app domain which is not shadow copied and the assembly file will get locked. In most cases you can go with AppDomain.ExecuteAssembly() methods.
Helper executable
The idea is to use some sort of update finisher. Your main app remains unchanged, you only add a little amount of code into it, so that your app will download update, put it into temporary folder, and then your main app starts update finisher (in separate process) and exits. Update finisher waits till your app closes and then copies new files from temporary folder into your app folder replacing all files. Update finisher can't replace it's own executable but it can be done by main application before it starts the update finisher. After copying files update finisher runs your application.
p.s. Personally I prefer the former solution because it involves some sort of voodoo magic using app domains, reflection, assemblies e.t.c. And it can be evolved into using plugins if you need (e.g. via MEF framework). But the latter is easier to understand especially if you have never worked with app domains and manual assemblies loading, it's quite straightforward.
You could probably use ClickOnce (based on your comment above that you would be prepared to have another assembly get the exe....as the other poster mentioned you can't replace a running assembly). You can configure it to check at various times (e.g. on startup) for new versions and it automatically downloads them. Its a very robust solution and you can do a lot with the deployment assemblies.

Categories

Resources