LoginSignup
7
7

More than 3 years have passed since last update.

C#でタスクスケジューラに登録しようとした話

Posted at

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の作成

  1. 普通にWindowsのタスクスケジューラを起動し、適当なタスクを作成します。
  2. 作成したタスクを選択して、右クリックなり、画面右なりから「エクスポート」を選択します。
  3. 作成したタスクを削除します。

登録

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とパスを入力させつつ登録してみる

色々こねくり回してます。

前提として、エクスポートするタスクの設定は、最低でも以下のようにしています。

タスク設定例.png

まず、バッチファイルをもう一つ作ります。
保存パスを動的に変更したいなら、バッチファイルを動的に作ります。

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とパスワードをユーザに入力させることができます。
とはいえ、逆に管理アカウントのパスワードを知らないとダメだったりするので、他のを使った方が良い場合もあると思います。

とりあえず、私がやりたかったことはこれで実現できたということで。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7