Terminating Windows-Based Application from Another App (92528)
The information in this article applies to:
- Microsoft Windows Software Development Kit (SDK) 3.0
- Microsoft Windows Software Development Kit (SDK) 3.1
This article was previously published under Q92528 SUMMARY
An 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 INFORMATION
Clean 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 Application
Obtain 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- EnumTaskWindows() can be used to enumerate all the top-level windows
owned by the task. The task handle of the task required for this
function can be obtained using the Tool Helper library functions and one
of the following three methods:
- If the task to be terminated is started after the terminator task,
the terminator can set up a call-back function using
NotifyRegister(). This call-back function is called by every task in
the system on start up and termination. GetCurrentTask() can be used
to obtain the task handle of the task that called the call-back
function. TaskFindHandle() can be used to obtain information about
this task from a TASKENTRY structure.
If the terminator task wants to terminate all the child tasks that it
spawned using WinExec(), the following method can be used. A child
task can be identified by checking whether the hTaskParent field in
the TASKENTRY structure contains the terminator's task handle:
BOOL FAR PASCAL NotifyRegisterCallback (WORD wID, DWORD dwData)
{
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().
- 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.
- 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().
- Window handles can be obtained using FindWindow() if the window title or
class name is known.
Modification Type: | Minor | Last Reviewed: | 8/2/2004 |
---|
Keywords: | kb16bitonly KB92528 |
---|
|