在完美的世界您的處理程序可能要求透過某種形式的到關機的處理序間通訊的另一個處理序。不過,如果您沒有您想要關閉應用程式的來源層級的控制項再您可能沒有這個選項。雖然沒有保證的全新方法,可以關閉在 Win32 應用程式有您可以採取確保應用程式的清除資源使用最佳的方法的步驟。
32 位元處理序 (和 16 位元處理程序在 Windows 95 下)
在 Win32,作業系統承諾清除它關機時,由處理程序所擁有的資源。這並不,但是,表示程序本身已經有機會執行磁碟透過遠端連線的任何最終通訊的資訊的任何最終排清也意思,處理序的 DLL 的將有機會執行其 PROCESS_DETACH 程式碼。這就是為什麼最好通常以避免終止受 Windows 95 與 Windows NT 應用程式。
如果您絕對必須關機處理程序,請依照下列步驟執行:
- 將一個 WM_CLOSE 張貼到您想要關閉處理序所擁有的全部 Top-Level 視窗。許多 Windows 應用程式由正在關閉回應這個訊息。
注意: WM_CLOSE A 主控台應用程式的回應取決於是否它已安裝控制處理常式。
使用 EnumWindows() 來尋找您的目標視窗的控制代碼。在您的回呼函式以查看視窗的處理序 ID 的核取符合您想要關機程的序。您可以藉由呼叫 GetWindowThreadProcessId() 完成這動作。一旦建立相符的項目使用 PostMessage() 或 SendMessageTimeout() 張貼內容 WM_CLOSE 訊息至該視窗。 - 使用 WaitForSingleObject() 等候處理序的控制代碼。因為有許多情況下,在其中 [WM_CLOSE 將不關閉應用程式,則請確定您等候逾時數值。請逾時時間夠長,請記得 (其中 WaitForSingleObject(),或 SendMessageTimeout()),讓使用者可以回應任何對話方塊方塊的 WM_CLOSE 訊息回應中所建立。
- 如果傳回值是 WAIT_OBJECT_0,然後應用程式關閉本身向下俐落地。如果傳回值是 WAIT_TIMEOUT,然後您必須使用 TerminateProcess() 關閉應用程式。
注意: 如果您是來自其他 WaitForSingleObject() 然後 WAIT_OBJECT_0 或 WAIT_TIMEOUT 的傳回值的 getting3,使用 GetLastError() 來判斷原因。
請依照下列步驟執行,您讓應用程式最佳的可能機會關機俐落地 (除了從 IPC 或使用者介入情況下)。
(在 Windows NT) 下的 [16 位元] 問題
上述步驟 16 位元應用程式在 Windows 95 下工作,但是,Windows NT 16 位元應用程式運作完全不同。
在 Windows NT 下所有的 16 位元應用程式在虛擬 DOS 機器 (VDM) 中執行。這個 VDM 以 Win32 程序 (NTVDM) 在 Windows NT 下執行。NTVDM 程序有處理序 ID。就跟您可以與任何其他 Win32 處理序的方式一樣,您可以取得 OpenProcess(),透過處理序的控制代碼。 不過,沒有任何 [VDM 中執行的 16 位元應用程式有一個處理程序] 識別碼,因此您無法取得處理序控制代碼從 OpenProcess()。每個 16 位元應用程式中一個 VDM 有 16 位元工作控制碼和執行的 32 位元執行緒。透過呼叫函式 VDMEnumTaskWOWEx() 找不到控制代碼和執行緒 ID。如需詳細資訊請參閱 「 Microsoft 知識庫 」 中下列文:
175030?
(http://support.microsoft.com/kb/175030/EN-US/
)
列舉在 Win32 的應用程式的方式
當關閉 Windows NT] 下的 16 位元應用程式時您第一個,也是最直接選項是關閉整個 NTVDM 處理序。您可以只要遵循上述步驟。您只需要知道 NTVDM 程序的處理序 ID (請參閱 [KB 文章
175030?
(http://support.microsoft.com/kb/175030/EN-US/
)
引用上面來尋找一個 NTVDM 處理序 ID)。這種方法的缺點是它會關閉所有執行中該 VDM 的 16 位元應用程式。如果這不是您的目標,您就需要採取另一種方法。
如果想關閉單一的 16 位元應用程式,在 NTVDM 程序中以下是您需採取的步驟:
- 將一個 WM_CLOSE 張貼到所有 Top-Level 視窗由在程序所擁有和有您要關閉的 16 位元工作跟相同主控執行緒識別碼。若要這麼做最有效方法是使用 EnumWindows()。 在您的回呼函式檢查視窗的處理序識別碼,而且執行緒 ID 是否符合您要關閉的 16 位元工作。請記住處理序 ID 要將 NTVDM 程序執行 16 位元應用程式的處理序 ID。
- 雖然您執行緒識別碼會者不能等待的 16 位元處理序終止。如此一來您必須等待的時間 (以容許一個乾淨關機),將任意長度,然後再嘗試仍要關閉應用程式。如果應用程式已經有關機,然後這將會做執行任何動作。如果它尚未關閉,它將會結束這個應用程式。
- 終止應用程式使用稱為可在 [Vdmdbg.dll 中找到的 VDMTerminateTaskWOW() 函式。花在 VDM 和 16 位元工作的工作數目的處理序 ID。
這種方法可讓您關閉單一的 16 位元應用程式在 Windows NT VDM 內。不過,16 位元 Windows 不是非常擅於清除已經終止任務的資源,且兩者都不是執行中 [VDM WOWExec。如果您要尋找最簡單的可能方式來終止在 Windows NT 為 16 位元應用程式,您應該考慮終止整個 VDM 程序。注意: 如果您啟動 16 位元應用程式,您可能稍後終止,然後使用 [CREATE_SEPARATE_WOW_VDM CreateProcess()。
範例程式碼
程式碼範例會實作上述使用下列兩個功能的 16 位元和 32 位元應用程式的技術: TerminateApp() 和 Terminate16App()。TerminateApp() 會使用 32 位元處理序識別碼和逾時 (以毫秒為單位))。Terminate16App()。這兩個函式使用明確連結至 DLL 函式,以便他們會透過 Windows NT 和 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 ;
}