We have an F# console app which processes some gigabytes of data over a couple of hours. We wanted to run this on a specific machine daily in the early hours of the morning. The easy answer Windows XP's Scheduled Tasks. Just click

Start > All Programs > Accessories > System Tools > Scheduled Tasks.

We also wanted a simple install mechanism for the script, dependencies and to schedule the task. Managing tasks can be automated from the command line with Schtasks.exe or a COM interface (see this CodeProject article). The command line option was taken for simplicity. A scheduled task could be created and deleted like this:

> Schtasks.exe /Create /TN MorningDewey /TR "c:\windows\explorer.exe http://www.msdewey.com" /SC DAILY /ST 09:00:00
> Schtasks.exe /Delete /TN MorningDewey

When you create the task you may be prompted for your account password. If your task is command line only then you can use the /RU "System" option to run under the system account; this will not work for launching a GUI application.

The whole solution was packaged up using Visual Studio Setup Project and a custom installer. The custom installer implements System.Configuration.Install.Installer with the Install and Uninstall methods invoking Schtasks.exe to create and delete the task respectively. The install directory can be deduced from the assemblypath context parameter:

        string installDirectory = Directory.GetCurrentDirectory();

        // Try to get install directory from install assembly path

        if (this.Context != null)

        {

            string assemblyPath = this.Context.Parameters["assemblypath"];

            installDirectory = Path.GetDirectoryName(assemblyPath);

        }

        string taskPath = Path.Combine(installDirectory, taskExecutable);

I found that if the path to the task had any spaces the task would not execute under the scheduler; the answer was to convert the path to short name using the Win32 method GetShortPathName and PInvoke. Finally a method to invoke Schtasks using the System.Diagnostics.Process class:

        private static void RunCommandLine(string fileName, string arguments)

        {

            string workingDirectory = Directory.GetCurrentDirectory();

 

            Process proc = new Process();

            using(proc)

            {

                ProcessStartInfo info = proc.StartInfo;

                info.UseShellExecute = false;

                      info.RedirectStandardOutput = true;

                info.RedirectStandardError = true;

                      info.CreateNoWindow = true;

                      info.FileName = fileName;

                      info.Arguments = arguments;

                      info.WorkingDirectory = workingDirectory;

                      if( proc.Start() )

                {

                    string output = proc.StandardOutput.ReadToEnd();

                    Debug.WriteLine(output);

                          proc.WaitForExit();

                }

            }

        }

Phillip Trelford