HOWTO: Terminate an Application "Cleanly" in Win32Last reviewed: January 7, 1998Article ID: Q178893 |
The information in this article applies to:
SUMMARYIn a perfect world, your process could ask another process, through some form of inter-process communication, to shut down. However, if you do not have source-level control of the application that you wish to shut down, then you may not have this option. Although there is no guaranteed "clean" way to shut down an application in Win32, there are steps that you can take to ensure that the application uses the best method for cleaning up resources.
MORE INFORMATION
32-Bit Processes (and 16-Bit Processes under Windows 95)Under Win32, the operating system promises to clean up resources owned by a process when it shuts down. This does not, however, mean that the process itself has had the opportunity to do any final flushes of information to disk, any final communication over a remote connection, nor does it mean that the process' DLL's will have the opportunity to execute their PROCESS_DETACH code. This is why it is generally preferable to avoid terminating an application under Windows 95 and Windows NT. If you absolutely must shut down a process, follow these steps:
The 16-Bit Issue (under Windows NT)The preceding steps work for 16-bit applications under Windows 95, however, Windows NT 16-bit applications work very differently. Under Windows NT, all 16-bit applications run in a virtual DOS machine (VDM). This VDM runs as a Win32 process (NTVDM) under Windows NT. The NTVDM process has a process ID. You can obtain a handle to the process through OpenProcess(), just like you can with any other Win32 process. Nevertheless, none of the 16-bit applications running in the VDM have a process ID, and therefore you cannot get a Process Handle from OpenProcess(). Each 16-bit application in a VDM has a 16-bit Task Handle and a 32-bit thread of execution. The handle and thread ID can be found through a call to the function VDMEnumTaskWOWEx(). For additional information, please see the following article in the Microsoft Knowledge Base:
ARTICLE-ID: Q175030 TITLE : HOWTO: Enumerate Applications in Win32Your first, and most straightforward, option when shutting down a 16-bit application under Windows NT is to shut down the entire NTVDM process. You can do this by following the steps outlined above. You only need to know the process ID of the NTVDM process (see the KB article Q175030 cited above to find the process ID of an NTVDM). The downside of this approach is that it closes all 16-bit applications that are running in that VDM. If this is not your goal, then you need to take another approach. If you wish to shut down a single 16-bit application within a NTVDM process, following are the steps you need to take:
Sample CodeThe sample code implements the techniques described above for 16-bit and 32- bit applications using the following two functions: TerminateApp() and Terminate16App(). TerminateApp() takes a 32-bit process ID and a timeout (in miliseconds). Terminate16App(). Both functions use explicit linking to DLL functions so that they will be binary compatible across Windows NT and Windows 95.
//****************** //Header //****************** #include <windows.h> #define TA_FAILED 0 #define TA_SUCCESS_CLEAN 1 #define TA_SUCCESS_KILL 2 #define TA_SUCCESS_16 3 DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) ; DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread, WORD w16Task, DWORD dwTimeout ); //****************** //Source //****************** #include "TermApp.h" #include <vdmdbg.h> typedef struct { DWORD dwID ; DWORD dwThread ; } TERMINFO ; // Declare Callback Enum Functions. BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) ; BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam ) ; /*---------------------------------------------------------------- DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) Purpose: Shut down a 32-Bit Process (or 16-bit process under Windows 95) Parameters: dwPID Process ID of the process to shut down. dwTimeout Wait time in milliseconds before shutting down the process. Return Value: TA_FAILED - If the shutdown failed. TA_SUCCESS_CLEAN - If the process was shutdown using WM_CLOSE. TA_SUCCESS_KILL - if the process was shut down with TerminateProcess(). NOTE: See header for these defines. ----------------------------------------------------------------*/ DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout ) { HANDLE hProc ; DWORD dwRet ; // If we can't open the process with PROCESS_TERMINATE rights, // then we give up immediately. hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, dwPID); if(hProc == NULL) { return TA_FAILED ; } // TerminateAppEnum() posts WM_CLOSE to all windows whose PID // matches your process's. EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ; // Wait on the handle. If it signals, great. If it times out, // then you kill it. if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0) dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED); else dwRet = TA_SUCCESS_CLEAN ; CloseHandle(hProc) ; return dwRet ; } /*---------------------------------------------------------------- DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread, WORD w16Task, DWORD dwTimeout ) Purpose: Shut down a Win16 APP. Parameters: dwPID Process ID of the NTVDM in which the 16-bit application is running. dwThread Thread ID of the thread of execution for the 16-bit application. w16Task 16-bit task handle for the application. dwTimeout Wait time in milliseconds before shutting down the task. Return Value: If successful, returns TA_SUCCESS_16 If unsuccessful, returns TA_FAILED. NOTE: These values are defined in the header for this function. NOTE: You can get the Win16 task and thread ID through the VDMEnumTaskWOW() or the VDMEnumTaskWOWEx() functions. ----------------------------------------------------------------*/ DWORD WINAPI Terminate16App( DWORD dwPID, DWORD dwThread, WORD w16Task, DWORD dwTimeout ) { HINSTANCE hInstLib ; TERMINFO info ; // You will be calling the functions through explicit linking // so that this code will be binary compatible across // Win32 platforms. BOOL (WINAPI *lpfVDMTerminateTaskWOW)(DWORD dwProcessId, WORD htask) ; hInstLib = LoadLibraryA( "VDMDBG.DLL" ) ; if( hInstLib == NULL ) return TA_FAILED ; // Get procedure addresses. lpfVDMTerminateTaskWOW = (BOOL (WINAPI *)(DWORD, WORD )) GetProcAddress( hInstLib, "VDMTerminateTaskWOW" ) ; if( lpfVDMTerminateTaskWOW == NULL ) { FreeLibrary( hInstLib ) ; return TA_FAILED ; } // Post a WM_CLOSE to all windows that match the ID and the // thread. info.dwID = dwPID ; info.dwThread = dwThread ; EnumWindows((WNDENUMPROC)Terminate16AppEnum, (LPARAM) &info) ; // Wait. Sleep( dwTimeout ) ; // Then terminate. lpfVDMTerminateTaskWOW(dwPID, w16Task) ; FreeLibrary( hInstLib ) ; return TA_SUCCESS_16 ; } BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) { DWORD dwID ; GetWindowThreadProcessId(hwnd, &dwID) ; if(dwID == (DWORD)lParam) { PostMessage(hwnd, WM_CLOSE, 0, 0) ; } return TRUE ; } BOOL CALLBACK Terminate16AppEnum( HWND hwnd, LPARAM lParam ) { DWORD dwID ; DWORD dwThread ; TERMINFO *termInfo ; termInfo = (TERMINFO *)lParam ; dwThread = GetWindowThreadProcessId(hwnd, &dwID) ; if(dwID == termInfo->dwID && termInfo->dwThread == dwThread ) { PostMessage(hwnd, WM_CLOSE, 0, 0) ; } return TRUE ; } |
Additional query words: Kill Exit Terminate Program Application Process
© 1998 Microsoft Corporation. All rights reserved. Terms of Use. |