背景

最近做了一个winform的程序,程序中有个设置,可以设置开机自动启动,本来采用的是注册表启动方式,在xp系统上测试没有问题,但是在自己的win10电脑上就不行。

后来查资料发现程序需要用管理员权限才能写入到注册表,非管理员权限不行,而我自己的电脑是用的非管理员账户登陆的,切到管理员账户之后,发现可以启动。于是继续查资料,强制程序以管理员身份运行可以添加一个清单文件,再将其中requestedExecutionLevel节点中的level修改成requireAdministrator即可,这样每次双击运行程序的时候就会弹出UAC提示以管理员权限运行。

做到这里的时候,以为问题已经解决,但是当我使用非管理员账户重启电脑的时候,发现程序并没有随之启动,看了一下注册表、任务管理器的启动项等,甚至是腾讯的电脑管家里面都设置了开机启动,真是百思不得其解。无奈继续查资料,后来发现虽然已经写入到注册表,但是还是因为非管理员账户没有权限来执行注册表中的启动项导致的。

方法一:使用其它进程来启动

后来查资料,发现一个方法,启动的时候判断是否是管理员权限。如果是管理员权限,则直接启动程序;如果非管理员权限,就先创建一个不需要管理员权限的进程,然后用这个进程来以管理员身份打开程序。但是会出现一个问题,每次自动启动的时候,都会弹出请求管理员权限,需要点一下才能启动。这还算哪门子的自动启动,所以该方法pass。

以下是判断当前权限的方法:

public static bool IsAdministrator(){ WindowsIdentity identity = WindowsIdentity.GetCurrent(); WindowsPrincipal principal = new WindowsPrincipal(identity); return principal.IsInRole(WindowsBuiltInRole.Administrator);}

方法二:使用任务计划

无奈之下继续查资料。后来发现有人提出可以创建一个Task Scheduler(任务计划)来实现,因为它可以以管理员权限启动程序,并可以跳过UAC,顿时又找到了方向。后来测试发现确实可以,但是在XP系统上测试的时候,发现无法创建任务计划,只能在Windows Vista之后的版本(如win7、win10等)才可以 ,因为从 Windows Vista之后才开始出现了UAC这个东西。所以最后采用的方案是:XP系统继续采用注册表启动的方式,Windows Vista之后的系统则采用任务计划来实现。

项目使用的是C#语言,如果要使用任务计划,需要添加DLL的引用。位置在COM中,TaskScheduler 1.1类型库。

代码如下:

/// <summary>/// 任务计划类/// </summary>public class TaskSchedulerHelper{ /// <summary> /// 删除任务计划 /// </summary> /// <param name="taskName">任务名称</param> public static void DeleteTask(string taskName) {  TaskSchedulerClass taskScheduler = new TaskSchedulerClass();  taskScheduler.Connect(null, null, null, null);  ITaskFolder folder = taskScheduler.GetFolder(@"\");  folder.DeleteTask(taskName, 0); } /// <summary> /// 获取所有已注册的任务计划 /// </summary> /// <returns>返回所有注册任务</returns> public static IRegisteredTaskCollection GetAllRegisteredTasks() {  TaskSchedulerClass taskScheduler = new TaskSchedulerClass();  taskScheduler.Connect(null, null, null, null);  ITaskFolder folder = taskScheduler.GetFolder(@"\");  IRegisteredTaskCollection registeredTasks = folder.GetTasks(1);  return registeredTasks; } /// <summary> /// 判断任务计划是否存在 /// </summary> /// <param name="taskName">任务名称</param> /// <returns>返回任务检查结果</returns> public static bool IsExists(string taskName) {  IRegisteredTaskCollection registeredTasks = GetAllRegisteredTasks();  return registeredTasks.Cast<IRegisteredTask>().Any(task => task.Name.Equals(taskName)); } /// <summary> /// 创建任务计划 /// </summary> /// <param name="options">任务计划触发条件</param> /// <param name="triggerSet">触发器其它设置</param> /// <returns></returns> public static IRegisteredTask CreateTaskScheduler(TaskTriggerOptions options, ITriggerSet triggerSet = null) {  try  {   if (IsExists(options.TaskName))   {    DeleteTask(options.TaskName);   }   //新的任务调度器   TaskSchedulerClass scheduler = new TaskSchedulerClass();   //pc-name/ip,username,domain,password   scheduler.Connect(null, null, null, null);   //设置基础属性   ITaskDefinition task = scheduler.NewTask(0);   task.RegistrationInfo.Author = options.Creator; //创建者   task.RegistrationInfo.Description = options.Description; //描述   task.Principal.RunLevel = _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST; //使用最高权限运行   //设置触发器   var trigger = task.Triggers.Create((_TASK_TRIGGER_TYPE2) options.TaskTriggerType);   trigger.Repetition.Interval = options.Interval;   trigger.Enabled = true;   trigger.StartBoundary = options.StartBoundary;   trigger.EndBoundary = options.EndBoundary;   triggerSet?.Set(trigger);   //设置操作   IExecAction action = (IExecAction) task.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC);   action.Path = options.ActionPath; //计划任务调用的程序路径   action.Arguments = options.ActionArg;   //设置   task.Settings.ExecutionTimeLimit = "PT0S"; //运行任务时间超时停止任务吗? PTOS 不开启超时   task.Settings.DisallowStartIfOnBatteries = false; //只有在交流电源下才执行   task.Settings.RunOnlyIfIdle = false; //仅当计算机空闲下才执行   //调度程序的位置   ITaskFolder folder = scheduler.GetFolder(@"\");   IRegisteredTask regTask = folder.RegisterTask.........