Terminating Windows-Based Application from Another AppLast reviewed: July 23, 1997Article ID: Q92528 |
3.00 3.10
WINDOWS
kbprg
The information in this article applies to:
SUMMARYAn application can be cleanly terminated by another application by posting WM_CLOSE to its top-level, non-owned, non-disabled windows. Disabled windows should not be posted WM_CLOSE because they may be disabled as a consequence of a modal dialog box being displayed or because they may be in a state that does not allow termination. The Tool Helper library has a function TerminateApp() to close an application. However, TerminateApp() does not terminate an application cleanly and is designed to be used in a debugging environment.
MORE INFORMATIONClean termination means that an application terminates as designed by the application designer after freeing its resources. Clean termination of a Windows-based application by another Windows-based application is possible in many cases by posting WM_CLOSE to top-level, non-owned, non-disabled windows of the application. However clean termination is not possible in some cases because of the following reasons. If a Windows-based application has a modal dialog box displayed, it is in a modal state. The transition out of the modal state takes place when the modal dialog box is closed in a manner specified by the application programmer -- typically when the user selects a dialog box button. If the owner of a modal dialog box is posted WM_CLOSE, it will destroy the modal dialog box by calling DestroyWindow(). This particular transition out of the modal state may not have been designed by the application programmer because modal dialog boxes are designed to be closed by EndDialog(), not DestroyWindow(). Consequently, the dialog box could terminate without cleaning up. For example, a dialog box that frees GDI objects when it receives a WM_COMMAND with wParam == IDOK or IDCANCEL will not free these objects if it is closed as a consequence of WM_CLOSE being posted to its owner. Clean termination is possible in the above case if the terminating application expects to be closed as a consequence of the owner being posted a WM_CLOSE while a modal dialog box is up. Note that most applications do not expect to be closed in this way.
Algorithm to Terminate an ApplicationObtain the handles of all top-level, non-owned windows of the application that is to be terminated as described in a following section. For each of the top-level, non-owned windows of an application: { if (IsWindowEnabled(hwnd)) PostMessage(hwnd, WM_CLOSE, 0, 0);} WM_CLOSE is posted only if the window is not disabled. A disabled window could mean that it is the owner of a modal dialog box and so should not be closed. A window is also disabled when an application does not want the user to manipulate it (including closing it), and consequently should not be closed. A modal dialog box disables only its immediate owner. This means that a top-level window that is not disabled, but that owns a window that in turn owns a modal dialog box, can be closed and the dialog box can consequently be destroyed. However, this can also be done by the user and so should have been prevented by the application if it was not to be allowed.
Obtaining Handles of Top-Level, Non-Owned Windows of an Application
HTASK hTask; TASKENTRY te; switch (wID) { case NFY_STARTTASK: case NFY_EXITTASK: // Obtain info about the task that is starting/terminating. hTask = GetCurrentTask(); te.dwSize = sizeof(TASKENTRY); TaskFindHandle(&te, hTask); // ghTaskParent is the task that called NotifyRegister(). // Check if hTask is a child task of ghtaskParent. // ghwnd is a window of the parent task. if (te.hTaskParent == ghtaskParent) if (wID == NFY_STARTTASK) PostMessage(ghwnd, PM_TASKSTART, (WORD)hTask, 0); else PostMessage(ghwnd, PM_TASKEND, (WORD)hTask, 0); break; default: break; } // Pass notification to other callback functions. return FALSE;}
The parent task must maintain a list of child tasks that are currently running by adding a child task handle to the list when PM_TASKSTART is received and by removing a task handle when PM_TASKEND is received. The Task List, which is brought up by choosing the Switch To system menu item, and some screen savers, are run as child tasks of the active application. All child tasks can be terminated by calling EnumTaskWindows() on each of the task handles in the list. Below is the callback to EnumTaskWindows():BOOL CALLBACK EnumTaskWindowsCallBack(HWND hwnd, LONG lParam) { // Check whether the window still exists and that it does not have an // owner. if (IsWindow(hwnd) && !GetWindow(hwnd, GW_OWNER)) { // Do not close disabled windows. if (!IsWindowEnabled(hwnd))) return TRUE; else PostMessage(hwnd, WM_CLOSE, 0, 0); } return TRUE;}
The TERMWAIT sample application, which can be found in the Software Library by searching on the keyword TERMWAIT, demonstrates the use of NotifyRegister(). b. The Tool Helper library functions TaskFirst(), TaskNext(), and TaskFindHandle() and the szModule field in the TASKENTRY structure can be used to obtain the task handle if the Module name is known. c. If the window handle of one window belonging to a task is known, the handles to other top-level windows of the task can be obtained using GetWindowTask() and EnumTaskWindows().
|
Additional reference words: 3.10 call back
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |