I am making a Windows service in Visual Studio using C#. When I run the program from the command line it works as expected. No exceptions get thrown or anything like that, and the event log get's written to like normal. Here is my entry method.
var service = new CSFolderWatcher();
if (Environment.UserInteractive)
{
service.CallStart(args);
Console.WriteLine("Press enter to stop program");
Console.Read();
service.CallStop();
}
else
{
ServiceBase.Run(new ServiceBase[] { new CSFolderWatcher() });
}
However, when I go into the SCM to start the service, a box immediately pops up that says "The CS Folder Watcher service on Local Computer started and then stopped. Some services stop automatically if they are not in use by other services or programs." Nothing gets written to the event log at all. Here is my onStart code:
internal void CallStart(string[] args) { OnStart(args); }
internal void CallStop() { OnStop(); }
protected override void OnStart(string[] args)
{
this.ServiceName = MyServiceName;
Properties.Settings.Default.Reload();
this.destfolder = Properties.Settings.Default.DestinationFolder;
this.watchfolder = Properties.Settings.Default.WatchFolder;
this.watchfilter = Properties.Settings.Default.WatchFilter;
LogEvent(this.ServiceName + " starting" + "\r\n" +
"Destination folder: " + this.destfolder + "\r\n" +
"Watch Folder: " + this.watchfolder + "\r\n" +
"Watch Filter: " + this.watchfilter + "\r\n" +
"OnStart args: " + string.Join(", ", args));
// Create a new FileSystemWatcher with the path
//and text file filter
try { watcher = new FileSystemWatcher(watchfolder, watchfilter); }
catch (Exception e) { LogEvent(e.ToString()); throw; }
watcher.IncludeSubdirectories = Properties.Settings.Default.WatchSubdirectories;
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
watcher.EnableRaisingEvents = true;
}
Here's the code for LogEvent:
private void LogEvent(string message)
{
string eventSource = MyServiceName;
DateTime dt = new DateTime();
dt = System.DateTime.UtcNow;
message = dt.ToLocalTime() + ": " + message;
Console.WriteLine(message);
EventLog.WriteEntry(eventSource, message);
}
The problem turns out to be that you can't set the ServiceName property from the OnStart method. this.ServiceName = MyServiceName; should be in the constructor instead, as it seems like it is necessary to set it.
The ServiceName identifies the service to the Service Control Manager. The value of this property must be identical to the name recorded for the service in the ServiceInstaller.ServiceName property of the corresponding installer class. In code, the ServiceName of the service is usually set in the main() function of the executable.
--MSDN Reference
Related
I have created a Windows Service which uses a FileSystemWatcher to look for changes in different directories. When I launch the service I am getting the error:
Error 1053:The service did not respond to start or control request in timely fashion.
I think that the error is coming from an infinite loop caused by using the using statement in the Watch() method as shown below:
public FileSystemWatcher Watch()
{
FileSystemWatcher watcher;
using (watcher = new FileSystemWatcher($"C:\\Users\\lashi\\AppData\\Roaming\\Sublime Text 3", _ext))
{
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
watcher.IncludeSubdirectories = true;
// Add event handlers.
watcher.Changed += OnChanged;
watcher.Created += OnChanged;
watcher.Deleted += OnChanged;
watcher.Renamed += OnRenamed;
// Begin watching.
watcher.EnableRaisingEvents = true;
}
return watcher;
}
This is my OnStart() method:
protected override void OnStart(string[] args)
{
String userName;
String expt;
if (args.Length < 2)
{
Console.WriteLine($"FileWatcher <user> <exptName>");
Console.WriteLine($"Captures files into /temp/<exptName>-log and /temp/<exptName>-files");
userName = "wost";
expt = "expt1";
}
else
{
userName = args[0];
expt = args[1];
}
String lexpt = $"C:\\Users\\lashi\\Desktop\\EMMC_CACHE\\{expt}-log";
String fexpt = $"C:\\Users\\lashi\\Desktop\\EMMC_CACHE\\{expt}-file";
if (!Directory.Exists(fexpt))
{
Directory.CreateDirectory(fexpt);
}
if (!Directory.Exists(lexpt))
{
Directory.CreateDirectory(lexpt);
}
// File Watcher Launch
Watcher w = new Watcher(lexpt, fexpt, userName);
FileSystemWatcher fw = w.Watch();
}
Can you please help me to find a solution to this issue? I have tried a lot of suggestions but they don't seem to work. Thank you!
Click here! to see how to increase Windows services pipe timeout by editing the registry keys
I have a multi-threaded program (3-4 threads). All the threads depend on a couple of parameters which are specified in an XML file.
Since the parameters in the XML file may be changed at any time by a user therefore, the different threads need to be notified about it and need to get the updated copy of parameters.
To monitor the changes in the XML file, I am using a FileWatcher as per the MSDN documentation.
clas ReadXML
{
//parameters
private static string Param1 = "";
private static string Param2 = "";
public static void ReadXmlParameters()
{
XmlDocument xDoc = new XmlDocument();
try
{
xDoc.Load(_ParameterFileDirrectory + #"\" + _ParameterFileDirrectory);
//parameters
Param1 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/IpAddress")).InnerText;
Param2 = (xDoc.DocumentElement.SelectSingleNode("/Parameters/SetOne/Username")).InnerText;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void CreateXMLWatcher()
{
try
{
// Create a new FileSystemWatcher and set its properties.
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = _ParameterFileDirrectory;
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
// Only watch .xml files.
watcher.Filter = _ParameterFileFilename; // "ParameterFile.xml";
// Add event handlers.
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
// Begin watching.
watcher.EnableRaisingEvents = true;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// Define the event handlers.
private static void OnChanged(object source, FileSystemEventArgs e)
{
// Specify what is done when a file is "Changed", "Created", or "Deleted".
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
if (e.ChangeType.ToString() == "Changed")
{
ReadXmlParameters(); //Read the Parameters from XML again
MyThreadClass1._waitTillParametersChange.Set(); //Notifying the thread that the parameters might have chnaged
}
}
}
The above implementation is working fine for me. I have to start the FileWatcher from the Main() using the following lines:
public static void Main()
{
ReadXml.ReadXmlParameters();
ReadXml.CreateXMLWatcher();
// Start other threads now
}
and then I start my other threads.
QUESTION: Since with the above-mentioned implementation, I have got Static methods and variables in my program so, I am wondering if this is the proper (at least acceptable) implementation of a FileWatcher or should I try to get rid of these static things by implementing ReadXml as a singleton class (or providing the same object to all the thread classes).
Conext
I have a scenario in which i have to check the specific directory after every 30 seconds for new files. If there is any new file then i have to process the files but can be multiple in a batch.
Questions
Should i use FileSystemWatcher or Read the directory and process the files in parallel ?
I am using Windows service which will process CSV files and output will be shown to windows form application.
Can we schedule the FileSystemWatcher on Timer ?
What will be the best approach in this case ?
If i choose the Directory read instead of FileSystemWatcher how to process the batch of 100 files in parallel and send to other application ?
Thanks
I was also trying and found this article interesting.
https://connectvishal.wordpress.com/2015/11/05/filesystemwatcher-and-queues-with-parallel-execution/
protected override void OnStart(string[] args)
{
current_directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
try
{
strDir = ConfigurationManager.AppSettings["Directory"];
fileMask = ConfigurationManager.AppSettings["FileMask"];
strBatfile = ConfigurationManager.AppSettings["Batch"];
strlog = ConfigurationManager.AppSettings["Log"];
Task.Factory.StartNew(QueueHandler);
var fsw = new FileSystemWatcher();
fsw.Created += (o, e) =>
{
// add a file to the queue
filenames.Enqueue(e.FullPath);
};
fsw.Path = strDir + "\\";
fsw.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
fsw.Filter = fileMask;
fsw.EnableRaisingEvents = true;
fsw.Deleted += new FileSystemEventHandler(OnDeleated);
fsw.Renamed += new RenamedEventHandler(OnRenamed);
fsw.EnableRaisingEvents = true;
}
catch (Exception exception)
{
CustomException.Write(CustomException.CreateExceptionString(exception.ToString()));
}
}
and a queue handler :
static void QueueHandler()
{
bool run = true;
AppDomain.CurrentDomain.DomainUnload += (s, e) =>
{
run = false;
filenames.Enqueue("stop");
};
try
{
while (run)
{
string filename;
if (filenames.TryDequeue(out filename) && run)
{
var proc = new Process();
proc.StartInfo.FileName = Service1.strBatfile; //here .exe can be added
proc.Start();
;
Log.getLogger("File Processed after executing batch\.exe: Filename - :" + filename + " " + "Batch File Executed- > " + Service1.strBatfile + " at timestamp : " + DateTime.Now.ToString(), Service1.strlog);
proc.WaitForExit(); // this blocks until the process ends....
}
}
}
catch (Exception exception)
{
CustomException.Write(CustomException.CreateExceptionString(exception.ToString()));
}
I wrote a windows service. Part of the code works : (example from msdn)
protected override void OnStart(string[] args)
{
file = new StreamWriter(new FileStream("MyFirstService.log",
System.IO.FileMode.Append));
this.file.WriteLine("MyFirstService стартовал");
this.file.Flush();
Thread thread = new Thread(new ThreadStart(WatchProcess));
thread.Start();
}
I am adding this code :
private void WatchProcess()
{
var key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Run\", true);
key.SetValue("dWatch", System.Reflection.Assembly.GetEntryAssembly().Location);
System.IO.FileSystemWatcher fWatcher;
fWatcher = new System.IO.FileSystemWatcher();
fWatcher.IncludeSubdirectories = true;
fWatcher.Path = #"C:\Windows";
fWatcher.Filter = "*.*";
fWatcher.NotifyFilter = System.IO.NotifyFilters.LastAccess | System.IO.NotifyFilters.LastWrite | System.IO.NotifyFilters.FileName | System.IO.NotifyFilters.DirectoryName;
fWatcher.Changed += new FileSystemEventHandler(OnChanged);
fWatcher.Created += new FileSystemEventHandler(OnChanged);
fWatcher.Deleted += new FileSystemEventHandler(OnChanged);
fWatcher.Renamed += new RenamedEventHandler(OnRenamed);
fWatcher.EnableRaisingEvents = true;
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
WatcherChangeTypes wtc = e.ChangeType;
string str = System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile);
StreamWriter sw = new StreamWriter(str);
Console.WriteLine("File: " + e.FullPath + " " + wtc.ToString());
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
string str = System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile);
StreamWriter sw = new StreamWriter(str);
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}
The code above works when I run it in console application, but in windows service it does not work. And service automatically stops after 30 seconds.
protected override void OnStart(string[] args)
{
file = new StreamWriter("C:\\Public\\fswThread." + DateTime.Now.Millisecond.ToString() + ".txt");
System.IO.FileSystemWatcher watcher = new System.IO.FileSystemWatcher();
watcher.Path = #"C:\Windows";
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
watcher.Changed += new FileSystemEventHandler( OnChanged );
watcher.EnableRaisingEvents = true;
}
protected override void OnStop()
{
file.Close();
}
private void OnChanged(object sender, FileSystemEventArgs e)
{
file.WriteLine( "C: " + e.FullPath );
file.AutoFlush = true;
if (file.BaseStream.Length > 100)
{
file.Flush();
file.Close();
file = new StreamWriter("C:\\Public\\fswThread." + DateTime.Now.Millisecond.ToString() + ".txt");
}
}
Is there any way to register for an event that fires when an executable of a particular filename starts? I know it's easy enough to get an event when a process exits, by getting the process handle and registering for the exited event. But how can you be notified when a process, that isn't already running, starts...without polling all the running processes?
You could use the following:
private ManagementEventWatcher WatchForProcessStart(string processName)
{
string queryString =
"SELECT TargetInstance" +
" FROM __InstanceCreationEvent " +
"WITHIN 10 " +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + processName + "'";
// The dot in the scope means use the current machine
string scope = #"\\.\root\CIMV2";
// Create a watcher and listen for events
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
watcher.EventArrived += ProcessStarted;
watcher.Start();
return watcher;
}
private ManagementEventWatcher WatchForProcessEnd(string processName)
{
string queryString =
"SELECT TargetInstance" +
" FROM __InstanceDeletionEvent " +
"WITHIN 10 " +
" WHERE TargetInstance ISA 'Win32_Process' " +
" AND TargetInstance.Name = '" + processName + "'";
// The dot in the scope means use the current machine
string scope = #"\\.\root\CIMV2";
// Create a watcher and listen for events
ManagementEventWatcher watcher = new ManagementEventWatcher(scope, queryString);
watcher.EventArrived += ProcessEnded;
watcher.Start();
return watcher;
}
private void ProcessEnded(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject targetInstance = (ManagementBaseObject) e.NewEvent.Properties["TargetInstance"].Value;
string processName = targetInstance.Properties["Name"].Value.ToString();
Console.WriteLine(String.Format("{0} process ended", processName));
}
private void ProcessStarted(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject targetInstance = (ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;
string processName = targetInstance.Properties["Name"].Value.ToString();
Console.WriteLine(String.Format("{0} process started", processName));
}
You would then call either WatchForProcessStart and/or WatchForProcessEnd passing in your process name (eg "notepad.exe").
The ManagementEventWatcher object is returned from the two Watch* methods as it implements IDisposable and so you should call Dispose on these objects when you have finished with them to prevent issues.
You could also change the polling value in the queries if you need the event to be raised more quickly after the process has started. To do this change the line "WITHIN 10" to be WITHIN something less than 10.
WMI can create events when processes are created. You could then filter these events.
Here is code.
Notice that you have to start Visual Studio like Administrator in order to execute this code.
using System;
using System.Management;
namespace AppLaunchDetector
{
class Program
{
static void Main(string[] args)
{
ManagementEventWatcher w = null;
WqlEventQuery q;
try
{
q = new WqlEventQuery();
q.EventClassName = "Win32_ProcessStartTrace";
w = new ManagementEventWatcher(q);
w.EventArrived += new EventArrivedEventHandler(ProcessStartEventArrived);
w.Start();
Console.ReadLine(); // block main thread for test purposes
}
catch (Exception ex)
{
}
finally
{
w.Stop();
}
}
static void ProcessStartEventArrived(object sender, EventArrivedEventArgs e)
{
foreach (PropertyData pd in e.NewEvent.Properties)
{
Console.WriteLine("\n============================= =========");
Console.WriteLine("{0},{1},{2}", pd.Name, pd.Type, pd.Value);
}
}
}
}