source: trunk/eraser/Eraser/Program.cs @ 2267

Revision 2267, 26.5 KB checked in by lowjoel, 4 years ago (diff)

Forward-port from Eraser 6.0: Fix the "Eraser cannot connect to the running instance for erasure" message when we need to start a new instance. This is because of a race condition when starting the new instance: the Frame is initialised before the remote server is started so WaitForInputIdle? returns before the server is set up.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Windows.Forms;
25
26using System.IO;
27using System.Threading;
28using System.Globalization;
29using System.ComponentModel;
30using System.Runtime.Serialization;
31using System.Security.Principal;
32using System.Text.RegularExpressions;
33
34using System.Reflection;
35using System.Diagnostics;
36
37using ComLib.Arguments;
38
39using Eraser.Manager;
40using Eraser.Util;
41using Eraser.DefaultPlugins;
42using System.Text;
43
44namespace Eraser
45{
46    internal static partial class Program
47    {
48        /// <summary>
49        /// The common program arguments shared between the GUI and console programs.
50        /// </summary>
51        class Arguments
52        {
53            /// <summary>
54            /// True if the program should not be started with any user-visible interfaces.
55            /// </summary>
56            /// <remarks>Errors will also be silently ignored.</remarks>
57            [Arg("quiet", "The program should not be started with any user-visible interfaces. " +
58                "Errors will be silently ignored.", typeof(bool), false, false, null)]
59            public bool Quiet { get; set; }
60        }
61
62        /// <summary>
63        /// Program arguments which only apply to the GUI program.
64        /// </summary>
65        class GuiArguments : Arguments
66        {
67            /// <summary>
68            /// True if the command line specified atRestart, which should result in the
69            /// queueing of tasks meant for running at restart.
70            /// </summary>
71            [Arg("atRestart", "The program should queue all tasks scheduled for running at " +
72                "the system restart.", typeof(bool), false, false, null)]
73            public bool AtRestart { get; set; }
74        }
75
76        class ConsoleArguments : Arguments
77        {
78            /// <summary>
79            /// Constructor.
80            /// </summary>
81            public ConsoleArguments()
82            {
83            }
84
85            /// <summary>
86            /// Copy constructor.
87            /// </summary>
88            /// <param name="arguments">The <see cref="ConsoleArguments"/> to use as a template
89            /// for this instance.</param>
90            protected ConsoleArguments(ConsoleArguments arguments)
91            {
92                Action = arguments.Action;
93                PositionalArguments = arguments.PositionalArguments;
94            }
95
96            /// <summary>
97            /// The Action which this handler is in charge of.
98            /// </summary>
99            [Arg(0, "The action this command line is stating.", typeof(string), true, null, null)]
100            public string Action { get; set; }
101
102            /// <summary>
103            /// The list of command line parameters not placed in a switch.
104            /// </summary>
105            public List<string> PositionalArguments { get; set; }
106        }
107
108        class EraseArguments : ConsoleArguments
109        {
110            /// <summary>
111            /// Constructor.
112            /// </summary>
113            public EraseArguments()
114            {
115            }
116
117            /// <summary>
118            /// Copy constructor.
119            /// </summary>
120            /// <param name="arguments">The <see cref="EraseArguments"/> to use as a template
121            /// for this instance.</param>
122            protected EraseArguments(EraseArguments arguments)
123                : base(arguments)
124            {
125                ErasureMethod = arguments.ErasureMethod;
126            }
127
128            /// <summary>
129            /// The erasure method which the user specified on the command line.
130            /// </summary>
131            [Arg("method", "The erasure method to use", typeof(string), false, null, null)]
132            public string ErasureMethod { get; set; }
133        }
134
135        class AddTaskArguments : EraseArguments
136        {
137            /// <summary>
138            /// Constructor.
139            /// </summary>
140            public AddTaskArguments()
141            {
142            }
143
144            /// <summary>
145            /// Constructs Add Task arguments from Erase arguments.
146            /// </summary>
147            /// <param name="arguments">The <see cref="EraseArguments"/> to use as a template
148            /// for this instance.</param>
149            internal AddTaskArguments(EraseArguments arguments)
150                : base(arguments)
151            {
152            }
153
154            /// <summary>
155            /// The schedule for the current set of targets.
156            /// </summary>
157            [Arg("schedule", "The schedule to use", typeof(Schedule), false, null, null)]
158            public string Schedule { get; set; }
159        }
160
161        class ShellArguments : ConsoleArguments
162        {
163            /// <summary>
164            /// The action which the shell extension has requested.
165            /// </summary>
166            [Arg("action", "The action selected by the user", typeof(string), true, null, null)]
167            public ShellActions ShellAction { get; set; }
168
169            /// <summary>
170            /// Whether the recycle bin was specified on the command line.
171            /// </summary>
172            [Arg("recycleBin", "The recycle bin as an erasure target", typeof(string), false, null, null)]
173            public bool RecycleBin { get; set; }
174
175            /// <summary>
176            /// The destination for secure move operations, only valid when
177            /// <see cref="ShellAction"/> is <see cref="ShellActions.SecureMove"/>
178            /// </summary>
179            [Arg("destination", "The destination for secure move operations", typeof(string), false, null, null)]
180            public string Destination { get; set; }
181
182            /// <summary>
183            /// The parent HWND which can be used as a parent to display dialogs.
184            /// </summary>
185            [Arg("parent", "The parent HWND which can be used as a parent to display dialogues", typeof(string), false, null, null)]
186            public string Parent { get; set; }
187        }
188
189        public enum ShellActions
190        {
191            /// <summary>
192            /// Erase the selected items now.
193            /// </summary>
194            EraseNow,
195
196            /// <summary>
197            /// Erase the selected items on restart.
198            /// </summary>
199            EraseOnRestart,
200
201            /// <summary>
202            /// Erase the unused space on the drive.
203            /// </summary>
204            EraseUnusedSpace,
205
206            /// <summary>
207            /// Securely moves a file from one drive to another (simple rename if the source and
208            /// destination drives are the same)
209            /// </summary>
210            SecureMove
211        }
212
213        /// <summary>
214        /// The main entry point for the application.
215        /// </summary>
216        [STAThread]
217        static int Main(string[] rawCommandLine)
218        {
219            //Immediately parse command line arguments. Start by substituting all
220            //response files ("@filename") arguments with the arguments found in the
221            //file
222            List<string> commandLine = new List<string>(rawCommandLine.Length);
223            foreach (string argument in rawCommandLine)
224            {
225                if (argument[0] == '@' && File.Exists(argument.Substring(1)))
226                {
227                    //The current parameter is a response file, parse the file
228                    //for arguments and substitute it.
229                    using (TextReader reader = new StreamReader(argument.Substring(1)))
230                    {
231                        commandLine.AddRange(Shell.ParseCommandLine(reader.ReadToEnd()));
232                    }
233                }
234                else
235                    commandLine.Add(argument);
236            }
237
238            string[] finalCommandLine = commandLine.ToArray();
239            ComLib.BoolMessageItem argumentParser = Args.Parse(finalCommandLine,
240                CommandLinePrefixes, CommandLineSeparators);
241            Args parsedArguments = (Args)argumentParser.Item;
242
243            //Load the Eraser.Manager library
244            using (ManagerLibrary library = new ManagerLibrary(new Settings()))
245            {
246                //Set our UI language
247                EraserSettings settings = EraserSettings.Get();
248                Thread.CurrentThread.CurrentUICulture = new CultureInfo(settings.Language);
249
250                //We default to a GUI if:
251                // - The parser did not succeed.
252                // - The parser resulted in an empty arguments list
253                // - The parser's argument at index 0 is not equal to the first argument
254                //   (this is when the user is passing GUI options -- command line options
255                //   always start with the action, e.g. Eraser help, or Eraser addtask
256                if (!argumentParser.Success || parsedArguments.IsEmpty ||
257                    parsedArguments.Positional.Count == 0 ||
258                    parsedArguments.Positional[0] != parsedArguments.Raw[0])
259                {
260                    GUIMain(finalCommandLine);
261                }
262                else
263                {
264                    return CommandMain(finalCommandLine);
265                }
266            }
267
268            //Return zero to signify success
269            return 0;
270        }
271
272        #region Console Program code
273        /// <summary>
274        /// Connects to the running Eraser instance for erasures.
275        /// </summary>
276        /// <returns>The connectin with the remote instance.</returns>
277        private static RemoteExecutorClient CommandConnect()
278        {
279            try
280            {
281                RemoteExecutorClient result = new RemoteExecutorClient();
282                result.Run();
283                if (!result.IsConnected)
284                {
285                    //The client cannot connect to the server. This probably means
286                    //that the server process isn't running. Start an instance.
287                    Process eraserInstance = Process.Start(
288                        Assembly.GetExecutingAssembly().Location, "/quiet");
289                    eraserInstance.WaitForInputIdle();
290
291                    //Wait for the server to be initialised.
292                    for (int i = 0; !result.IsConnected; ++i)
293                    {
294                        Thread.Sleep(100);
295                        result.Run();
296
297                        //After 10s, we should probably give up.
298                        if (i > 100)
299                            throw new IOException(S._("Eraser cannot connect to the running " +
300                                "instance for erasures."));
301                    }
302                }
303
304                return result;
305            }
306            catch (UnauthorizedAccessException e)
307            {
308                //We can't connect to the pipe because the other instance of Eraser
309                //is running with higher privileges than this instance.
310                throw new UnauthorizedAccessException(S._("Another instance of Eraser " +
311                    "is already running but it is running with higher privileges than " +
312                    "this instance of Eraser. Tasks cannot be added in this manner.\n\n" +
313                    "Close the running instance of Eraser and start it again without " +
314                    "administrator privileges, or run the command again as an " +
315                    "administrator.", e));
316            }
317        }
318
319        /// <summary>
320        /// Runs Eraser as a command-line application.
321        /// </summary>
322        /// <param name="commandLine">The command line parameters passed to Eraser.</param>
323        private static int CommandMain(string[] commandLine)
324        {
325            using (ConsoleProgram program = new ConsoleProgram(commandLine))
326                try
327                {
328                    program.Handlers.Add("help",
329                        new ConsoleActionData(CommandHelp, new ConsoleArguments()));
330                    program.Handlers.Add("erase",
331                        new ConsoleActionData(CommandErase, new EraseArguments())); 
332                    program.Handlers.Add("addtask",
333                        new ConsoleActionData(CommandAddTask, new AddTaskArguments()));
334                    program.Handlers.Add("importtasklist",
335                        new ConsoleActionData(CommandImportTaskList, new ConsoleArguments()));
336                    program.Handlers.Add("shell",
337                        new ConsoleActionData(CommandShell, new ShellArguments()));
338                    program.Run();
339                    return 0;
340                }
341                catch (UnauthorizedAccessException)
342                {
343                    return Win32ErrorCode.AccessDenied;
344                }
345                catch (Win32Exception e)
346                {
347                    Console.WriteLine(e.Message);
348                    return e.ErrorCode;
349                }
350                catch (Exception e)
351                {
352                    Console.WriteLine(e.Message);
353                    return 1;
354                }
355        }
356
357        /// <summary>
358        /// Prints the command line help for Eraser.
359        /// </summary>
360        private static void PrintCommandHelp()
361        {
362            //Get the command-line help for every erasure target
363            StringBuilder targets = new StringBuilder();
364            foreach (ErasureTarget target in ManagerLibrary.Instance.ErasureTargetRegistrar)
365            {
366                //Replace all \r\n with \n, and split into lines
367                string[] helpText = target.Configurer.Help().Replace("\r\n", "\n").Split('\r', '\n');
368
369                //Pad the start of each line with spaces
370                foreach (string line in helpText)
371                    targets.AppendLine(line.Insert(0, "    "));
372            }
373
374            //Get the list of registered Erasure Methods. First, output the header.
375            const string methodFormat = "    {0,-2} {1,-35} {2}\n";
376            StringBuilder methods = new StringBuilder();
377            methods.AppendFormat(methodFormat, "", "Erasure Method", "GUID");
378            methods.AppendLine("    " + new string('-', 75));
379
380            //Generate the list of erasure methods.
381            foreach (ErasureMethod method in ManagerLibrary.Instance.ErasureMethodRegistrar)
382            {
383                methods.AppendFormat(methodFormat, (method is UnusedSpaceErasureMethod) ?
384                    "U" : "", method.Name, method.Guid);
385            }
386
387            //Print the message
388            Console.WriteLine(S._(@"usage: Eraser <action> <arguments>
389where action is
390  help                Show this help message.
391  erase               Erases items specified on the command line. This is
392                      equivalent to addtask, with the schedule set to ""now"".
393  addtask             Adds a task to the current task list.
394  importtasklist      Imports an Eraser Task list to the current user's Task
395                      List.
396
397global parameters:
398  /quiet              Do not create a Console window to display progress.
399
400parameters for help:
401  eraser help
402
403  no parameters to set.
404
405parameters for erase and addtask:
406  eraser erase [/method=(<methodGUID>|<methodName>)] <target> [target [...]]
407  eraser addtask [/method=(<methodGUID>|<methodName>)] [/schedule=(now|manually|restart)] <target> [target [...]]
408
409  /method             The Erasure method to use.
410    methodGUID and methodName can be any GUID/Name from the following list:
411{0}
412    Only erasure methods labelled ""U"" can be used to erase unused disk space.
413
414  /schedule           The schedule the task will follow. The value must be one
415                      of:
416    now               The task will be queued for immediate execution.
417    manually          The task will be created but not queued for execution.
418    restart           The task will be queued for execution when the computer
419                      is next restarted.
420
421                      This parameter is only valid for use with ""addtask"".
422
423  target is one or more of:
424{1}
425parameters for importtasklist:
426  eraser importtasklist <file>[...]
427
428    file               A list of one or more files to import.
429
430All arguments are case sensitive.
431
432Response files can be used for very long command lines (generally, anything
433involving more than 32,000 characters.) Response files are used by prepending
434""@"" to the path to the file, and passing it into the command line. The
435contents of the response files' will be substituted at the same position into
436the command line.", methods, targets));
437
438            Console.Out.Flush();
439        }
440
441        /// <summary>
442        /// Prints the help text for Eraser (with copyright)
443        /// </summary>
444        /// <param name="arguments">Not used.</param>
445        private static void CommandHelp(ConsoleArguments arguments)
446        {
447            Console.WriteLine(S._(@"Eraser {0}
448(c) 2008-2010 The Eraser Project
449Eraser is Open-Source Software: see http://eraser.heidi.ie/ for details.
450", BuildInfo.AssemblyFileVersion));
451
452            PrintCommandHelp();
453        }
454
455        /// <summary>
456        /// Parses the command line for tasks and adds them to run immediately
457        /// using the <see cref="RemoveExecutor"/> class.
458        /// </summary>
459        /// <param name="arg">The command line parameters passed to the program.</param>
460        private static void CommandErase(ConsoleArguments arg)
461        {
462            AddTaskArguments arguments = new AddTaskArguments((EraseArguments)arg);
463            arguments.Schedule = "NOW";
464
465            CommandAddTask(arguments);
466        }
467
468        /// <summary>
469        /// Parses the command line for tasks and adds them using the
470        /// <see cref="RemoteExecutor"/> class.
471        /// </summary>
472        /// <param name="arg">The command line parameters passed to the program.</param>
473        private static void CommandAddTask(ConsoleArguments arg)
474        {
475            AddTaskArguments arguments = (AddTaskArguments)arg;
476
477            //Create the task
478            Task task = new Task();
479
480            //Get the erasure method the user wants to use
481            ErasureMethod method = string.IsNullOrEmpty(arguments.ErasureMethod) ?
482                ErasureMethodRegistrar.Default :
483                ErasureMethodFromNameOrGuid(arguments.ErasureMethod);
484
485            //Define the schedule
486            switch (arguments.Schedule.ToUpperInvariant())
487            {
488                case "":
489                case "NOW":
490                    task.Schedule = Schedule.RunNow;
491                    break;
492                case "MANUALLY":
493                    task.Schedule = Schedule.RunManually;
494                    break;
495                case "RESTART":
496                    task.Schedule = Schedule.RunOnRestart;
497                    break;
498                default:
499                    throw new ArgumentException(
500                        S._("Unknown schedule type: {0}", arguments.Schedule), "/schedule");
501            }
502
503            //Parse the rest of the command line parameters as target expressions.
504            foreach (string argument in arguments.PositionalArguments)
505            {
506                ErasureTarget selectedTarget = null;
507
508                //Iterate over every defined erasure target
509                foreach (ErasureTarget target in ManagerLibrary.Instance.ErasureTargetRegistrar)
510                {
511                    //See if this argument can be handled by the target's configurer
512                    IErasureTargetConfigurer configurer = target.Configurer;
513                    if (configurer.ProcessArgument(argument))
514                    {
515                        //Check whether a target has been set (implicitly: check whether two
516                        //configurers can process the argument)
517                        if (selectedTarget == null)
518                        {
519                            configurer.SaveTo(target);
520                            selectedTarget = target;
521                        }
522                        else
523                        {
524                            //Yes, it is an ambiguity. Throw an error.
525                            throw new ArgumentException(S._("Ambiguous argument: {0} can be " +
526                                "handled by more than one erasure target.", argument));
527                        }
528                    }
529                }
530
531                //Check whether a target has been made from parsing the entry.
532                if (selectedTarget == null)
533                {
534                    Console.WriteLine(S._("Unknown argument: {0}, skipped.", argument));
535                }
536                else
537                {
538                    selectedTarget.Method = method;
539                    task.Targets.Add(selectedTarget);
540                }
541            }
542
543            //Check the number of tasks in the task.
544            if (task.Targets.Count == 0)
545                throw new ArgumentException(S._("Tasks must contain at least one erasure target."));
546
547            //Send the task out.
548            using (eraserClient = CommandConnect())
549                eraserClient.Tasks.Add(task);
550        }
551
552        private static ErasureMethod ErasureMethodFromNameOrGuid(string param)
553        {
554            try
555            {
556                return ManagerLibrary.Instance.ErasureMethodRegistrar[new Guid(param)];
557            }
558            catch (FormatException)
559            {
560                //Invalid GUID. Check every registered erasure method for the name
561                string upperParam = param.ToUpperInvariant();
562                ErasureMethod result = null;
563                foreach (ErasureMethod method in ManagerLibrary.Instance.ErasureMethodRegistrar)
564                {
565                    if (method.Name.ToUpperInvariant() == upperParam)
566                        if (result == null)
567                            result = method;
568                        else
569                            throw new ArgumentException(S._("Ambiguous erasure method name: {0} " +
570                                "identifies more than one erasure method.", param));
571                }
572            }
573
574            throw new ArgumentException(S._("The provided Erasure Method '{0}' does not exist.",
575                param));
576        }
577
578        /// <summary>
579        /// Imports the given tasklists and adds them to the global Eraser instance.
580        /// </summary>
581        /// <param name="args">The list of files specified on the command line.</param>
582        private static void CommandImportTaskList(ConsoleArguments args)
583        {
584            //Import the task list
585            using (eraserClient = CommandConnect())
586                foreach (string path in args.PositionalArguments)
587                    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
588                        eraserClient.Tasks.LoadFromStream(stream);
589        }
590
591        /// <summary>
592        /// Handles the files from the Shell extension.
593        /// </summary>
594        /// <param name="args">The command line parameters passed to the program.</param>
595        private static void CommandShell(ConsoleArguments args)
596        {
597            switch (((ShellArguments)args).ShellAction)
598            {
599                case ShellActions.SecureMove:
600                    CommandShellSecureMove((ShellArguments)args);
601                    break;
602
603                default:
604                    CommandShellErase((ShellArguments)args);
605                    break;
606            }
607        }
608
609        /// <summary>
610        /// Handles the erasure of files from the Shell extension.
611        /// </summary>
612        /// <param name="args">The command line parameters passed to the program.</param>
613        private static void CommandShellErase(ShellArguments args)
614        {
615            //Construct a draft task.
616            Task task = new Task();
617            switch (args.ShellAction)
618            {
619                case ShellActions.EraseOnRestart:
620                    task.Schedule = Schedule.RunOnRestart;
621                    goto case ShellActions.EraseNow;
622
623                case ShellActions.EraseNow:
624                    foreach (string path in args.PositionalArguments)
625                    {
626                        //If the path doesn't exist, skip the file
627                        if (!(File.Exists(path) || Directory.Exists(path)))
628                            continue;
629
630                        FileSystemObjectErasureTarget target = null;
631                        if ((File.GetAttributes(path) & FileAttributes.Directory) != 0)
632                        {
633                            target = new FolderErasureTarget();
634                            target.Path = path;
635                        }
636                        else
637                        {
638                            target = new FileErasureTarget();
639                            target.Path = path;
640                        }
641
642                        task.Targets.Add(target);
643                    }
644
645                    //Was the recycle bin specified?
646                    if (args.RecycleBin)
647                        task.Targets.Add(new RecycleBinErasureTarget());
648                    break;
649
650                case ShellActions.EraseUnusedSpace:
651                    foreach (string path in args.PositionalArguments)
652                    {
653                        UnusedSpaceErasureTarget target = new UnusedSpaceErasureTarget();
654                        target.Drive = path;
655                        task.Targets.Add(target);
656                    }
657                    break;
658            }
659
660            //Do we have a parent dialog?
661            IWin32Window parent = null;
662            if (args.Parent != null)
663            {
664                parent = new Win32Window((IntPtr)(ulong)
665                    Convert.ChangeType(args.Parent, typeof(ulong)));
666            }
667
668            //Confirm that the user wants the erase.
669            Application.EnableVisualStyles();
670            using (Form dialog = new ShellConfirmationDialog(task))
671            {
672                if (dialog.ShowDialog(parent) != DialogResult.Yes)
673                    return;
674            }
675
676            //Then queue for erasure.
677            using (eraserClient = CommandConnect())
678                eraserClient.Tasks.Add(task);
679        }
680
681        /// <summary>
682        /// Handles the movement of files from the Shell extension.
683        /// </summary>
684        /// <param name="args">The command line parameters passed to the program.</param>
685        private static void CommandShellSecureMove(ShellArguments args)
686        {
687            //Construct a draft task.
688            Task task = new Task();
689            foreach (string path in args.PositionalArguments)
690            {
691                SecureMoveErasureTarget target = new SecureMoveErasureTarget();
692                target.Path = path;
693                target.Destination = args.Destination;
694
695                task.Targets.Add(target);
696            }
697
698            //Then queue for erasure.
699            using (eraserClient = CommandConnect())
700                eraserClient.Tasks.Add(task);
701        }
702        #endregion
703
704        #region GUI Program code
705        /// <summary>
706        /// Runs Eraser as a GUI application.
707        /// </summary>
708        /// <param name="commandLine">The command line parameters passed to Eraser.</param>
709        private static void GUIMain(string[] commandLine)
710        {
711            //Create a unique program instance ID for this user.
712            string instanceId = "Eraser-BAD0DAC6-C9EE-4acc-8701-C9B3C64BC65E-GUI-" +
713                WindowsIdentity.GetCurrent().User.ToString();
714
715            //Then initialise the instance and initialise the Manager library.
716            using (GuiProgram program = new GuiProgram(commandLine, instanceId))
717            {
718                program.InitInstance += OnGUIInitInstance;
719                program.NextInstance += OnGUINextInstance;
720                program.ExitInstance += OnGUIExitInstance;
721                program.Run();
722            }
723        }
724
725        /// <summary>
726        /// Triggered when the Program is started for the first time.
727        /// </summary>
728        /// <param name="sender">The sender of the object.</param>
729        /// <param name="e">Event arguments.</param>
730        private static void OnGUIInitInstance(object sender, InitInstanceEventArgs e)
731        {
732            GuiProgram program = (GuiProgram)sender;
733            eraserClient = new RemoteExecutorServer();
734            Application.SafeTopLevelCaptionFormat = S._("Eraser");
735
736            //Load the task list
737            try
738            {
739                if (File.Exists(TaskListPath))
740                {
741                    using (FileStream stream = new FileStream(TaskListPath, FileMode.Open,
742                        FileAccess.Read, FileShare.Read))
743                    {
744                        eraserClient.Tasks.LoadFromStream(stream);
745                    }
746                }
747            }
748            catch (InvalidDataException ex)
749            {
750                File.Delete(TaskListPath);
751                MessageBox.Show(S._("Could not load task list. All task entries have " +
752                    "been lost. The error returned was: {0}", ex.Message), S._("Eraser"),
753                    MessageBoxButtons.OK, MessageBoxIcon.Error,
754                    MessageBoxDefaultButton.Button1,
755                    Localisation.IsRightToLeft(null) ?
756                        MessageBoxOptions.RtlReading | MessageBoxOptions.RightAlign : 0);
757            }
758
759            //Decide whether to display any UI.
760            GuiArguments arguments = new GuiArguments();
761            Args.Parse(program.CommandLine, CommandLinePrefixes, CommandLineSeparators, arguments);
762            e.ShowMainForm = !arguments.AtRestart && !arguments.Quiet;
763
764            //Queue tasks meant for running at restart if we are given that command line.
765            if (arguments.AtRestart)
766                eraserClient.QueueRestartTasks();
767
768            //Run the eraser client.
769            eraserClient.Run();
770
771            //Create the main form.
772            program.MainForm = new MainForm();
773        }
774
775        /// <summary>
776        /// Triggered when a second instance of Eraser is started.
777        /// </summary>
778        /// <param name="sender">The sender of the event.</param>
779        /// <param name="e">Event argument.</param>
780        private static void OnGUINextInstance(object sender, NextInstanceEventArgs e)
781        {
782            //Another instance of the GUI Program has been started: show the main window
783            //now as we still do not have a facility to handle the command line arguments.
784            GuiProgram program = (GuiProgram)sender;
785
786            //Invoke the function if we aren't on the main thread
787            if (program.MainForm.InvokeRequired)
788            {
789                program.MainForm.Invoke(
790                    (GuiProgram.NextInstanceEventHandler)OnGUINextInstance,
791                    sender, e);
792                return;
793            }
794
795            program.MainForm.Show();
796        }
797
798        /// <summary>
799        /// Triggered when the first instance of Eraser is exited.
800        /// </summary>
801        /// <param name="sender">The sender of the event.</param>
802        /// <param name="e">Event argument.</param>
803        private static void OnGUIExitInstance(object sender, EventArgs e)
804        {
805            //Save the task list
806            if (!Directory.Exists(Program.AppDataPath))
807                Directory.CreateDirectory(Program.AppDataPath);
808            using (FileStream stream = new FileStream(TaskListPath, FileMode.Create,
809                FileAccess.Write, FileShare.None))
810            {
811                eraserClient.Tasks.SaveToStream(stream);
812            }
813
814            //Dispose the eraser executor instance
815            eraserClient.Dispose();
816        }
817        #endregion
818
819        /// <summary>
820        /// The acceptable list of command line prefixes we will accept.
821        /// </summary>
822        public const string CommandLinePrefixes = "^(/|-|--)";
823
824        /// <summary>
825        /// The acceptable list of command line separators we will accept.
826        /// </summary>
827        public const string CommandLineSeparators = "(:|=)";
828
829        /// <summary>
830        /// The global Executor instance.
831        /// </summary>
832        public static Executor eraserClient;
833
834        /// <summary>
835        /// Path to the Eraser application data path.
836        /// </summary>
837        public static readonly string AppDataPath = Path.Combine(Environment.GetFolderPath(
838            Environment.SpecialFolder.LocalApplicationData), @"Eraser 6");
839
840        /// <summary>
841        /// File name of the Eraser task list.
842        /// </summary>
843        private const string TaskListFileName = @"Task List.ersx";
844
845        /// <summary>
846        /// Path to the Eraser task list.
847        /// </summary>
848        public static readonly string TaskListPath = Path.Combine(AppDataPath, TaskListFileName);
849
850        /// <summary>
851        /// Path to the Eraser settings key (relative to HKCU)
852        /// </summary>
853        public const string SettingsPath = @"SOFTWARE\Eraser\Eraser 6";
854    }
855
856    class Win32Window : IWin32Window
857    {
858        public Win32Window(IntPtr hwnd)
859        {
860            Hwnd = hwnd;
861        }
862
863        #region IWin32Window Members
864
865        public IntPtr Handle
866        {
867            get { return Hwnd; }
868        }
869
870        #endregion
871
872        private IntPtr Hwnd;
873    }
874}
Note: See TracBrowser for help on using the repository browser.