TL;DR
- 最上位権限でタスクスケジューラに登録したい
- ログオフ(サインアウト)しておいても動いてほしいから
- エクスポートしたXMLファイルをもとに作って、登録すればなんとかいけた
現在作業中のユーザで登録してみる
まずは普通に登録してみます。
// タスクスケジューラの登録解除
runProcess(@"/c schtasks.exe /delete /TN SampleTask /F");
// タスクスケジューラの登録
var command = "/c schtasks.exe /create /TN SampleTask /TR " +
Directory.GetCurrentDirectory() + @"\SampleTask.bat ";
// TODO 必要に応じて、ここでcommandに色々な指定を追加する
// 登録
Process proc = new Process();
proc.StartInfo.FileName = Environment.GetEnvironmentVariable("ComSpec"); //"cmd.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.Arguments = command;
proc.Start();
var results = proc.StandardOutput.ReadToEnd();
// TODO 必要ならresultsからログ出力とか
proc.WaitForExit();
proc.Close();
// 完了通知
MessageBox.Show("登録しました");
さて。
これだと、ログインしている状態じゃないと動きません。
それでもいいなら、十分です。
XMLを利用して、登録してみる
「いちいち色々な設定を記載して登録するのとか面倒じゃない?」
ということで、一度テンプレートとなるようなXMLを作成してからタスクを登録する方法です。
テンプレートとなるXMLの作成
- 普通にWindowsのタスクスケジューラを起動し、適当なタスクを作成します。
- 作成したタスクを選択して、右クリックなり、画面右なりから「エクスポート」を選択します。
- 作成したタスクを削除します。
登録
XMLをタスクスケジューラに登録します。
といっても、command部分を以下のようにするだけです。
var command = "/c schtasks.exe /create /XML " + Path.Combine(Environment.CurrentDirectory, "template.xml") + " /TN SampleTask";
仮に、テンプレートを読み込んで編集したい場合があれば、XMLを読み込んだ後、追加したり変更したりします。
XmlDocument document = new XmlDocument();
document.Load(inputXmlPath); // テンプレートとなるXMLをロード
var task = document.GetElementsByTagName("Task")[0];
DateTime now = DateTime.Now;
// 今日の日付
var date = document.GetElementsByTagName("Date")[0];
date.InnerText = now.ToString("yyyy-MM-ddTHH:mm:ss");
// Author
var author = document.GetElementsByTagName("Author")[0];
author.InnerText = $@"{Environment.UserDomainName}\{Environment.UserName}";
// バッチのパス
var command = document.GetElementsByTagName("Command")[0];
command.InnerText = Directory.GetCurrentDirectory() + @"\SampleTask.bat";
// todo などなど適当にXMLに追加したり、変更したりする
// 保存
document.Save(outputXmlPath); // テンプレートに上書きでもいいが、他の名前で保存しておく(つまり、登録時のパスはoutputXmlPathを使う)
XMLを利用して、SYSTEMユーザで登録してみる
サインアウト時にも動くようにしたいので、登録時のユーザをSYSTEMにしてみます。
"schtasks.exe /create /XML " + Path.Combine(Environment.CurrentDirectory, "sample.xml") + " /TN SampleTask /RU "" /RP ""
これで実行ユーザがSYSTEM(たぶん、最上位)になる。
ただし、ツールを起動したときのアカウントの権限次第ですが、タスクが登録されているかどうかの確認でエラーになります。
ITaskService taskservice = null;
Boolean exist = false;
try
{
taskservice = new TaskScheduler.TaskScheduler();
taskservice.Connect(null, null, null, null);
ITaskFolder containingFolder = taskservice.GetFolder("\\");
// 存在確認
containingFolder.GetTask("SampleTask");
exist = true;
}
catch (Exception ex)
{
// こっちに来る
exist = false;
}
finally
{
if (taskservice != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(taskservice);
}
}
return exist;
タスクが登録されているかどうかを判定する必要がないなら、このままでもOKです。
XMLを利用して、ユーザにIDとパスを入力させつつ登録してみる
色々こねくり回してます。
前提として、エクスポートするタスクの設定は、最低でも以下のようにしています。
まず、バッチファイルをもう一つ作ります。
保存パスを動的に変更したいなら、バッチファイルを動的に作ります。
string str =
"echo off" + Environment.NewLine +
"set USR_INPUT_STR=" + Environment.NewLine +
"set /P USR_INPUT_STR=\"ユーザIDを入力してください: \"" + Environment.NewLine +
"schtasks /create /XML " + Path.Combine(Environment.CurrentDirectory, "template.xml") + " /TN SampleTask /RU %USR_INPUT_STR% /RP \"\"" + Environment.NewLine +
"timeout 5 /nobreak"; // 最後5秒待っているのは趣味(すぐ閉じないで、結果を見たかったから)
string outputBatPath = Path.Combine(Environment.CurrentDirectory, @"Register.bat"); // 保存先のパス
StreamWriter sw = new StreamWriter(
outputBatPath,
false,
Encoding.GetEncoding("shift_jis"));
// 内容を書き込む
sw.Write(str);
// 閉じる
sw.Close();
次に、Processも少し設定をいじります。
string outputBatPath = Path.Combine(Environment.CurrentDirectory, @"Register.bat"); // 保存先のパス
Process proc = new Process();
proc.StartInfo.Verb = "RunAs";
proc.StartInfo.FileName = Environment.GetEnvironmentVariable("ComSpec");;
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.Arguments = "/c " + outputBatPath;
try
{
proc.Start();
proc.WaitForExit();
proc.Close();
}
catch (System.ComponentModel.Win32Exception)
{
//「ユーザーアカウント制御」ダイアログでキャンセルされたなどによって
//起動できなかった時
return true;
}
return false;
この方法をとると、ユーザIDとパスワードをユーザに入力させることができます。
とはいえ、逆に管理アカウントのパスワードを知らないとダメだったりするので、他のを使った方が良い場合もあると思います。
とりあえず、私がやりたかったことはこれで実現できたということで。