資料:https://vilic.info/archives/785
背景
最近做了一個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.RegisterTaskDefinition(options.TaskName, task,
(int) _TASK_CREATION.TASK_CREATE, null, //user
null, // password
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN);
return regTask;
}
catch (Exception e)
{
throw e;
}
}
}
任務計劃觸發條件類:
/// <summary>
/// 任務計劃觸發條件
/// </summary>
public class TaskTriggerOptions
{
/// <summary>
/// 任務名稱
/// </summary>
public string TaskName { get; set; }
/// <summary>
/// 創建者
/// </summary>
public string Creator { get; set; }
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; }
/// <summary>
/// 重復任務間隔
/// <remarks>format PT1H1M==1小時1分鐘 設定的值最終都會轉成分鐘加入到觸發器</remarks>
/// </summary>
public string Interval { get; set; }
/// <summary>
/// 開始時間
/// </summary>
public string StartBoundary { get; set; }
/// <summary>
/// 結束時間
/// </summary>
public string EndBoundary { get; set; }
/// <summary>
/// 操作要執行的程式路徑
/// </summary>
public string ActionPath { get; set; }
/// <summary>
/// 添加引數
/// </summary>
public string ActionArg { get; set; }
/// <summary>
/// 觸發型別
/// </summary>
public TaskTriggerType TaskTriggerType { get; set; }
}
觸發型別:
public enum TaskTriggerType
{
TASK_TRIGGER_EVENT = 0,
TASK_TRIGGER_TIME = 1,
TASK_TRIGGER_DAILY = 2,
TASK_TRIGGER_WEEKLY = 3,
TASK_TRIGGER_MONTHLY = 4,
TASK_TRIGGER_MONTHLYDOW = 5,
TASK_TRIGGER_IDLE = 6,
TASK_TRIGGER_REGISTRATION = 7,
TASK_TRIGGER_BOOT = 8,
TASK_TRIGGER_LOGON = 9,
TASK_TRIGGER_SESSION_STATE_CHANGE = 11,
TASK_TRIGGER_CUSTOM_TRIGGER_01 = 12
}
使用方式:
/// <summary>
/// 設定自動啟動
/// </summary>
/// <param name="isAutoRun"></param>
public static void AutoRun(bool isAutoRun)
{
try
{
if (HasUAC())
{
TaskSchedulerStart(isAutoRun);
}
else
{
RegeditStart(isAutoRun);
}
}
catch (Exception ex)
{
LogUtils.Error("設定自動啟動失敗,", ex);
}
}
/// <summary>
/// 判斷當前系統是否有UAC,WindowsVista(主版本號為6)之前的系統沒有UAC
/// </summary>
/// <returns></returns>
private static bool HasUAC()
{
OperatingSystem osInfo = Environment.OSVersion;
int versionMajor = osInfo.Version.Major;
return versionMajor >= 6;
}
/// <summary>
/// 注冊表啟動方式
/// WindowsVista之前版本使用注冊表啟動
/// </summary>
/// <param name="isAutoRun"></param>
private static void RegeditStart(bool isAutoRun)
{
string filefullpath = Application.ExecutablePath;
string appName = Path.GetFileNameWithoutExtension(filefullpath);
string regPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
RegistryKey _rlocal = Registry.LocalMachine.OpenSubKey(regPath, true);
if (_rlocal == null) _rlocal = Registry.LocalMachine.CreateSubKey(regPath);
if (isAutoRun)
{
_rlocal.SetValue(appName, string.Format(@"""{0}""", filefullpath));
}
else
{
_rlocal.DeleteValue(appName, false);
}
_rlocal.Close();
}
/// <summary>
/// 任務計劃啟動
/// </summary>
/// <param name="isAutoRun"></param>
private static void TaskSchedulerStart(bool isAutoRun)
{
if (isAutoRun)
{
var options = new TaskTriggerOptions
{
TaskName = "任務名稱",
Creator = "創建者",
Description = "任務描述",
ActionPath = Application.ExecutablePath,//應用程式路徑
TaskTriggerType = TaskTriggerType.TASK_TRIGGER_LOGON//任務觸發方式
};
TaskSchedulerHelper.CreateTaskScheduler(options);
}
else
{
if (TaskSchedulerHelper.IsExists(TaskSchedulerName))
TaskSchedulerHelper.DeleteTask(TaskSchedulerName);
}
}
其它設定:
CreateTaskScheduler(TaskTriggerOptions options, ITriggerSet triggerSet = null)方法中只是做了最基礎的配置,不同的觸發方式,觸發器的配置可能不同,針對不同的觸發設定,擴展了一個介面,如果有特殊設定時傳入一個ITriggerSet 的實體即可,
/// <summary>
/// 觸發器其它設定介面
/// </summary>
public interface ITriggerSet
{
// 觸發器設定 ITrigger:當前的觸發器
void Set(ITrigger trigger);
}
例如:
/// <summary>
/// 登陸時觸發其它設定
/// </summary>
public class LogonTriggerSet : ITriggerSet
{
public void Set(ITrigger trigger)
{
//TODO:設定其它設定
}
}
方法三:使用Topshelf來創建一個服務
還有人提過使用Topshelf來創建一個服務,本人沒有親自測驗,感覺方法應該也是可行的,如果有興趣的朋友可以自己試一下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/127.html
標籤:WinForm
上一篇:C# WinForm捕獲全域例外
