source: 2014/tet/FileWatcher/FileWatcher/Program.cs @ 4833

Revision 4833, 22.5 KB checked in by anlakane, 9 years ago (diff)
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Security.Permissions;
6using System.IO;
7using System.Diagnostics;
8using System.Threading;
9using System.ComponentModel;
10using System.Runtime.Serialization;
11using System.Runtime.Serialization.Formatters.Binary;
12using System.Windows.Threading;
13
14
15namespace FileWatcher
16{
17    public static class Program
18    {
19        /// <summary>
20        /// Kansio, jota tarkkaillaan.
21        /// </summary>
22        public static String SelectedFolder { get; set; }
23
24        /// <summary>
25        /// Tiedostotyypit, joita tarkkaillaan.
26        /// </summary>
27        public static String[] SelectedFileExtensions { get; set; }
28
29        /// <summary>
30        /// Ohjelmat, jotka ajetaan tiedoston löytyessä.
31        /// </summary>
32        public static List<Executable> ExecutablePrograms;
33
34        private static List<String> jobs = new List<String>();
35        private static List<FileSystemWatcher> watchers;
36
37        public static bool WatchCreation { get; set; }
38        public static bool WatchDeletion { get; set; }
39        public static bool WatchChanges { get; set; }
40        public static bool WatchRenaming { get; set; }
41
42        /// <summary>
43        /// Tarkkaillaanko alikansioita.
44        /// </summary>
45        public static bool WatchSubdirectories { get; set; }
46
47        public static readonly String WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
48        public static readonly String OptionsFilePath = WorkingDirectory + "\\options.dat";
49        public static readonly String LogFilePath = WorkingDirectory + "\\log.txt";
50        public static StreamWriter LogWriter;
51
52        /// <summary>
53        /// Ladataanko edelliset asetukset automaattisesti ohjelman käynnistyessä.
54        /// </summary>
55        public static bool LoadSettingsAutomatically { get; set; }
56
57        /// <summary>
58        /// Tallenetaanko asetukset ohjelman sulkeutuessa.
59        /// </summary>
60        public static bool SaveSettingsAutomatically { get; set; }
61
62        /// <summary>
63        /// Aloitetaanko tarkkailu automaattisesti edellisillä asetuksilla.
64        /// </summary>
65        public static bool StartWatchingAutomatically { get; set; }
66
67        /// <summary>
68        /// Pidetäänkö lokia ohjelman tekemisistä.
69        /// </summary>
70        public static bool KeepLog { get; set; }
71
72        /// <summary>
73        /// Oletusasetukset.
74        /// </summary>
75        public static readonly OptionsFile DefaultSettings = new OptionsFile("", null, null, true, false, false, false, false, false, false, true);
76
77        public static void Initialize()
78        {
79            AddMessage("Initializing...");
80            LogWriter = new StreamWriter(LogFilePath, true);
81            OptionsFile ops = LoadSettings();
82            if (ops.LoadSettingsAutomatically)
83            {
84                UseSettings(ops);
85                AddMessage("Loaded previous settings.");
86            }
87            else UseSettings(ops, true);
88            if (ops.KeepLog)
89            {
90                AddMessage("Logging ON, keeping log in file: log.txt.");
91            }
92            else AddMessage("Logging OFF.");
93            if (StartWatchingAutomatically)
94            {
95                AddMessage("Starting to watch automatically...");
96                Start();
97                MainWindow.StartButton.IsEnabled = false;
98                MainWindow.StopButton.IsEnabled = true;
99                MainWindow.ResetButton.IsEnabled = false;
100                MainWindow.Directory.IsEnabled = false;
101                MainWindow.Extensions.IsEnabled = false;
102                MainWindow.SubfolderBox.IsEnabled = false;
103                MainWindow.CreationBox.IsEnabled = false;
104                MainWindow.DeletionBox.IsEnabled = false;
105                MainWindow.ChangeBox.IsEnabled = false;
106                MainWindow.RenameBox.IsEnabled = false;
107                MainWindow.LoadButton.IsEnabled = false;
108                MainWindow.DirectoryBrowseButton.IsEnabled = false;
109            }
110        }
111
112        /// <summary>
113        /// Aloitetaan tarkkailuprosessi.
114        /// </summary>
115        public static bool Start()
116        {
117            AddMessage("Verifying inputs...");
118
119            if (SelectedFolder == "" && SelectedFolder == MainWindow.DIRECTORY_DEFAULT_TEXT)
120            {
121                AddMessage("The folder path is empty!");
122                return false;
123            }
124
125            try
126            {
127                FileInfo f = new FileInfo(SelectedFolder); // tarkistetaan kansion polun oikeellisuus
128            }
129            catch (ArgumentException)
130            {
131                AddMessage("The folder path is invalid!");
132                return false;
133            }
134
135            if (SelectedFileExtensions == null)
136            {
137                AddMessage("No file extensions specified!");
138                return false;
139            }
140
141            if (SelectedFileExtensions.Length == 0)
142            {
143                AddMessage("No file extensions specified!");
144                return false;
145            }
146
147            if (ExecutablePrograms == null)
148            {
149                AddMessage("No executable programs selected!");
150                return false;
151            }
152
153            WatchCreation = (bool)MainWindow.CreationBox.IsChecked;
154            WatchDeletion = (bool)MainWindow.DeletionBox.IsChecked;
155            WatchChanges = (bool)MainWindow.ChangeBox.IsChecked;
156            WatchRenaming = (bool)MainWindow.RenameBox.IsChecked;
157
158            if (WatchChanges == false && WatchCreation == false && WatchDeletion == false && WatchRenaming == false)
159            {
160                AddMessage("No events selected!");
161                return false;
162            }
163
164            AddMessage("Inputs valid. Starting to watch.");
165
166            StartWatching();
167            return true;
168        }
169
170        /// <summary>
171        /// Aloitetaan tiedosten muutosten tarkkailu valitussa kansiossa.
172        /// </summary>
173        [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
174        public static void StartWatching()
175        {
176            watchers = new List<FileSystemWatcher>();
177
178            foreach (String extension in SelectedFileExtensions)
179            {
180                FileSystemWatcher watcher = new FileSystemWatcher();
181                var searchPath = SelectedFolder;
182                watcher.Path = searchPath;
183                watcher.IncludeSubdirectories = WatchSubdirectories;
184                watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
185                   | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.Security | NotifyFilters.CreationTime;
186                watcher.Filter = extension;
187
188                if (WatchCreation) watcher.Created += delegate(object source, FileSystemEventArgs e) { OnChanged(source, e); };
189                if (WatchDeletion) watcher.Deleted += delegate(object source, FileSystemEventArgs e) { OnChanged(source, e); };
190                if (WatchChanges) watcher.Changed += delegate(object source, FileSystemEventArgs e) { OnChanged(source, e); };
191                if (WatchRenaming) watcher.Renamed += delegate(object source, RenamedEventArgs e) { OnChanged(source, e); };
192
193                watcher.EnableRaisingEvents = true;
194                watchers.Add(watcher);
195            }
196            if (WatchSubdirectories) AddMessage("Watching folder " + SelectedFolder + " and its subfolders for changes.");
197            else AddMessage("Watching folder " + SelectedFolder + " for changes.");
198
199            AddMessage("Watched file types: ", false);
200            foreach (String ext in SelectedFileExtensions)
201            {
202                AddMessage(ext, false);
203            }
204
205            AddMessage("\nWatched events: ");
206            if (WatchCreation) AddMessage("File creation");
207            if (WatchDeletion) AddMessage("File deletion");
208            if (WatchChanges) AddMessage("Changes in files");
209            if (WatchRenaming) AddMessage("Renaming files");
210        }
211
212        /// <summary>
213        /// Lopetetaan muutosten tarkkailu.
214        /// </summary>
215        public static void StopWatching()
216        {
217            if (watchers != null)
218            {
219                for (int i = 0; i < watchers.Count; i++)
220                {
221                    watchers[i].EnableRaisingEvents = false;
222                    watchers[i].Dispose();
223                }
224                watchers.Clear();
225                jobs.Clear();
226                AddMessage("Stopped watching.");
227            }
228            AddMessage("-----------------------------------");
229            AddMessage();
230        }
231
232        /// <summary>
233        /// Näytetään tekstiä viesti-ikkunassa.
234        /// </summary>
235        /// <param name="message">Näytettävä viesti.</param>
236        public static void AddMessage(String message, bool addNewLine = true)
237        {
238            if (addNewLine) MainWindow.MessageWindow.Content += message + "\n";
239            else MainWindow.MessageWindow.Content += message;
240            MainWindow.MessageWindow.ScrollToBottom();
241            if (KeepLog) WriteToLog(message);
242        }
243
244        /// <summary>
245        /// Lisätään rivinvaihto viesti-ikkunaan.
246        /// </summary>
247        public static void AddMessage()
248        {
249            MainWindow.MessageWindow.Content += "\n";
250            MainWindow.MessageWindow.ScrollToBottom();
251            if (KeepLog) WriteToLog();
252        }
253
254        /// <summary>
255        /// Lisätään viesti-ikkunaan viesti toisesta säikeestä.
256        /// </summary>
257        /// <param name="message">Viesti</param>
258        public static void AddMessageFromAnotherThread(String message, bool addNewLine = true)
259        {
260            MainWindow.UIDispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
261            {
262                if (addNewLine) MainWindow.MessageWindow.Content += message + "\n";
263                else MainWindow.MessageWindow.Content += message;
264                MainWindow.MessageWindow.ScrollToBottom();
265                if (KeepLog) WriteToLog(message);
266            }));
267        }
268
269        /// <summary>
270        /// Tyhjennetään viesti-ikkuna.
271        /// </summary>
272        public static void ClearMessages()
273        {
274            MainWindow.MessageWindow.Content = "";
275        }
276
277        /// <summary>
278        /// Ajetaan ohjelmat tiedoston muututtua.
279        /// </summary>
280        /// <param name="source">Jaa?</param>
281        /// <param name="e">Tiedoston polku.</param>
282        private static void OnChanged(object source, FileSystemEventArgs e)
283        {
284            String changedFile = e.FullPath;
285            FileInfo file = new FileInfo(changedFile);
286
287            if (jobs.Contains(changedFile))
288                return;
289
290            AddMessageFromAnotherThread("File \"" + GetName(changedFile) + "\" changed (" + e.ChangeType.ToString() + ")");
291            jobs.Add(changedFile);
292
293            // wait until file is not locked anymore
294            while (IsFileLocked(file))
295            {
296                AddMessageFromAnotherThread("File locked, waiting for it to be released before continuing.");
297                Thread.Sleep(5000);
298            }
299
300            for (int i = 0; i < ExecutablePrograms.Count; i++)
301            {
302                ProcessStartInfo psi = new ProcessStartInfo();
303                psi.FileName = Environment.ExpandEnvironmentVariables(ExecutablePrograms[i].Path);
304                psi.Arguments = Environment.ExpandEnvironmentVariables(GenerateArguments(ExecutablePrograms[i], changedFile));
305
306                try
307                {
308                    AddMessageFromAnotherThread("Executing program \"" + ExecutablePrograms[i].GetName() + "\".");
309                    Process process = Process.Start(psi);
310                    process.EnableRaisingEvents = true; // muuten ei tule Exitediä
311                    process.Exited += delegate {
312                        jobs.Remove(changedFile);
313                        /*if (ExecutablePrograms[i].SubPrograms != null && ExecutablePrograms[i].SubPrograms.Count != 0)
314                        {
315                            RunSubProgram(ExecutablePrograms[i], changedFile);
316                        }*/
317                    };
318                }
319                catch (Win32Exception ex)
320                {
321                    AddMessageFromAnotherThread("Execution failed, Win32 error code: " + ex.ErrorCode);
322                }
323            }
324        }
325
326        /// <summary>
327        /// Ajetaan kaikki ohjelman aliohjelmat siten, että edellisen päätyttyä seuraava käynnistyy.
328        /// </summary>
329        /// <param name="parent">Ohjelma, jonka aliohjelmat ajetaan.</param>
330        /// <param name="file">Päivittynyt tiedosto.</param>
331        /*private static void RunSubProgram(Executable parent, string file)
332        {
333            if (parent.CurrentSubProgram >= parent.SubPrograms.Count) return;
334
335            ProcessStartInfo psi = new ProcessStartInfo();
336            psi.FileName = Environment.ExpandEnvironmentVariables(parent.SubPrograms[parent.CurrentSubProgram].Path);
337            psi.Arguments = Environment.ExpandEnvironmentVariables(GenerateArguments(parent.SubPrograms[parent.CurrentSubProgram], file));
338
339            try
340            {
341                AddMessageFromAnotherThread("Executing subprogram \"" + parent.SubPrograms[parent.CurrentSubProgram].GetName() + "\".");
342                Process process = Process.Start(psi);
343                process.EnableRaisingEvents = true; // muuten ei tule Exitediä
344                process.Exited += delegate
345                {
346                    parent.CurrentSubProgram++;
347                    RunSubProgram(parent, file);
348                };
349            }
350            catch (Win32Exception ex)
351            {
352                AddMessageFromAnotherThread("Execution failed, Win32 error code: " + ex.ErrorCode);
353            }
354        }*/
355
356        /// <summary>
357        /// Muutetaan parametrit sopivaan muotoon asetusten mukaan.
358        /// </summary>
359        /// <param name="ex">Parametrit ja niiden asetukset.</param>
360        /// <param name="file">Tiedosto.</param>
361        /// <returns>Parametrit.</returns>
362        public static String GenerateArguments(Executable ex, string file)
363        {
364            String args = "";
365
366            if (ex.HasFileAsFirstParameter)
367            {
368                args = file + ex.Arguments;
369            }
370            else
371            {
372                int filePos = ex.Arguments.IndexOf("%1");
373                args = ex.Arguments.Substring(0, filePos); // ennen %1
374                args += " " + file; // tiedostopolku
375                args += ex.Arguments.Substring(filePos + 2); // %1 jälkeen
376            }
377
378            return args;
379        }
380
381        /// <summary>
382        /// Palauttaa ohjelman nimen ilman hakemistopolkua.
383        /// </summary>
384        /// <param name="path">Koko hakemistopolku.</param>
385        /// <returns>Ohjelman nimi.</returns>
386        public static String GetName(string path)
387        {
388            int kauttaviivanPaikka = 0;
389            String nimi = "";
390            for (int i = path.Length - 1; i >= 0; i--)
391            {
392                if (path[i] == '\\')
393                {
394                    kauttaviivanPaikka = i;
395                    break;
396                }
397            }
398            nimi = path.Substring(kauttaviivanPaikka + 1);
399            return nimi;
400        }
401
402
403        /// <summary>
404        /// Checks if the file is locked because of
405        /// still being written to
406        /// or being processed by another thread
407        /// or does not exist (has already been processed)
408        /// </summary>
409        /// <param name="file">File</param>
410        /// <returns>Is the file locked</returns>
411        private static bool IsFileLocked(FileInfo file)
412        {
413            FileStream stream = null;
414
415            try
416            {
417                stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
418                //stream = file.Open(FileMode.Open, FileAccess.Read);
419            }
420            catch (IOException)
421            {
422                //the file is unavailable because it is:
423                // * still being written to or
424                // * being processed by another thread or
425                // * does not exist (has already been processed)
426                return true;
427            }
428            finally
429            {
430                if (stream != null)
431                    stream.Close();
432            }
433
434            //file is not locked
435            return false;
436        }
437
438        public static void UpdateExecutableList()
439        {
440            MainWindow.ProgramWindow.Items.Clear();
441            for (int i = 0; i < ExecutablePrograms.Count; i++)
442            {
443                if (ExecutablePrograms[i].HasFileAsFirstParameter)
444                    MainWindow.ProgramWindow.Items.Add(ExecutablePrograms[i].GetName() + " [file] " + ExecutablePrograms[i].Arguments);
445                else
446                    MainWindow.ProgramWindow.Items.Add(ExecutablePrograms[i].GetName() + " " + ExecutablePrograms[i].Arguments.Replace("%1", "[file]"));
447
448                if (ExecutablePrograms[i].SubPrograms == null) continue;
449                for (int j = 0; j < ExecutablePrograms[i].SubPrograms.Count; j++)
450                {
451                    if (ExecutablePrograms[i].SubPrograms[j].HasFileAsFirstParameter)
452                        MainWindow.ProgramWindow.Items.Add("  " + (j + 1) + ". " + ExecutablePrograms[i].SubPrograms[j].GetName() + " [file] " + ExecutablePrograms[i].SubPrograms[j].Arguments);
453                    else
454                        MainWindow.ProgramWindow.Items.Add("  " + (j + 1) + ". " + ExecutablePrograms[i].SubPrograms[j].GetName() + " " + ExecutablePrograms[i].SubPrograms[j].Arguments.Replace("%1", "[file]"));
455                }
456            }
457        }
458
459        public static void UpdateEvents()
460        {
461            WatchCreation = (bool)MainWindow.CreationBox.IsChecked;
462            WatchDeletion = (bool)MainWindow.DeletionBox.IsChecked;
463            WatchChanges = (bool)MainWindow.ChangeBox.IsChecked;
464            WatchRenaming = (bool)MainWindow.RenameBox.IsChecked;
465        }
466
467        /// <summary>
468        /// Tallennetaan käyttäjän tämänhetkiset asetukset.
469        /// </summary>
470        public static void SaveSettings()
471        {
472            UpdateEvents();
473            OptionsFile ops = new OptionsFile(SelectedFolder, SelectedFileExtensions, ExecutablePrograms, WatchCreation, WatchDeletion, WatchChanges, WatchRenaming, LoadSettingsAutomatically, SaveSettingsAutomatically, StartWatchingAutomatically, KeepLog);
474
475            FileStream stream = new FileStream(OptionsFilePath, FileMode.Create);
476
477            BinaryFormatter formatter = new BinaryFormatter();
478            try
479            {
480                formatter.Serialize(stream, ops);
481            }
482            catch (SerializationException se)
483            {
484                AddMessage("Saving settings failed: " + se.Message);
485            }
486            finally
487            {
488                stream.Close();
489            }
490        }
491
492        /// <summary>
493        /// Tallennetaan käyttäjän tämänhetkiset asetukset.
494        /// </summary>
495        public static void SaveSettings(OptionsFile options)
496        {
497            if (options == null) return;
498
499            UpdateEvents();
500            FileStream stream = new FileStream(OptionsFilePath, FileMode.Create);
501
502            BinaryFormatter formatter = new BinaryFormatter();
503            try
504            {
505                formatter.Serialize(stream, options);
506            }
507            catch (SerializationException se)
508            {
509                AddMessage("Saving settings failed: " + se.Message);
510            }
511            finally
512            {
513                stream.Close();
514            }
515        }
516
517
518        /// <summary>
519        /// Ladataan asetukset tiedostosta.
520        /// </summary>
521        public static OptionsFile LoadSettings()
522        {
523            OptionsFile ops = new OptionsFile();
524            FileStream stream;
525            BinaryFormatter formatter = new BinaryFormatter();
526            try
527            {
528                using (stream = new FileStream(OptionsFilePath, FileMode.Open))
529                {
530                    ops = (OptionsFile)formatter.Deserialize(stream);
531                }
532            }
533            catch (SerializationException se)
534            {
535                AddMessage("Loading settings failed: " + se.Message);
536            }
537            catch (FileNotFoundException)
538            {
539                AddMessage("Options file missing, reverting to defaults.");
540                SaveSettings(DefaultSettings);
541                return DefaultSettings;
542            }
543            return ops;
544        }
545
546        public static void UseSettings(OptionsFile settings, bool useOnlyOptionsScreenSettings = false)
547        {
548            if (!useOnlyOptionsScreenSettings)
549            {
550                SelectedFolder = settings.SelectedFolder;
551                SelectedFileExtensions = settings.SelectedFileExtensions;
552                ExecutablePrograms = settings.ExecutablePrograms;
553                WatchSubdirectories = settings.WatchSubdirectories;
554
555                WatchCreation = settings.WatchCreation;
556                WatchDeletion = settings.WatchDeletion;
557                WatchChanges = settings.WatchChanges;
558                WatchRenaming = settings.WatchRenaming;
559            }
560
561            LoadSettingsAutomatically = settings.LoadSettingsAutomatically;
562            SaveSettingsAutomatically = settings.SaveSettingsAutomatically;
563            StartWatchingAutomatically = settings.StartWatchingAutomatically;
564
565            KeepLog = settings.KeepLog;
566
567            if (!useOnlyOptionsScreenSettings)
568                MainWindow.UpdateVisuals();
569        }
570
571        /// <summary>
572        /// Kirjoitetaan tekstiä lokitiedostoon.
573        /// </summary>
574        /// <param name="message">Kirjoitettava teksti.</param>
575        public static void WriteToLog(string message)
576        {
577            LogWriter.WriteLine(DateTime.Now + " " + message);
578            LogWriter.Flush();
579        }
580
581        /// <summary>
582        /// Lisätään rivinvaihto lokiin.
583        /// </summary>
584        public static void WriteToLog()
585        {
586            LogWriter.WriteLine();
587            LogWriter.Flush();
588        }
589    }
590}
Note: See TracBrowser for help on using the repository browser.