Windows服務啟動exe無介面終極解決方案

2023-06-30 15:00:37

 

 

1、前言

我這個方案(C#操作)是徹底解決【從Windows服務啟動程式exe,程式無介面】問題的終極解決方案,終極方案,絕對的終極方案,本來打算收錢的,還是算了,你們也不容易,關注我一下就行。後附程式碼下載地址。

由於安全性問題,Vista以後的Windows都會出現該問題,從服務中呼叫/啟動其他程式出現無介面,但是工作管理員中可以看到已經成功啟動,就是無操作介面,具體出現該狀況的原因大家自行搜尋。我的方法絕對可行。

2、網上方案

網上有各種各樣的方案,絕大部分都有一樣的,都是是呼叫系統API,CreateProcess之類的API,然並卵,並不能徹底解決。

3、我的方案

我的方案極其簡單而不粗暴,反而優美。

3.1、簡單不粗暴


我的方案是使用計劃任務功能啟動指定程式。任務計劃的啟動不受服務限制,和服務的邊界不太一樣。不需要特別多的程式碼就可以實現,其實就是實現新增任務計劃,簡單吧。API方式,光結構和偵錯就夠你們喝一壺了,還得通過其他API呼叫和設定其他資訊,比如建立和複製現有執行令牌(DuplicateTokenEx方法),實現這功能粗暴得狠。我這個不用,啥都不用。

3.2、優美

建立任務寥寥十幾行程式碼,優美得狠。

4、實現過程


實現過程即任務計劃實現過程,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;
    }

5、工具及程式碼下載地址

    https://files.cnblogs.com/files/ZoeWong/TaskScheduler.2.10.1%E5%8C%85.rar?t=1688102826&download=true

6、收尾

  哈哈,這個方法怎麼樣。徹底麼?