質問するログイン新規登録

Q&A

解決済

2回答

2796閲覧

C#で特定のプロセスを検索してkillしたい

shimeji_XX

総合スコア42

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

1クリップ

投稿2023/06/15 02:36

0

1

実現したいこと

ローカルネットワークでhttp-serverを実装しています。
基本的にhttp-serverのプロセスは常時起動したままですが、
serverプロセスに不具合が発生した際にユーザーさん側からサーバーの再起動ができるようにしたいです。

前提

C# のWindowsフォームアプリケーションを使用しています。
プロセスに異常が発生してアクセス不能になった場合等に、ユーザーさんがプロセスを再起動できるようにしたいです。
ポート番号8080を使用しているserverプロセスを特定して
①起動しているならkill⇒プロセス起動
②していないならプロセス起動
としたいです。

発生している問題・エラーメッセージ

プロセスが特定できない。

エラーメッセージ http-serverプロセスは見つかりませんでした。 というメッセージが出る。

該当のソースコード

C#

1//* 2//* http-serverの起動 3//* 4 // http-serverの起動コマンドを作成 5 string httpServerCommand = @"http-server C:\\test_http -p 8080 -d=false"; 6 7 // プロセス情報を設定 8 ProcessStartInfo startInfo = new ProcessStartInfo(); 9 startInfo.FileName = "cmd.exe"; 10 startInfo.Arguments = $"/c {httpServerCommand}"; 11 startInfo.UseShellExecute = false; 12 startInfo.CreateNoWindow = true; 13 14 // 外部プロセスを起動 15 httpServerProcess = new Process(); 16 httpServerProcess.StartInfo = startInfo; 17 httpServerProcess.EnableRaisingEvents = true; 18 httpServerProcess.Start(); 19 20//* 21//*http-serverを8080ポートで検索して停止させる 22//* 23private void StopHttpServerProcess() 24{ 25 int portNumber = 8080; 26 Process existingProcess = GetProcessByPort(portNumber); 27 if (existingProcess != null) 28 { 29 existingProcess.Kill(); 30 existingProcess.WaitForExit(); 31 32 Console.WriteLine("http-serverプロセスを停止しました。"); 33 } 34 else 35 { 36 Console.WriteLine("http-serverプロセスは見つかりませんでした。"); 37 } 38} 39 40private Process GetProcessByPort(int port) 41{ 42 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); 43 IPEndPoint[] tcpEndpoints = ipProperties.GetActiveTcpListeners(); 44 45 foreach (IPEndPoint endpoint in tcpEndpoints) 46 { 47 if (endpoint.Port == port) 48 { 49 int processId = GetProcessIdByPort(endpoint); 50 if (processId != -1) 51 { 52 return Process.GetProcessById(processId); 53 } 54 } 55 } 56 57 return null; 58} 59 60private int GetProcessIdByPort(IPEndPoint endpoint) 61{ 62 IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties(); 63 TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections(); 64 65 foreach (TcpConnectionInformation connection in tcpConnections) 66 { 67 if (connection.LocalEndPoint.Equals(endpoint)) 68 { 69 return connection.RemoteEndPoint.Port; 70 } 71 } 72 73 return -1; 74}

試したこと

1.cmd.exeで起動する際に識別子をつけてみる
⇒識別子の取得が出来ず、判定できない
2.port番号を指定して検索
⇒見つからない

補足情報(FW/ツールのバージョンなど)

・Visual Studio 2019
ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

YAmaGNZ

2023/06/15 03:42

http-serverが複数起動しないならプロセス名で探してもいいのでは?
Zuishin

2023/06/15 04:25

自作サーバーなら、特定のリクエストを受け付けた時に終了するように作っておけば良いかと。
shimeji_XX

2023/06/15 06:07

>>http-serverが複数起動しないならプロセス名で探してもいいのでは? 回答ありがとうございます。その方法も試してみたのですがprocess.ProcessNameやprocess.MainModule.FileNameが上手く参照できないんですよね…。アクティブなプロセスIDを取得してGetProcessByIdで引っ張て来ているだけなんですけど…。 >>自作サーバーなら、特定のリクエストを受け付けた時に終了するように作っておけば良いかと。 回答ありがとうございます。なんらかの異常でhttp-serverにアクセスやリクエストが飛ばない状態になった時にプロセスを消す手段が欲しかったんです。遠方のユーザーさんにローカル環境で使用してもらうプログラムなので、自分が十分にサポートできそうにないんですよね…。PC再起動すればプロセス切れるんですけど、アプリ上からもプロセスの再起動ができればユーザーフレンドリーかなと思いまして…。
YAmaGNZ

2023/06/15 06:45

プロセス名で探すのがありならProcess.GetProcessesByNameで引っ張ればいいのでは?
hqf00342

2023/06/15 07:08 編集

GetProcessIdByPort()の実装がプロセスIDを返しておらず、クライアントのポート番号を返しています。 WindowsならIPHelperのGetExtendedTcpTable()等を使って探すのがご質問の答えだと思いますが この目的なら皆さんが言われるようにプロセス名を探すほうが速いと思います。
Zuishin

2023/06/15 07:09

kill を仕様にしてしまうと、ファイルの書き込みとかできなくなるのであまり好ましくないです。 サービスとして作成すれば、名前を指定して停止することも、停止前に何かの処理をすることもできるので、そちらをおすすめします。 サービスにできない時は、名前付きパイプなどを使ってプロセス間通信し、自分で停止させるのがおすすめです。
shimeji_XX

2023/06/15 07:16

>>YAmaGNZさん >>hqf00342さん 回答ありがとうございます。 上記のプログラムの通り // プロセス情報を設定 ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = "cmd.exe"; startInfo.Arguments = $"/c {httpServerCommand}"; startInfo.UseShellExecute = false; startInfo.CreateNoWindow = true; のようにしてプロセスを開始しています。 プロセス名で検索するのがありというか、http-serverのプロセスを特定できればどのような方法でも構いません。 この場合プロセス名はcmdになると思うのですが、この認識であっていますでしょうか? cmdで探した場合他のプロセスも停止させることになってしまうため、識別子付きで起動してhttp-serverだけ停止しようとしたのですが、識別子をうまく取得できずに断念しました。
shimeji_XX

2023/06/15 07:47

>>Zuishinさん サービスとして登録ですか… サービスとして登録する方法で実装できるか試してみます。回答ありがとうございます。
hqf00342

2023/06/15 08:18

cmd経由で起動してもプロセスはあるはずです。 http-serverが動いている環境でタスクマネージャーや簡単なテストコードでみるとよいと思います。 var plist = Process.GetProcesses(); //又は var plist = Process.GetProcessesByName("http-server"); foreach(var p in plist) { Console.WriteLine($"{p.Id} {p.ProcessName}"); } ただ、親cmdも一緒に消したいとなると面倒ですね。 cmd経由でプロセス開始する必要がなければ直接起動したほうが良い気もします。
shimeji_XX

2023/06/16 00:14

>>hqf00342さん 回答ありがとうございます。 http-serverの起動前と後のすべてのプロセスを見比べると以下のプロセスが追加されていました。 nodeというのがおそらくhttp-serverのプロセスです。 cmd経由せずに起動が難しそうなのでどうするべきか迷っています…。 Process ID: 4292, Name: cmd Process ID: 8044, Name: conhost Process ID: 8896, Name: SearchProtocolHost Process ID: 9080, Name: ServiceHub.TestWindowStoreHost Process ID: 9804, Name: node
hqf00342

2023/06/16 10:38 編集

http-server周りの情報がないので不確かですがnodeをkillすればよいと思います。 nodeが複数あって特定できなかったり、親プロセスのcmdもkillしたいのであればwmiを利用して探すことになると思います。 WMIで親プロセスIDとコマンドラインを表示するサンプルを書いておきます(System.Managementの参照、またはnugetからインストールが必要) using System.Management; var mos = new ManagementObjectSearcher(); mos.Query.QueryString = "select * from win32_process"; var result = mos.Get(); foreach (var obj in result) { Console.WriteLine($"pid={obj["ProcessId"],5} ppid={obj["ParentProcessId"],5} name={obj["Name"],16} cmd={obj["commandline"]}"); } プロセス名が分かるならこれらでプロセス特定すれば十分だと思いますが、必要であらばLISTENポート番号からプロセスIDを探す部分を回答に書きます(長いです)。
shimeji_XX

2023/06/19 00:43

返信が遅れ申し訳ございません。 こちらで確認してみたところ、nodeのプロセスをkillすると親プロセスも消えているようです。 とりあえずこれで動かしてみます。 今回の自分の環境では大丈夫ですが、この方法の場合他のnodeで動いているプログラムも削除してしまうため後ほど技術的負債になりそうです。 他の方がおっしゃられていたサービスを使う方法も含めてもう一度考えてみたいと思います。 対応ありがとうございました。
guest

回答2

0

自己解決

C#

1 2 //指定されたプロセス名を検索して全て削除する関数 3 public static void KillProcessesByName(string processName) 4 { 5 // 指定したプロセス名を持つすべてのプロセスを取得 6 Process[] processes = Process.GetProcessesByName(processName); 7 8 // 各プロセスを終了させる 9 foreach (Process process in processes) 10 { 11 try 12 { 13 process.Kill(); 14 process.WaitForExit(); // プロセスの終了を待機 15 Console.WriteLine($"Process '{process.ProcessName}' with ID {process.Id} killed."); 16 } 17 catch (Exception ex) 18 { 19 Console.WriteLine($"Failed to kill process '{process.ProcessName}' with ID {process.Id}: {ex.Message}"); 20 } 21 } 22 }

質問欄での提案の結果、とりあえず名前で検索してkillすることになりました。
同名プロセスが存在している場合そのプロセスも終了しますのでご注意ください。
サービスとして登録した方が丸いという意見も頂きましたが、まだ試せていないので
とりあえずこの質問は上記関数を使用してプロセス名でkillすることで終了とします。
BAについては質問欄をBAと出来ないため、自己解決として投稿します。
詳細については質問欄の閲覧をお願いします。
情報をくださった方々、ありがとうございました。

投稿2023/06/19 07:41

shimeji_XX

総合スコア42

0

8080番とは、待ち受けTCP portなわけで、使うのはGetActiveTcpListenersの方では。

投稿2023/06/15 03:13

matukeso

総合スコア1689

shimeji_XX

2023/06/15 05:07

回答ありがとうございます。 自分の理解が間違っていたら申し訳ございません。 GetActiveTcpListenersとはアクティブなポート(待ち受けTCP port)を検出するという認識で間違いないでしょうか? とりあえずGetActiveTcpListenersで8080ポートがアクティブかを取得⇒ポート番号から対応するプロセスID(60989)を取得⇒GetProcessByIdを使用してプロセスIDからプロセスの情報を取得 という形で試してみたのですが、最後のGetProcessById(60989) で「プロセスが見つかりませんでした。」と出る状態です。
matukeso

2023/06/15 06:52

サーバは高位権限で動いていで、この監視ツールが制限ユーザーとかではないですよね?
shimeji_XX

2023/06/15 07:31

回答ありがとうございます。 現状Visual Studio上でテスト的に実装している状態ですので、http-serverはPC上のプロセスで動いています。実行しているユーザーも管理者としての権限を持っています。visualstudio自体も管理者権限で動いているのは確認しました。うーん、もう一度権限周りを確認してみます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問