Jak ukončit aplikaci "Čistě" v systému Win32

Souhrn

V ideálním procesu může požádat jiný proces přes nějakou formu komunikaci mezi procesy, ukončit. Však pokud nemáte zdroj řízení aplikace, kterou chcete vypnout, pak pravděpodobně nemáte tuto možnost. Přestože zaručených "čistou" způsob, jak ukončit aplikaci v systému Win32, existují kroky, které můžete provést k zajištění, že aplikace používá metody pro uvolnění prostředků.

Další informace

32bitové procesy (a 16bitové procesy v systému Windows 95)

V prostředí Win32 slibuje uvolnit prostředky vlastněné proces při jeho vypnutí operačního systému. To, však neznamená, že samotný proces měl příležitost provádět žádné konečné vyprázdnění informace na disk jakékoliv konečné sdělení prostřednictvím vzdáleného připojení, ani to znamená, že tento proces DLL bude mít možnost spustit svůj kód PROCESS_DETACH. Z tohoto důvodu je obvykle vhodnější, aby se zabránilo ukončení aplikace v systému Windows 95 a Windows NT.


Pokud je to nezbytně nutné vypnout proces, postupujte takto:
  1. Vystavit funkce WM_CLOSE bude vlastníkem procesu, který chcete ukončit činnost všech oken nejvyšší úrovně. Mnoho aplikací systému Windows reagovat na tuto zprávu Probíhá vypnutí počítače.

    POZNÁMKA: Funkce WM_CLOSE bude odezva aplikace konzoly závisí na zda nainstaloval obslužnou rutinu pro ovládací prvek.


    Pomocí EnumWindows() můžete najít úchyty na cílovém systému windows. Zkontrolujte, pokud ID procesu systému windows ve vaší funkci zpětného volání odpovídá procesu, který chcete vypnout. To lze provést voláním GetWindowThreadProcessId(). Po stanovení shody pomocí okna Odeslat zprávu funkce WM_CLOSE bude PostMessage() nebo SendMessageTimeout().
  2. Čekání na popisovač procesu použijte WaitForSingleObject(). Zkontrolujte, zda že počkat s hodnotou časového limitu, protože existuje řada situací, ve kterých Funkce WM_CLOSE bude nevypne aplikace. Mějte na paměti dostatečně dlouhý časový limit (s WaitForSingleObject() nebo s SendMessageTimeout()) tak, aby uživatel může reagovat na všechna pole, která byla vytvořena v reakci na zprávu funkce WM_CLOSE bude.
  3. Pokud je vrácená hodnota je WAIT_OBJECT_0, pak aplikace zavřena sám čistě. Pokud je vrácená hodnota je WAIT_TIMEOUT, musíte použít TerminateProcess() vypnutí aplikace.

    POZNÁMKA: Pokud jste getting3 vrácení hodnoty z jiných WaitForSingleObject() potom WAIT_OBJECT_0 nebo WAIT_TIMEOUT, pomocí GetLastError() zjistit příčinu.
Pomocí následujících kroků, dáte aplikaci nejvyšší možnou pravděpodobnost vypnutí čistě (kromě IPC nebo zásahu uživatele).

Problém 16 bitů (v systému Windows NT)

Předchozí kroky pracovat pro 16bitové aplikace v systému Windows 95 však 16bitového systému Windows NT aplikace práce velmi odlišně.


V systému Windows NT spuštění všech 16bitových aplikací v virtuální počítač DOS (VDM). Tento VDM je spuštěna jako proces Win32 (NTVDM) v systému Windows NT. ID procesu byl procesu NTVDM Můžete získat popisovač procesu prostřednictvím OpenProcess(), stejně jako u jiných proces Win32. Však žádné 16bitové aplikace spuštěné v VDM mít ID procesu, a proto nelze získat popisovač procesu z OpenProcess(). Každá aplikace 16 bitů v podsystému VDM je zpracování úkolu 16bitové a 32bitové podproces provádění. Úchyt a podprocesu ID lze získat prostřednictvím volání funkce VDMEnumTaskWOWEx(). Další informace naleznete v následujícím článku znalostní báze Microsoft Knowledge Base:
175030 Jak vytvořit výčet aplikací v systému Win32
Vaše první a nejjednodušší, možnost při ukončení 16bitové aplikace v systému Windows NT je ukončení celého procesu NTVDM. Lze provést pomocí výše uvedených kroků. Potřebujete znát ID procesu NTVDM procesu (viz článek KB 175030 výše citované vyhledat Identifikátor procesu NTVDM). Nevýhodou tohoto přístupu je, že jej zavře všechny 16bitové aplikace, které jsou spuštěny v tomto VDM. Pokud není vaším cílem, je třeba přijmout další přístup.


Pokud chcete vypnout jeden 16bitové aplikace v rámci procesu NTVDM jsou následující kroky, které je třeba provést:

  1. Funkce WM_CLOSE bude účtovat do všech oken nejvyšší úrovně, které vlastní proces a které mají stejné ID vlastnícího vlákno jako 16bitové úlohy, kterou chcete vypnout. Nejúčinnějším způsobem je pomocí EnumWindows(). Ve vaší funkci zpětného volání zkontrolujte, zda v okně proces ID a ID vlákna odpovídá 16bitové úlohy, kterou chcete vypnout. Mějte na paměti, že ID procesu bude ID procesu NTVDM procesu, ve kterém je spuštěna aplikace 16 bitů.
  2. I když máte ID vlákna, máte žádný způsob čekání na ukončení procesu 16bitové. V důsledku toho musí počkat libovolnou dobu (Chcete-li umožnit že čisté vypnutí) a zkuste přesto ukončit aplikaci. Pokud aplikace byl již vypnut, pak to bude neprovádějte žádnou akci. Pokud není ukončena, bude jej ukončit aplikace.
  3. Ukončení aplikace využívající funkci s názvem VDMTerminateTaskWOW(), který naleznete Vdmdbg.dll. ID procesu VDM a číslo úkolu 16bitové úlohy trvá.
Tento přístup umožňuje vypnout jeden 16bitové aplikace v rámci VDM v systému Windows NT. Však 16bitového systému Windows není velmi dobrý na vyčištění prostředků ukončení úkolu a ani je WOWExec aplikaci VDM. Pokud hledáte cleanest možný přístup k ukončení 16bitové aplikace v systému Windows NT, měli byste zvážit ukončení celého procesu VDM. POZNÁMKA: Při spuštění 16bitové aplikace, který může později ukončit, pak použijte CREATE_SEPARATE_WOW_VDM s CreateProcess().

Ukázkový kód

Ukázkový kód implementuje metody popsané výše pro 16bitové a 32bitové aplikace používající následující dvě funkce: TerminateApp() a Terminate16App(). TerminateApp() trvá proces 32bitové ID a časový limit (v miliseconds). Terminate16App(). Obě funkce používají explicitní propojení funkcí knihovny DLL tak, aby byly binární kompatibilní v systému Windows NT a 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 ;
}

Vlastnosti

ID článku: 178893 - Poslední kontrola: 12. 1. 2017 - Revize: 1

Váš názor