Windows アプリケーションで、自分以外のプロセスに対して安全に終了させる
という手段は中々ありません。
ただネットの情報では下記2つの関数、
A. PostThreadMessage() でメインスレッドに WM_QUIT を送信する
B. TerminateProcess() でメインプロセスを強制終了させる
を使用することによる、デメリットの誤解を招く表現をしているコメントが非常に
多いです。
"どちらも変わらないほど危険" という表現のみのコメントが目立ちます。
これだけでは十分な説明になっていなく "どっちも同じ" と受け止められる可能性
があります。
危険という結論だけで言えばそうなのですが、起こりえる弊害には差異もあるため、
どちらも同じと言うには正しくない。
ここでは私視点からこの2つの手段による終了する事の弊害を説明します。
まず A の方は、問答無用に WM_QUIT を割り込ませるので WM_DESTROY などで
解放ルーチンを実行しているプロセスではリソースリーク・メモリリークする可能性があります。
但し Windows9x 系ならともかく、NT 系以降の OS では unix 系と同様にプロセスが
終了されると同時に、その空間で確保された
ファイルハンドル
ヒープメモリ
ヒープメモリ
は解放されます。言い換えるなら delete[] をしてなくてもファイルハンドルを閉じて無く
ても、プロセスが死ねば自動的に解放やフラッシュされるのです。
と言っても私は昔からのクセもあり、Windows 上では確実に解放処理を実装しますが。
さて本題に戻りますが、 A の方は、
メインスレッドのメッセージループ は終了する( 抜ける )
という点で非常に意味があります。
例えば C++で実装し WinMain 関数にて
int WINAPI WinMain( ... )
{
C_Application app;
C_Application app;
// メッセージループ
return app.MessageLoop();
}
return app.MessageLoop();
}
の様な実装で、 C_Application クラスの "デストラクタにて解放を実装している設計"
なら極端な話、WM_QUIT をいきなり投げられても問題は全く無いのです。
またどのプロセスにも言えるのですが WM_QUIT を投げた場合は、メインスレッドが
return 終了するのでグローバル変数の各デストラクタも通常にコールされます。
上記のコードの様な構造で実装していた場合は問題無いかと思いますが、
そうではない場合…例えば WM_DESTROY の方で解放処理をしていた場合は、
逆に中途半端な状況になり危険もあります。
例を言うと、メインスレッドは死んだが、サブスレッドが走ったままになりプロセスは
終了出来ない状況など。
サブスレッドの状況によっては、ヌルポエラーで強制終了する可能性もありますし、
メインスレッドが走っている事を前提にしているプログラムがある場合は、
挙動が想像付きません。
さてここまでの結論をまとめると A の方は、
プロセスによっては安全、プロセスによっては危険
という2通りの結果があるということです。
続いて B の方は、無条件にプロセスを強制終了させるので、プログラム個別に
実装している独自の正常な終了処理などは一切行われる余地がありません。
ヒープメモリやファイルハンドル等はプロセス空間が解放されることにより解放されます。
強制終了で重要なのは対象プロセスにアタッチしている DLL には終了処理が行われる事が
通知されない為、DLL が管理しているメモリ領域は整合性の取れない状態に陥る可能性が
あります。
例えば、プロセス起動時に読み込まれる DLL にリファレンスカウント +1するような
内部実装がされてる場合、本来はデタッチする時に -1 するべきなのですが、
DLL に通知がいかない事によってリファレンスカウントが正しくカウントされない事に…
など起きたりすると思います。
これは場合によってはシステムを不安定にする可能性もあるので使用する時点で
危険とも言えるメソッドです。
どっちが危険なの?って言う問いに対しては、TerminateProcess() は全てのプロセスで
弊害が発生する可能性があり、WM_QUIT 送信は、正常に終了される実装のプロセスも
あれば、そうでないのもあるって経緯を説明した上で、両方とも弊害はありえる。
と言う結論になります。
0 コメント:
コメントを投稿