起動したアプリケーションを終了させる

自らが起動した他のアプリケーションを終了させるには
CreateProcess()で起動させプロセスハンドルを保持し
終了はTerminateProcess()で簡単に終了させる事が出来ます。
しかしこのAPIは有無を言わさずプロセスを強制終了させるので危険です。
出来る限り使わないほうが良いでしょう。

では安全に終了させるにはどうしたら良いかと言うと
起動したアプリケーション自らが終了する様に仕向けます。
方法としてはウィンドウハンドルを取得しWM_CLOSEを送るのが一般的です。

只、プロセスハンドルから直接ウィンドウハンドルを取得するAPI等は存在しません。
そこでウィンドウハンドルからウィンドウを作成したプロセスのIDを調られる
GetWindowThreadProcessId()というAPIを使用します。
つまり、全てのウィンドウハンドルを取得し、1つずつ作成したプロセスIDが
CreateProcess()で取得できるプロセスIDと一致するか調べるのです。
一致した場合、そのウィンドウは起動したアプリケーションのウィンドウと言う事です。

全てのウィンドウハンドルを取得する方法はEnumWindows()を使います。
このAPIは画面上のトップレベルのウィンドウハンドルをアプリケーション定義の
コールバック関数に順番に渡していく事を実現します。

コールバック関数とはWindowsから呼ばれる関数の事で
実際はアプリケーションが直接呼び出すものではありません。
Windowsはコールバック関数に色々なメッセージを繰り返し送ってきます。
コールバック関数内では送られたメッセージを独自の処理をする事が出来ます。
EnumWindows()は全てのトップレベルのウィンドウハンドルをWindowsから
コールバック関数に繰り返し呼ばせるようにします。

さて、コールバック関数の定義ですがここでは先に書いた様に
Windowsから送られたウィンドウハンドルからプロセスIDを取得し
CreateProcess()で取得したプロセスIDと比較。
それぞれが一致したらそのウィンドウにWM_CLOSEを送るという処理をします。

つまり以下の様になります。


// ウィンドウハンドルを取得しアプリケーションを終了させる。
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
    // CreateProcess()で取得したPROCESS_INFORMATION構造体のポインタを取得
    PROCESS_INFORMATION* pi = (PROCESS_INFORMATION*)lParam;

    // ウインドウを作成したプロセスIDを取得。
    DWORD lpdwProcessId = 0;
    ::GetWindowThreadProcessId(hWnd, &lpdwProcessId);

    // CreateProcessで起動したアプリのプロセスIDとメインウィンドウを
    // 作成したプロセスIDが同じ場合、起動したアプリを終了させる。
    if(pi->dwProcessId == lpdwProcessId)
    {
        ::PostMessage(hWnd, WM_CLOSE, 0, 0);
        return FALSE;
    }
    return TRUE;
}

補足としてコールバック関数の名前はEnumWindowsProcでなくても構いません。
好きな名前を付けてもらって結構です。
それから戻り値でFALSEを返すとウィンドウハンドルの列挙が途中でも
中断して次は呼ばれなくなります。
あともう1つ大事な事で、コールバック関数はグローバル関数でなければなりません。
コールバック関数を呼び出すのはWindowsであるという事を考えると当然ですね。

それでは以下に実際の使用例を示します。


// メモ帳の起動。
STARTUPINFO si;
PROCESS_INFORMATION pi;
::ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb           = sizeof(STARTUPINFO);
si.dwFlags      = STARTF_USESHOWWINDOW;
si.wShowWindow  = SW_SHOWNORMAL;
::CreateProcess(NULL, _T("notepad"), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

AfxMessageBox(_T("メモ帳を終了させます。"));

// コールバック関数の呼び出し。
EnumWindows(EnumWindowsProc, (LPARAM)&pi);

// メモ帳が終了するまで待機し、もし5秒待っても終わらない時は強制終了させる。
if(::WaitForSingleObject(pi.hProcess, 5000) == WAIT_TIMEOUT)
{
    if(AfxMessageBox(_T("強制終了させますか?"), MB_YESNO) == IDYES)
        TerminateProcess(pi.hProcess, 0);
}

::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);

EnumWindows()の第一引数にコールバック関数名を(実際はポインタ)
第二引数に取得したPROCESS_INFORMATION構造体のポインタを渡します。

これで、メモ帳を起動しメッセージボックスをクリックする事で終了させられます。
この時メモ帳に何かを書いて保存しないでクリックすると
メモ帳が内容を保存するか聞いてきます。
ここで5秒待つと強制終了させるかどうかを選択する事になります。
「はい」ならそのまま終了させ「いいえ」なら終了させません。

< 戻る << HOME ©1999-2001 by Akky, All right reserved.