Windowsタスクスケジューラ(ネイティブ)
C ++。COMプログラミングの悪夢。すべてのチャレンジ要件を満たしています。
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <taskschd.h>
#include <comutil.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsuppw.lib")
static void timeplus (int seconds, char timestr[30]);
int main () {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
const char *name = "Restarter";
char path[MAX_PATH + 1];
GetModuleFileNameA(NULL, path, sizeof(path));
path[sizeof(path) - 1] = 0; // workaround for xp
ITaskService *taskman;
CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
IID_ITaskService, (void **)&taskman);
taskman->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ITaskFolder *root;
taskman->GetFolder(_bstr_t("\\"), &root);
// Delete the task.
root->DeleteTask(_bstr_t(name), 0);
// pause for 5 seconds to give user a chance to kill the cycle
fprintf(stderr, "If you want to kill the program, close this window now.\n");
Sleep(5000);
fprintf(stderr, "Sorry, time's up, maybe next time.\n");
// Create the task for 5 seconds from now.
ITaskDefinition *task;
taskman->NewTask(0, &task);
IPrincipal *principal;
task->get_Principal(&principal);
principal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
ITaskSettings *settings;
task->get_Settings(&settings);
settings->put_StartWhenAvailable(VARIANT_TRUE);
settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
settings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
ITriggerCollection *triggers;
task->get_Triggers(&triggers);
ITrigger *trigger;
triggers->Create(TASK_TRIGGER_TIME, &trigger);
char when[30];
ITimeTrigger *trigger_time;
trigger->QueryInterface(IID_ITimeTrigger, (void **)&trigger_time);
trigger_time->put_Id(_bstr_t("TimeTrigger"));
timeplus(10, when);
trigger_time->put_StartBoundary(_bstr_t(when));
timeplus(300, when);
trigger_time->put_EndBoundary(_bstr_t(when));
IActionCollection *actions;
task->get_Actions(&actions);
IAction *action;
actions->Create(TASK_ACTION_EXEC, &action);
IExecAction *action_exec;
action->QueryInterface(IID_IExecAction, (void **)&action_exec);
action_exec->put_Path(_bstr_t(path));
IRegisteredTask *regtask;
root->RegisterTaskDefinition(_bstr_t(name), task,
TASK_CREATE_OR_UPDATE, _variant_t(), _variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN, _variant_t(""),
®task);
regtask->Release();
action_exec->Release();
actions->Release();
trigger_time->Release();
trigger->Release();
triggers->Release();
settings->Release();
principal->Release();
task->Release();
root->Release();
taskman->Release();
CoUninitialize();
}
// outputs current utc time + given number of seconds as
// a string of the form YYYY-MM-DDTHH:MM:SSZ
static void timeplus (int seconds, char timestr[30]) {
SYSTEMTIME when;
FILETIME whenf;
LARGE_INTEGER tempval;
GetSystemTimeAsFileTime(&whenf);
tempval.HighPart = whenf.dwHighDateTime;
tempval.LowPart = whenf.dwLowDateTime;
tempval.QuadPart += seconds * 10000000LL; // 100 nanosecond units
whenf.dwHighDateTime = tempval.HighPart;
whenf.dwLowDateTime = tempval.LowPart;
FileTimeToSystemTime(&whenf, &when);
sprintf(timestr, "%04hu-%02hu-%02huT%02hu:%02hu:%02huZ",
when.wYear, when.wMonth, when.wDay,
when.wHour, when.wMinute, when.wSecond);
}
MSVC(またはすべての依存関係がある場合はMinGW GCC)でコンパイルします。
プログラムは、Windowsタスクスケジューラに1回限りのタスクを開始して登録し、5秒後に開始します([コントロールパネル]-> [管理ツール]-> [表示するタスクスケジューラ。タスクの名前は「リスターター」)。プログラムは5秒間一時停止して、タスクを作成する前にプログラムを強制終了する機会を与えます。
チャレンジ要件:
終了すると、自動的に再起動します。
はい。タスクはプログラム終了の直前にスケジュールされます。
同時に実行されるプログラムのインスタンスは1つだけです。
はい。プログラムは完全に終了し、5秒間実行されません。スケジューラーによって開始されます。
サイクル中にユーザーが手動で開始したインスタンスは無視できます。
はい、一定のタスク名を使用する副作用として。
再び開始することが保証されている限り。
はい。ただし、タスクスケジューラが実行されている場合(標準のWindows構成内)。
サイクルを停止する唯一の方法は、プロセスを強制終了することです。
はい、実行中の5秒間にプロセスを強制終了できます。プログラムは、5秒の遅延の前にタスクを削除します。この時点でタスクを強制終了しても、スケジューラーに浮遊タスクが残ることはありません。
ソリューションには、環境の再起動は含まれません
はい。
ちなみに、Windowsアプリケーションが(.NETとC#が登場する前に)なぜこれほど不安定であるのかと疑問に思った人がいたなら、これが理由の1つです。必要なエラー処理の量(私がそれを含めていた)、リソース管理、および冗長性は、プログラマーがほんの少し怠けている場合でもエラーが発生しやすい状況をセットアップします(上記のコードは非常に怠zyです)。
はるかに簡単で短い代替手段は、schtasks.exeを呼び出すことです。.BATスクリプトでも同じバージョンを送信しました。
exec
は、Linuxの機能だけではありませんか?