I am trying to monitor a log file for changes. My code is working, and does everything it should. However, as I want this to run as a windows service and be constantly monitoring I'm not sure of the proper way to set it into a waiting state. Here is what it's doing at the moment.
public static void Main()
{
log_watcher = new FileSystemWatcher();
log_watcher.Path = Path.GetDirectoryName(pathToFile);
log_watcher.Filter = recent_file.Name;
log_watcher.NotifyFilter = NotifyFilters.LastWrite;
log_watcher.Changed += new FileSystemEventHandler(OnChanged);
log_watcher.EnableRaisingEvents = true;
//do rest of stuff OnChanged
while (true)
{
}
}
And then just a simple:
public static void OnChanged(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File has changed");
}
What would be a better way in a windows service to do this?
You can start a message pump using Application.Run() from WinForms.
using System.Windows.Forms;
// The class that handles the creation of the application windows
class MyApplicationContext : ApplicationContext {
private MyApplicationContext() {
// Handle the ApplicationExit event to know when the application is exiting.
Application.ApplicationExit += new EventHandler(this.OnApplicationExit);
log_watcher = new FileSystemWatcher();
log_watcher.Path = Path.GetDirectoryName(pathToFile);
log_watcher.Filter = recent_file.Name;
log_watcher.NotifyFilter = NotifyFilters.LastWrite;
log_watcher.Changed += new FileSystemEventHandler(OnChanged);
log_watcher.EnableRaisingEvents = true;
}
public static void OnChanged(object sender, FileSystemEventArgs e) {
Console.WriteLine("File has changed");
}
private void OnApplicationExit(object sender, EventArgs e) {
Console.WriteLine("File monitor exited.");
}
[STAThread]
static void Main(string[] args) {
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context. It will exit when
// all forms are closed.
Application.Run(context);
}
}
See Run(ApplicationContext) on learn.microsoft.com.
Related
I am writing a program that has a FileSystemWatcher. I also have two other methods that I want to run alongside the FSW. But my other methods don't get executed because the program is always at the FSW.
Essentially, I want to have the FileSystemWatcher keep going and be able to perform other actions in my program at the same time.
How can I structure my code to achieve this?
Currently, my code has this structure:
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
// call the FSW
MyFileSystemWatcher(path);
// call another method
AnotherMethod1();
// call another method
AnotherMethod2();
}
//----- file system watcher methods -----//
private static void MyFileSystemWatcher(string path)
{
// code for the file system watcher
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
fileSystemWatcher.Path = path;
fileSystemWatcher.Created += FileSystemWatcher_Created;
fileSystemWatcher.Renamed += FileSystemWatcher_Renamed;
fileSystemWatcher.Deleted += FileSystemWatcher_Deleted;
fileSystemWatcher.EnableRaisingEvents = true;
}
private static void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File created: {0}", e.Name);
}
private static void FileSystemWatcher_Renamed(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File renamed: {0}", e.Name);
}
private static void FileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine("File deleted: {0}", e.Name);
}
//----- end of file system watcher methods -----//
//----- other methods in the program -----//
public static void AnotherMethod1()
{
// code for Method1
}
private static void AnotherMethod2()
{
// code for Method2
}
}
}
Thank you.
Make your method async
private static async Task MyFileSystemWatcher(string path)
{
// code for the file system watcher
}
then
static void Main(string[] args)
{
// call the File System Watcher
var task = MyFileSystemWatcher(path);
// call another method
AnotherMethod1();
// call another method
AnotherMethod2();
}
Alternatively, If you dont want to touch your method (not preferable), then
var task = Task.Run(() => MyFileSystemWatcher(path));
I create a FileSystemWatcher on a separate thread to monitor a directory for changes. None of my events fire when I add a new file or copy a new file into the directory that I am trying to monitor. I've used the FileSystemWatcher class successfully in Windows Forms apps, so I am guessing I am missing something simple.
public partial class MainWindow : Window
{
System.IO.FileSystemWatcher watcher;
public MainWindow()
{
InitializeComponent();
System.Threading.Thread t1 = new System.Threading.Thread(MonitorDir);
t1.IsBackground = true;
t1.Start();
}
private void MonitorDir()
{
watcher = new System.IO.FileSystemWatcher("C:\\Temp","*.*");
watcher.Created += Watcher_Created;
watcher.Disposed += Watcher_Disposed;
watcher.Error += Watcher_Error;
watcher.Changed += Watcher_Changed;
while (true)
{
}
}
private void Watcher_Changed(object sender, System.IO.FileSystemEventArgs e)
{
throw new NotImplementedException();
}
private void Watcher_Error(object sender, System.IO.ErrorEventArgs e)
{
throw new NotImplementedException();
}
private void Watcher_Disposed(object sender, EventArgs e)
{
throw new NotImplementedException();
}
private void Watcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
throw new NotImplementedException();
}
}
You need to set its EnableRaisingEvents property to true (it's false by default), otherwise it won't raise any events.
watcher.EnableRaisingEvents = true;
I created a Service in C#. I needed a GUI for configuration of the Service so I added a WinForms project to my solution. My plans was to create the Form in the Service and show it in the OnStart() Method of the Service. However, it won't show. The WriteEntry() Methods of the EventLog are all firing, so my code definitely is processed. Anybody know what I'm doing wrong here?
public partial class UrlWatcherService : ServiceBase
{
private UrlWatcherForm _urlwatcherform;
private EventLog _eventLog;
private string _eventLogName = "UrlWatcherEventLog";
private string _eventLogSource = "UrlWatcherSource";
public UrlWatcherService()
{
InitializeComponent();
LoadVariables();
}
public void OnDebug()
{
OnStart(null);
}
private void LoadVariables()
{
_urlwatcherform = new UrlWatcherForm();
_eventLog = new EventLog();
CanPauseAndContinue = true;
if (!EventLog.SourceExists(_eventLogSource))
EventLog.CreateEventSource(_eventLogSource, _eventLogName);
_eventLog.Source = _eventLogSource;
_eventLog.Log = _eventLogName;
_eventLog.WriteEntry("Url Watcher Log Created", EventLogEntryType.Information);
}
protected override void OnStart(string[] args)
{
_eventLog.WriteEntry("Url Watcher Service Started", EventLogEntryType.Information);
_urlwatcherform.Show();
_eventLog.WriteEntry("Url Watcher Form Created", EventLogEntryType.Information);
}
protected override void OnPause()
{
base.OnPause();
_eventLog.WriteEntry("Url Watcher Service Paused", EventLogEntryType.Information);
}
protected override void OnContinue()
{
base.OnContinue();
_eventLog.WriteEntry("Url Watcher Log Continued", EventLogEntryType.Information);
}
protected override void OnStop()
{
_eventLog.WriteEntry("Url Watcher Service Stopped", EventLogEntryType.Information);
}
}
public partial class UrlWatcherForm : Form
{
public UrlWatcherForm()
{
InitializeComponent();
}
private void btnAdd_Click(object sender, EventArgs e)
{
}
private void UrlWatcherGui_Resize(object sender, EventArgs e)
{
if (FormWindowState.Minimized == WindowState)
Hide();
}
private void UrlWatcherGui_FormClosing(object sender, FormClosingEventArgs e)
{
Hide();
e.Cancel = true;
}
private void urlWatcherNofiyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
}
}
EDIT: To clarify, if I debug it like below, the Form shows. I can put the thread to sleep but that won't let me interact with the Form anymore. But the Form definitely shows, it's just in an unresponsive state.
static void Main()
{
#if DEBUG
UrlWatcherService service = new UrlWatcherService();
service.OnDebug();
#else
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new UrlWatcherService()
};
ServiceBase.Run(ServicesToRun);
#endif
}
OK due to Steve's hint, I split the projects up. I use a merged module as per this MSDN article, and instead of referencing the GUI in the Service project, I separated them so I can put both their project outputs in the merged module. I then add the merged module to my installer and now I have the service running after install and the ability to call the form from my start menu. It's not what I originally wanted, but a very plausible alternative.
Thanks for Steve for the hint.
I am learning some WPF and have written this little program that reads an Excel file for data and updates the UI on save. Only after the first save does my ResetTimer() function work. But the GetDisplayData() does load the data and the program will update data on save. Only they timer does not start until that first save..
But I want the timer to start right away in case there is not a save event on the Excel file at load.
What can I do to get it to work, seems like whenever I try and place it in window_loaded or other places I tried, my program loops or does not load the data.
Thank you for your help.
using System;
using System.Data;
using System.IO;
using System.Windows;
using System.Windows.Threading;
namespace WPFReadExcel
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private const string ExcelPath = #"C:\";
private const string ExcelPathFile = #"C:\DataSource.xlsx";
DataTable _dashBoardData = new DataTable();
public MainWindow()
{
InitializeComponent();
}
protected void Window_Loaded(object sender, RoutedEventArgs e)
{
GetDisplayData();
StartFileSystemWatcher();
}
public void GetDisplayData()
{
var excelData = new ExcelData();
_dashBoardData = excelData.ReadExcelFile("Live", ExcelPathFile);
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
ExcelDataGrid.ItemsSource = _dashBoardData.AsDataView();
RefreshDateTime.Content = "Refresh at: " +
DateTime.Now.ToShortTimeString();
}
));
}
private void ResetDisplayData()
{
if (_dashBoardData != null) _dashBoardData.Dispose();
GetDisplayData();
ResetTimer();
}
private void ResetTimer()
{
while (true)
{
System.Threading.Thread.Sleep(20000);
ResetDisplayData();
}
}
private void StartFileSystemWatcher()
{
if (string.IsNullOrWhiteSpace(ExcelPath))
return;
FileSystemWatcher watcher = new FileSystemWatcher();
// set directory to watch
watcher.Path = ExcelPath;
// set what to watch for
watcher.NotifyFilter = NotifyFilters.LastWrite;
// set event handlers
watcher.Changed += new FileSystemEventHandler(watcher_Changed);
// start watching
watcher.EnableRaisingEvents = true;
}
private void watcher_Changed(object sender, FileSystemEventArgs e)
{
ResetDisplayData();
}
private void Label_Loaded(object sender, RoutedEventArgs e)
{
RefreshDateTime.Content = "Refresh at: " + DateTime.Now.ToShortTimeString();
}
}
}
The Window.Loaded event is the correct place to do what you want:
protected void Window_Loaded(object sender, RoutedEventArgs e)
{
ResetTimer();
GetDisplayData();
StartFileSystemWatcher();
}
However, it appears that you aren't using a Timer anywhere, so your question and your method name are inappropriate. In WPF, we use the DispatcherTimer Class. First, you'd need to initialise it and then start it:
private DispatcherTimer timer = new DispatcherTimer();
...
private void ResetTimer()
{
timer.Interval = TimeSpan.FromSeconds(20);
timer.Tick += Timer_Tick;
timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
ResetDisplayData();
}
For your information, you really couldn't write much worse code than this, as it will block your UI and make your application non responsive:
while (true)
{
System.Threading.Thread.Sleep(20000);
ResetDisplayData();
}
I have a console application in C#. If something goes wrong, I call Environment.Exit() to close my application. I need to disconnect from the server and close some files before the application ends.
In Java, I can implement a shutdown hook and register it via Runtime.getRuntime().addShutdownHook(). How can I achieve the same in C#?
You can attach an event handler to the current application domain's ProcessExit event:
using System;
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += (s, e) => Console.WriteLine("Process exiting");
Environment.Exit(0);
}
}
Hook AppDomain events:
private static void Main(string[] args)
{
var domain = AppDomain.CurrentDomain;
domain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
domain.ProcessExit += new EventHandler(domain_ProcessExit);
domain.DomainUnload += new EventHandler(domain_DomainUnload);
}
static void MyHandler(object sender, UnhandledExceptionEventArgs args)
{
Exception e = (Exception)args.ExceptionObject;
Console.WriteLine("MyHandler caught: " + e.Message);
}
static void domain_ProcessExit(object sender, EventArgs e)
{
}
static void domain_DomainUnload(object sender, EventArgs e)
{
}
I'd recommend wrapping the call to Environment.Exit() in your own method and using that throughout. Something like this:
internal static void MyExit(int exitCode){
// disconnect from network streams
// ensure file connections are disposed
// etc.
Environment.Exit(exitCode);
}