【Unity】Windowsのサービスの状態取得やコントロールを行う
UnityからWindowsのサービスの状態を取得したり,起動,停止を行う方法が,ちょっとつまづいたのでメモ.そもそもUnityからサービスの取得を行ったりするのは需要が無いのだと思うのだが…
他に良い方法があれば誰か教えて下さい…
とりあえず完成品サンプルはここにあります.
https://github.com/nmxi/Unity_WinServiceController
Env.
Windows10 64bit
Unity2019.3.3f1
Method
今回実装した方法はコンソールアプリケーションをUnityから引数付きで呼ぶ方法.
本当はC++でWindowsのNativePluginを作成すればキレイかつ,取り回しが楽なのだと思うが,体力が今はなかった.悪しからず…(GitHubに既にある予感もする)
コンソールアプリケーションを作成した
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
using System; using using System; using System.ServiceProcess; namespace WinServiceController { class Program { static void Main(string[] args) { if (args.Length != 2) return; switch (args[0]) { case "GET": //Console.WriteLine("get service list"); GetServices(args[1]); break; case "START": //Console.WriteLine("start service"); StartService(args[1]); break; case "STOP": //Console.WriteLine("stop service"); StopService(args[1]); break; case "RESTART": //Console.WriteLine("restart service"); RestartService(args[1]); break; default: //Console.WriteLine(Environment.CommandLine + " is unknown method"); break; } } /// <summary> /// サービス一覧を取得する /// </summary> /// <param name="serviceName"></param> private static void GetServices(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; ServiceController[] scSercices; scSercices = ServiceController.GetServices(); var flag = false; foreach (var scTemp in scSercices) { if (scTemp.ServiceName == serviceName) { Console.WriteLine(scTemp.ServiceName + " STATUS:" + scTemp.Status); flag = true; } } if (!flag) Console.WriteLine("not found service : " + serviceName); } /// <summary> /// サービスを起動する /// </summary> /// <param name="serviceName"></param> private static void StartService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; try { ServiceController sc = new ServiceController(); sc.ServiceName = serviceName; if (sc.Status == ServiceControllerStatus.Stopped) { sc.Start(); sc.WaitForStatus(ServiceControllerStatus.Running); sc.Refresh(); } } catch (Exception) { Console.WriteLine("Could not start the service"); } Console.WriteLine("started " + serviceName); } /// <summary> /// サービスを停止する /// </summary> /// <param name="serviceName"></param> private static void StopService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; try { ServiceController sc = new ServiceController(); sc.ServiceName = serviceName; if (sc.Status == ServiceControllerStatus.Running) { sc.Stop(); sc.WaitForStatus(ServiceControllerStatus.Stopped); sc.Refresh(); } } catch (Exception) { Console.WriteLine("Could not stop the service"); } Console.WriteLine("Stoped " + serviceName); } /// <summary> /// サービスを再起動する /// </summary> /// <param name="serviceName"></param> private static void RestartService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; StopService(serviceName); StartService(serviceName); } } } ; namespace WinServiceController { class Program { static void Main(string[] args) { if (args.Length != 2) return; switch (args[0]) { case "GET": //Console.WriteLine("get service list"); GetServices(args[1]); break; case "START": //Console.WriteLine("start service"); StartService(args[1]); break; case "STOP": //Console.WriteLine("stop service"); StopService(args[1]); break; case "RESTART": //Console.WriteLine("restart service"); RestartService(args[1]); break; default: //Console.WriteLine(Environment.CommandLine + " is unknown method"); break; } } /// <summary> /// サービス一覧を取得する /// </summary> /// <param name="serviceName"></param> private static void GetServices(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; ServiceController[] scSercices; scSercices = ServiceController.GetServices(); var flag = false; foreach (var scTemp in scSercices) { if (scTemp.ServiceName == serviceName) { Console.WriteLine(scTemp.ServiceName + " STATUS:" + scTemp.Status); flag = true; } } if (!flag) Console.WriteLine("not found service : " + serviceName); } /// <summary> /// サービスを起動する /// </summary> /// <param name="serviceName"></param> private static void StartService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; try { ServiceController sc = new ServiceController(); sc.ServiceName = serviceName; if (sc.Status == ServiceControllerStatus.Stopped) { sc.Start(); sc.WaitForStatus(ServiceControllerStatus.Running); sc.Refresh(); } } catch (Exception) { Console.WriteLine("Could not start the service"); } Console.WriteLine("started " + serviceName); } /// <summary> /// サービスを停止する /// </summary> /// <param name="serviceName"></param> private static void StopService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; try { ServiceController sc = new ServiceController(); sc.ServiceName = serviceName; if (sc.Status == ServiceControllerStatus.Running) { sc.Stop(); sc.WaitForStatus(ServiceControllerStatus.Stopped); sc.Refresh(); } } catch (Exception) { Console.WriteLine("Could not stop the service"); } Console.WriteLine("Stoped " + serviceName); } /// <summary> /// サービスを再起動する /// </summary> /// <param name="serviceName"></param> private static void RestartService(string serviceName) { if (string.IsNullOrEmpty(serviceName)) return; StopService(serviceName); StartService(serviceName); } } } |
上記のソースを要約するとSystem.ServiceProcess.ServiceControllerを使って,サービスの取得と起動,停止を行うプログラム.
第1引数で取得なのか開始なのか停止なのかを選択し,第2引数でサービス名を与えてあげるとサービスのコントロールを行えるというものになっている.
System.ServiceProcess.ServiceController クラスがUnityのMonoBehaviour上で直接使えれば万事解決なのだが,残念ながら使えない..NETの開発者ツールの中に入っている,なんとかDLLを追加する方法もあったが,DLLが再配布禁止っぽかったので割愛.
System.ServiceProcess.ServiceController 自体はnugetパッケージでも提供されていて,ライセンスはMITなので嬉しい.
コンソールアプリケーション のビルド
ビルドして以下のような感じ.
今回このコンソールアプリケーションはWinServiceControllerと名付けた.
これであとはUnityのスクリプトから引数付きで呼んであげればOK.
UnityからWinServiceControllerを呼ぶ
コンソールアプリケーションをUnityのAssets/StreamingAssetsフォルダにいれる.
以下のスクリプトのControlServiceNameにあれこれしたいサービス名を入れて,
GetStatus()
StartService()
StopService()
を呼び出せばあれこれできる.
System.Diagnosticsクラスを使ってcmdでコマンドをたたくのと同じことを行っている.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
// WinServiceController.cs using System.Collections; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; using UnityEngine.UI; namespace nmxi.winservicecontroller { public class WinServiceController : MonoBehaviour { [SerializeField] private string ControlServiceName; [Space(15f), SerializeField] private Text statusShowText; private readonly string winServiceControllerPath = Application.streamingAssetsPath + "/WinServiceController/WinServiceController.exe"; /// <summary> /// Setting the service name of the control target. /// </summary> /// <param name="newServiceName"></param> public void SetServiceName(string newServiceName) { ControlServiceName = newServiceName; } /// <summary> /// Returns the operating status of the current service. /// </summary> public void GetStatus() { statusShowText.text = WSCGet(); } /// <summary> /// Start service. /// </summary> public void StartService() { WSCStart(); } /// <summary> /// Stop service. /// </summary> public void StopService() { WSCStop(); } #region WSCController private string WSCGet() { var p = new Process(); p.StartInfo.FileName = winServiceControllerPath; p.StartInfo.Arguments = "GET \"" + ControlServiceName + "\""; p.StartInfo.CreateNoWindow = true; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.Start(); var stdoutStr = p.StandardOutput.ReadToEnd(); p.WaitForExit(); stdoutStr = stdoutStr.Replace("\r\r\n", "\n"); p.Close(); return stdoutStr; } private void WSCStart() { var p = new Process(); p.StartInfo.FileName = winServiceControllerPath; p.StartInfo.Verb = "RunAs"; p.StartInfo.Arguments = "START \"" + ControlServiceName + "\""; p.StartInfo.CreateNoWindow = true; p.StartInfo.UseShellExecute = true; p.Start(); p.WaitForExit(); p.Close(); } private void WSCStop() { var p = new Process(); p.StartInfo.FileName = winServiceControllerPath; p.StartInfo.Verb = "RunAs"; p.StartInfo.Arguments = "STOP \"" + ControlServiceName + "\""; p.StartInfo.CreateNoWindow = true; p.StartInfo.UseShellExecute = true; p.Start(); p.WaitForExit(); p.Close(); } #endregion } } |
サービスの開始と停止は管理者権限でないとできないので注意.
上記ソースの
1 |
p.StartInfo.Verb = "RunAs"; |
を入れると管理者として実行するかダイアログがでて聞かれるようになる.
管理者権限が得られなかったときなどのエラーをCatchするようにソースを書いてないので,もう少し改良が必要なところ.
GitHub
https://github.com/nmxi/Unity_WinServiceController
ここにサンプルのプロジェクトを置きました.
ライセンスはMIT.
Unityのプロジェクトファイルとコンソールアプリケーションのソースが両方入ってます.
サンプルではInputFieldにあれこれしたいサービス名を入力してボタンを押すことで動作するようになっている.
Ref.
System.ServiceProcess.ServiceController nugetパッケージhttps://www.nuget.org/packages/System.ServiceProcess.ServiceController/