Windows Serverで手動起動したサービス(exe)が二重起動してクラッシュする原因と調査手順(ProcMon/Process Explorer)

Windows Serverで、手動起動したexe(サービス運用のアプリ)が一定時間後にもう一度起動され、二重起動が引き金になってクラッシュする――この現象は「誰が2回目を起動したか」と「親が落ちて子が残っていないか」を切り分けると、最短で原因に到達できます。

目次

この問題が起きる背景:見た目は「二重起動」でも中身は別物

「手動で NavisXPS.exe -d ... を起動したのに、しばらくすると同じexeがもう一度起動され、2回目の起動直後にアプリが落ちる」という相談は、現場では珍しくありません。ポイントは、二重起動に見えても原因は大きく次の3系統に分かれることです。

見え方よくある正体典型トリガー優先して見る場所
同じexeが2つ並んでいる監視/スケジューラ/サービス復旧が「起動」を重ねた監視の自動復旧、タスク重複、サービス再起動ProcMonのProcess Create、タスクスケジューラ履歴、サービス復旧設定
いったん消えた後に再び起動する本体(親プロセス)が落ち、復旧で再起動された例外で終了→自動再起動イベントログ(Application/System)、WER
「二重起動」後にクラッシュ子プロセスが残り、2回目が衝突して単一インスタンス前提が破綻親だけ終了、子が孤児化、ポート/ファイル/Mutex競合Process Explorerのプロセスツリー、ハンドル/ポート占有状況

つまり、最初にやるべきことは「原因の候補を増やす」ではなく、2回目の起動の親プロセス(誰が起動したか)を確定し、同時に“残ってはいけないプロセス”が残っていないか確認することです。ここが分かれば、Windows側設定の問題か、監視・運用の問題か、アプリ側の問題かを高確度で切り分けできます。

結論を早く出すための最短ルート:見るべきはこの2点

  • ProcMonで「2回目の起動」を作った親プロセス(CreateProcessの実行元)を特定する
  • Process Explorerで、親が落ちて子が残る“孤児プロセス”や関連プロセスの残骸を確認する

この2点が揃うと、次のように判断が進みます。

ProcMonで分かった「2回目の親」疑うべき領域次にやること
taskeng.exe / svchost.exe(Schedule)タスクスケジューラ同名タスク、トリガー、既に実行中の場合の動作、履歴の時刻一致を確認
services.exe / service hostWindowsサービス(復旧設定/サービス本体)services.msc / scで復旧(再起動)や失敗回数、ログの7031/7034を確認
監視エージェント(例:監視ソフトのexe)運用監視/常駐の自動復旧監視設定・復旧条件、プロセス判定の誤検知(子が残る)を確認
cmd.exe / powershell.exe / バッチ運用スクリプト/手動手順の自動化同じ起動コマンドがどこかで定期実行されていないか棚卸し
アプリ自身(自己再起動)アプリの異常系処理アプリログ・ダンプ、ベンダー問い合わせ、単一インスタンス制御の確認

調査に使うツールと、見るべき“具体的なポイント”

ツール名だけ知っていても、見方を間違えると時間だけが溶けます。以下は「二重起動」を短時間で切るための、実務での見どころです。

ツール/ログ分かることこの問題での注目点補足
Process Explorer(Sysinternals)プロセスツリー、親子関係、コマンドライン、ユーザー親が消えて子が残る/同名プロセスが複数/起動引数が同一か正常サーバーとツリー比較が効く
Process Monitor(ProcMon)プロセス生成(CreateProcess)、起動元、時刻2回目の起動の親プロセス名、起動コマンドライン、PIDの連鎖「証拠」を作れるので最重要
イベントビューア(System/Application)サービス制御、アプリ障害、再起動の痕跡Service Control Managerの失敗→再起動、Application Error時刻を2回目の起動と突き合わせ
WER(Windows Error Reporting)アプリクラッシュの障害情報、ダンプFaulting module、例外コード、クラッシュ時刻ベンダー解析に強い
タスクスケジューラ定期/イベント連動の自動起動同じexeを起動するタスクが存在しないか、既に実行中の場合の挙動履歴が無効だと追いにくい
sc / services.mscサービス登録、復旧動作、実行ユーザー「サービスとして登録されているか」「復旧で再起動になっていないか」exe直叩き運用はラッパーの可能性

原因パターンを網羅:なぜ2回起動されるのか

監視/復旧が“善意で”再起動している

二重起動の最大公約数は、止まった(または止まったように見えた)と判断され、監視や復旧が再起動をかけるパターンです。ここで重要なのは、監視側が見ているのが「本体プロセス」ではなく、サービスラッパーや親プロセスだけ、というケースがある点です。

  • 本体(親)が例外で落ちる → 監視が「停止」と判定 → 再起動 → 子が残って衝突
  • 一時的に応答が遅い/CPU高騰 → 監視が「ハング」と判定 → 強制再起動 → 二重化

監視ツールを使っている環境では、ProcMonで2回目の親に「監視エージェント名」が出ることが多く、これが出た時点で勝ち筋が見えます。

タスクスケジューラが“同じコマンド”を重ねている

「手動起動したつもり」でも、裏でタスクが同じexeを起動していると、一定時間後に2回目が発生します。特に次の設定が地雷になりがちです。

  • トリガーが複数(ログオン時+定期、起動時+毎時など)
  • 「既にタスクが実行中の場合」の設定が新しいインスタンスを並列実行になっている
  • 失敗時の再試行(再試行間隔が短い)

「本当にWindowsサービスか」を誤認している(exe直叩き運用)

NavisXPS.exe -d ... のように exe を直接叩いている場合、実態は次のどれかである可能性があります。

  • サービスではなく、常駐アプリを“サービスっぽく”運用している
  • サービスラッパー(例:nssm、WinSW、srvany等)でサービス化している
  • ベンダー独自のランチャー/デーモンが別に存在し、それが復旧を担っている

この場合、「手動起動=正規ルート」ではなく、正規の起動元(サービス/ラッパー/監視)が別にいて、しばらくしてから正規ルートが“いつも通り”起動し直し、二重起動になります。

親だけ終了し、子プロセスが残る(孤児プロセス)が原因になっている

現場で“有力”なのがこのパターンです。サービス本体(親)が落ちた・終了したのに、子プロセス(ワーカー、通信モジュール、GUIレス常駐部品など)が残ると、次のような事故が起きます。

  • 2回目が起動 → 既存の子が使っているポート/共有メモリ/ファイルを奪えず異常終了
  • 単一インスタンス前提のMutexが残っていて起動処理が例外
  • ログファイル/ロックファイルを同時に開いてクラッシュ(例外処理が甘い)

Process Explorerで見るべきは「プロセス名が残っているか」だけではありません。親子関係(ツリー)が不自然になっていないか、同じフォルダの関連プロセスが残っていないか、実行ユーザーが混在していないか、まで確認すると精度が上がります。

手動起動と自動起動で“実行ユーザー/セッション/環境変数”が違う

見落とされがちですが、二重起動の引き金として多いのが、起動経路による実行環境差です。

  • 手動起動:管理者ユーザー(対話セッション)
  • 自動起動:LocalSystemや専用サービスアカウント(非対話セッション)

これにより、設定ファイルの参照先、権限、プロファイル、カレントディレクトリ、アクセスできるネットワーク資源が変わり、片方が“落ちる→復旧”の連鎖を作ります。ProcMonでコマンドラインとユーザーが違うなら、この線を疑う価値があります。

実務での推奨手順:再現から原因確定までを一本道にする

ProcMonで「2回目の起動の親」を確定する

ProcMonはログ量が多いので、最初から“絞る”のがコツです。以下の設定で、2回目起動の証拠だけを抜きます。

  1. ProcMonを管理者で起動し、キャプチャをいったん停止します(虫眼鏡アイコン)。
  2. フィルターを追加します(Filter → Filter...)。
フィルター例値アクション意図
Process NameNavisXPS.exeInclude対象プロセスに絞る
OperationProcess CreateIncludeプロセス起動イベントだけを見る
ResultSUCCESSInclude実際に起動した行に限定

この状態でキャプチャを開始し、二重起動が起きるまで待ち、発生したら停止します。ログ上で2回目の起動行を見つけたら、次を必ず確認してください。

  • Parent PID / Parent Process(誰が起動したか)
  • Command line(手動起動と同じ引数か)
  • Time of Day(イベントログや監視ログと突き合わせる基準)

ProcMonの表示だけで親が追いにくい場合は、該当行の詳細(イベントプロパティ)を開いて確認します。環境によってはスタック(Stack)を見て、どのモジュール経由で起動されたかの手がかりが得られることもあります。

Process Explorerで「残ってはいけないプロセス」を洗い出す

次に、二重起動が起きた直後の状態でProcess Explorerを開き、次をチェックします。

  • プロセスツリー上で、対象exeの親が何か(services.exe配下か、タスク系か、監視系か)
  • 同じexeが複数ある場合、起動時刻(Start Time)とコマンドラインが一致するか
  • 対象exeが起動する子プロセスがあり、片方の起動で子が取り残されていないか

特に「親が落ちて子が残る」疑いがある場合、次の観点が効きます。

  • 二重起動の前から残っているプロセスが、対象exeの配下にいない(ツリーが切れている)
  • サービスアカウントで動くはずのプロセスが、手動ユーザー権限で残っている(または逆)
  • プロセスが握っているポートやファイル(ログ、DB、IPC)が解放されていない

「残骸があるかどうか」を判断できない時は、正常系サーバー(同一構成で問題が起きていない個体)と、プロセスツリーとコマンドラインを並べて差分を見ると一気に進みます。

さらに一段深く、二重起動の“衝突点”を現物で掴みたい場合は、排他や競合(ファイル/ポート/Mutex)を疑います。二重起動で落ちるケースの多くは、起動そのものではなく、起動後に起きる競合がクラッシュの直接原因です。

  • Properties → Image:Command line / Current directory / User name を2つのプロセスで比較
  • Properties → TCP/IP:使用ポートが重なっていないか確認(片方がポートを握り、もう片方が例外になることがある)
  • Find → Find Handle or DLL:製品名、Mutex名、ログファイル名で検索し、どのプロセスが握っているかを特定

イベントログとWERで「落ちた→再起動」の線を固める

2回目の起動が自動復旧由来であれば、ログの時系列が一致します。次の順で確認すると迷いにくいです。

  • Applicationログ:アプリケーションエラー(Faulting application / module)
  • Systemログ:Service Control Managerの停止・再起動(サービスが予期せず終了、復旧動作など)
  • WER:障害レポート、ダンプ、例外コード

WERの保存場所は環境で異なりますが、代表例として C:\ProgramData\Microsoft\Windows\WER 配下に痕跡が残ります。イベントログの時刻とProcMonの2回目起動時刻を合わせると、「クラッシュが先か、二重起動が先か」を明確にできます。

イベントログで“当たり”を付ける:代表的なイベントIDの例

ログの取り方は環境で変わりますが、Windows Serverの標準ログだけでも「落ちた」「再起動された」の筋道が見えることがあります。次のイベントIDは、二重起動問題の調査で登場しやすい代表例です(環境・バージョンで文言や出方は多少変わります)。

ログイベントID例意味(ざっくり)この問題での使い方
System(Service Control Manager)7031 / 7034サービスが予期せず終了「落ちた→復旧で再起動」の起点になりやすい
System(Service Control Manager)7036サービス状態の遷移(開始/停止)2回目の起動時刻と一致するか確認
Application1000Application Error(障害)Faulting moduleや例外コードの手がかり
Application(Windows Error Reporting)1001障害レポート作成WERが残る環境だとクラッシュの“確証”になる
Microsoft-Windows-TaskScheduler/Operational(環境依存)タスクの開始/完了/失敗「実はタスクが起動していた」を確定しやすい

タスクスケジューラ・サービス登録を棚卸しする

2回目の親がタスクやサービス系だった場合、設定側を棚卸しします。GUIでもできますが、サーバー運用ではコマンドで“証跡として残す”のがおすすめです。

サービス登録の確認例:

sc query type= service state= all
sc qc <サービス名>

タスクスケジューラの検索例(該当exe名で棚卸し):

schtasks /query /fo LIST /v | findstr /i "NavisXPS.exe"

PowerShellでコマンドラインまで追う場合の例:

Get-ScheduledTask | ForEach-Object {
  $t = $_
  $actions = ($t.Actions | ForEach-Object { $_.Execute + " " + $_.Arguments }) -join "; "
  if ($actions -match "NavisXPS.exe") {
    [PSCustomObject]@{
      TaskName = $t.TaskName
      TaskPath = $t.TaskPath
      Actions  = $actions
    }
  }
} | Format-Table -AutoSize

ここでタスクが見つかった場合は、次の設定を優先して見ます。

  • トリガーの種類と頻度(起動時・ログオン時・イベント発生時・定期)
  • 失敗時の再試行(再試行間隔と回数)
  • 「既に実行中の場合」の動作(並列起動になっていないか)

「本当にWindowsサービスか」を確実に見抜くチェックポイント

exe直叩き運用だと、関係者の認識がズレたまま調査が迷走しがちです。次の観点で“サービスとして管理されている実体”を確認すると、起動元の切り分けが一気に楽になります。

  • services.mscに表示されるか(表示名が製品名で、exe名と一致しない場合もあります)
  • サービスのプロパティで実行アカウント(LocalSystem/専用アカウント)
  • ImagePathにexeと引数が入っているか(ラッパー経由の場合は別exeになる)
  • 復旧(Recovery)設定が「サービスの再起動」になっていないか

コマンドで“サービス実体”を確認する例:

sc query type= service state= all | findstr /i "Navis"
sc qc <サービス名>

もし sc qc の BINARY_PATH_NAME が対象exeではなく、別のラッパー(別exe)を指している場合、ラッパーの復旧設定や「子プロセスをどう扱うか」が二重起動の原因になっていることがあります。

すぐ効く暫定対処:原因を止める前に“被害”を止めたいとき

本番環境では、原因究明より先に「二重起動で落ちる状態」を止めたい場面があります。ただし暫定対処は副作用もあるため、必ず“何を犠牲にして何を守るか”を意識してください。

タスク由来なら「並列起動させない」設定に寄せる

  • 既に実行中の場合:新しいインスタンスを開始しない(または既存インスタンスを停止)
  • 失敗時の再試行を抑える(間隔を伸ばす/回数を減らす)

監視/復旧由来なら「再起動条件」を見直す

  • プロセス監視の対象が“親だけ”になっていないか(子の残骸で誤判定しないか)
  • ハング判定の閾値(応答遅延の許容)
  • 再起動前にプロセスツリーをクリーンに落とす設定があるか

スクリプト起動なら「起動前チェック」を入れて事故を防ぐ

アプリ側に手を入れられない場合、起動スクリプトに“既に動いていれば起動しない”ガードを入れることで、二重起動によるクラッシュを回避できることがあります。重要なのは、プロセス名だけで判定しないことです(別用途の同名プロセスを誤検知しやすい)。

コマンドライン引数まで見て判定するPowerShell例:

$exe = "NavisXPS.exe"
$argContains = "-d"

$running = Get-CimInstance Win32_Process |
Where-Object { $*.Name -eq $exe -and $*.CommandLine -match [regex]::Escape($argContains) }

if ($running) {
Write-Host "Already running. Skip start."
exit 0
}

Start-Process -FilePath "C:\Path\To\NavisXPS.exe" -ArgumentList "-d ..."

これはあくまで暫定です。根本原因(誰がいつ起動しているか)を特定した上で、起動経路の整理へ進むのが安全です。

ベンダー/開発元に解析依頼するときに渡すと強い“証拠セット”

「二重起動が原因で落ちる」は、アプリ側の単一インスタンス制御や異常系処理の問題に行き着くことが少なくありません。開発元に丸投げしても再現しないことが多いので、次の“証拠セット”を揃えると解析が進みます。

渡すもの内容なぜ効くか
ProcMonのPML/CSV2回目起動のProcess Create行(親・コマンドライン・時刻が分かる範囲)「誰が起動したか」を客観的に示せる
Process Explorerのスクリーンショット問題発生直後のプロセスツリー、該当プロセスのProperties親子関係や孤児プロセスの証拠になる
イベントログのエクスポート発生時刻前後のSystem/Application落ちた→再起動の因果が追える
WERレポート/ダンプ例外コード、Faulting module、必要ならダンプクラッシュ解析の入り口になる
起動コマンドと運用手順手動起動・自動起動の両方(引数/実行ユーザー/作業ディレクトリ)環境差による不具合を潰せる

再発防止の考え方:運用で潰すか、設計で潰すか

根本的には、二重起動しても落ちない設計(単一インスタンス制御、排他、例外処理)が理想です。ただし現場では、アプリ改修ができないことも多いので、現実的には次のレイヤーで対策を考えます。

運用・設定で潰す(すぐできるが、環境依存)

  • 起動経路を一本化(手動起動を禁止し、サービス/タスク/監視のどれかに統一)
  • 監視復旧の「再起動前にプロセスツリーを停止」設定を使う
  • タスクの並列起動を禁止し、失敗時再試行を調整

設計・実装で潰す(理想だが、改修が必要)

  • 単一インスタンス制御(Mutex、Named Pipe、ポートバインド、ロックファイル)を堅牢にする
  • 親が死んだら子も確実に終了する(Job Object等)
  • “既に起動済み”の検出時に例外で落ちない(正常終了/フォアグラウンド切替等)

よくある質問(現場で詰まるポイント)

ProcMonのログが多すぎて見つけられません

最初から「Process Create」に絞り、対象exe名でIncludeしてください。さらに“発生直前にキャプチャ開始→発生直後に停止”の運用にすると、ログは数十行〜数百行に収まり、追える量になります。

手動起動と2回目の起動で引数が微妙に違います

引数差は重要な手がかりです。別の構成(別プロファイル、別データディレクトリ)で動き、片方が落ちて復旧、または排他が効かず衝突する原因になります。起動経路ごとに「実行ユーザー」「作業ディレクトリ」「環境変数」「参照する設定ファイル」をセットで比較してください。

2回目が起動されるのに、イベントログに何も出ません

タスクスケジューラの履歴が無効、監視ツールが独自ログのみ、あるいはスクリプトから起動している場合があります。ProcMonで親プロセスを確定し、その親が吐くログ(監視ログ、タスク履歴、運用ジョブログ)へ辿るのが最短です。

ありがちな実例:調査結果がこう出たら、こう考える

最後に、現場でよくある“結果パターン”を短くまとめます。自環境のログと照らすと、次の一手が選びやすくなります。

観測できた事実示唆次にやること
ProcMonで2回目の親がtaskeng.exe、同じ引数で起動しているタスクスケジューラの定期起動が本命該当タスクを特定し、並列起動禁止・トリガー重複・再試行を見直す
ProcMonで2回目の親が監視エージェント、直前に本体が一瞬消えている監視が停止判定→再起動している監視判定の対象と閾値、再起動前のプロセスツリー停止を調整
Process Explorerで子プロセスだけ残り、2回目が起動すると落ちる親だけ落ちて子が孤児化→競合でクラッシュアプリの異常終了原因の調査+再起動前に残骸を確実に落とす運用へ
手動起動はユーザー、2回目はLocalSystemで起動している起動経路による実行環境差がトリガー正規起動経路へ一本化(サービス/タスク)、実行アカウント統一

まとめ:調査の順番を間違えなければ、原因は絞り込める

  • 二重起動の正体は「誰かが再起動している」か「親が落ちて子が残っている」ことが多い
  • 最初にやるべきは、ProcMonで2回目の親プロセスを確定し、Process Explorerで孤児プロセスの有無を見ること
  • 親がタスク/サービス/監視のどれか分かれば、設定とログの当たり所が一気に狭まる
  • 暫定対処(並列起動禁止・起動前チェック)は有効だが、最終的には起動経路の一本化かアプリ側の堅牢化が再発防止になる

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次