我這個方案(C#操作)是徹底解決【從Windows服務啟動程式exe,程式無介面】問題的終極解決方案,終極方案,絕對的終極方案,本來打算收錢的,還是算了,你們也不容易,關注我一下就行。後附程式碼下載地址。
由於安全性問題,Vista以後的Windows都會出現該問題,從服務中呼叫/啟動其他程式出現無介面,但是工作管理員中可以看到已經成功啟動,就是無操作介面,具體出現該狀況的原因大家自行搜尋。我的方法絕對可行。
網上有各種各樣的方案,絕大部分都有一樣的,都是是呼叫系統API,CreateProcess之類的API,然並卵,並不能徹底解決。
我的方案極其簡單而不粗暴,反而優美。
我的方案是使用計劃任務功能啟動指定程式。任務計劃的啟動不受服務限制,和服務的邊界不太一樣。不需要特別多的程式碼就可以實現,其實就是實現新增任務計劃,簡單吧。API方式,光結構和偵錯就夠你們喝一壺了,還得通過其他API呼叫和設定其他資訊,比如建立和複製現有執行令牌(DuplicateTokenEx方法),實現這功能粗暴得狠。我這個不用,啥都不用。
建立任務寥寥十幾行程式碼,優美得狠。
實現過程即任務計劃實現過程,C#有3種方法,其實就是2種,一種是使用API建立任務,這個方法其實,可以通過系統呼叫現有dll庫實現,最後一種是使用開源庫。建議用開源庫方式。
呼叫系統的dll,這dll就是C:\Windows\System32\taskschd.dll,在C#裡直接參照就行,它實現API的C#封裝,很簡單。使用TaskSchedulerClass類連線、建立修改任務計劃,很簡單,我這不是主推方法,不貼程式碼,但原始碼地址裡有。
使用開源庫TaskScheduler,可以實現,名稱空間為Microsoft.Win32.TaskScheduler,下載地址為:https://github.com/dahall/TaskScheduler。例子為:https://github.com/dahall/TaskScheduler/wiki/Examples
public static void AddOrRunWinTask( string sTaskName, string sExePath, string sArgs = null ) { var task = TaskService.Instance.FindTask(sTaskName, true); if ( task != null ) { task.Definition.Triggers[0].StartBoundary = DateTime.Now.AddSeconds ( 10 ); task.RegisterChanges (); } else { var td = TaskService.Instance.NewTask (); td.RegistrationInfo.Author = "白羊佐CSDN"; td.RegistrationInfo.Description = "用於跨域啟動特定程式"; td.Settings.ExecutionTimeLimit = TimeSpan.Zero;// td.Settings.DisallowStartIfOnBatteries = false; td.Settings.RunOnlyIfIdle = false; td.Settings.RunOnlyIfNetworkAvailable = false; //此處注意,如果你待啟動程式需要管理員許可權執行,必須使用Highest,否則使用LUA就行 td.Principal.RunLevel = TaskRunLevel.Highest; //獲取Administrators的GroupID string sGpId = GetGroupID(); //此處最為關鍵,如果不指定使用者名稱ID或組名ID,依舊不顯示介面,因為建立時的使用者為SYSTEM td.Principal.GroupId = sGpId; var trigger = (TimeTrigger)td.Triggers.Add( new TimeTrigger() ); trigger.StartBoundary = DateTime.Now.AddSeconds ( 10 ); trigger.ExecutionTimeLimit = TimeSpan.Zero; trigger.Enabled = true; td.Actions.Add ( new ExecAction ( sExePath, sArgs ) ); task = TaskService.Instance.RootFolder.RegisterTaskDefinition ( sTaskName, td ); } //開啟表示立即執行(切執行兩次,因為上面有個執行延時) //var rz = task.Run (); }
注意,注意,再注意:A、此方法是win10的,因為win10預設遮蔽Administrator使用者,我使用者屬於這個組,所以我這個地方使用這種方式沒有問題。但是,其他非Administrators組使用者登入可能不行了,那也好解決,將下面程式碼中GroupPrincipal改為UserPrincipal,用它去找登入的使用者名稱,上面程式碼設userid,logontype就行。或者輸入正確的組名都可以;B、此方式程式的啟動位置為System32,你程式的目錄獲取時要注意了。
private static string GetGroupID () { string sGid = null; System.DirectoryServices.AccountManagement.PrincipalContext pc = new System.DirectoryServices.AccountManagement.PrincipalContext(System.DirectoryServices.AccountManagement.ContextType.Machine); var identity = System.DirectoryServices.AccountManagement.GroupPrincipal.FindByIdentity(pc, "Administrators"); if ( identity != null ) { sGid = identity.Sid.Value; } return sGid; }
https://files.cnblogs.com/files/ZoeWong/TaskScheduler.2.10.1%E5%8C%85.rar?t=1688102826&download=true
哈哈,這個方法怎麼樣。徹底麼?