/*========================================================================= Program: KWSys - Kitware System Library Module: $RCSfile$ Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #define KWSYS_IN_PROCESS_C #include "kwsysPrivate.h" #include KWSYS_HEADER(ProcessWin32Kill.h) /* The following process tree kill implementation is taken from http://www.alexfedotov.com/articles/killproc.asp It will work only on some versions of windows. Hopefully I will eventually get some time to do a real implementation of this for all windows versions. */ #include #include #include #include #include #include //--------------------------------------------------------------------------- // KillProcess // // Terminates the specified process. // // Parameters: // dwProcessId - identifier of the process to terminate // // Returns: // TRUE, if successful, FALSE - otherwise. // static BOOL WINAPI KillProcess( IN DWORD dwProcessId ) { HANDLE hProcess; DWORD dwError; // first try to obtain handle to the process without the use of any // additional privileges hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); if (hProcess == NULL) { if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE; OSVERSIONINFO osvi; // determine operating system version osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); // we cannot do anything else if this is not Windows NT if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) return SetLastError(ERROR_ACCESS_DENIED), FALSE; // enable SE_DEBUG_NAME privilege and try again TOKEN_PRIVILEGES Priv, PrivOld; DWORD cbPriv = sizeof(PrivOld); HANDLE hToken; // obtain the token of the current thread if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, FALSE, &hToken)) { if (GetLastError() != ERROR_NO_TOKEN) return FALSE; // revert to the process token if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE; } if(!(ANYSIZE_ARRAY > 0)) { return 0; } Priv.PrivilegeCount = 1; Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Priv.Privileges[0].Luid); // try to enable the privilege if (!AdjustTokenPrivileges(hToken, FALSE, &Priv, sizeof(Priv), &PrivOld, &cbPriv)) { dwError = GetLastError(); CloseHandle(hToken); return SetLastError(dwError), FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { // the SE_DEBUG_NAME privilege is not present in the caller's // token CloseHandle(hToken); return SetLastError(ERROR_ACCESS_DENIED), FALSE; } // try to open process handle again hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); dwError = GetLastError(); // restore the original state of the privilege AdjustTokenPrivileges(hToken, FALSE, &PrivOld, sizeof(PrivOld), NULL, NULL); CloseHandle(hToken); if (hProcess == NULL) return SetLastError(FALSE), NULL; } // terminate the process if (!TerminateProcess(hProcess, (UINT)-1)) { dwError = GetLastError(); CloseHandle(hProcess); return SetLastError(dwError), FALSE; } CloseHandle(hProcess); // completed successfully return TRUE; } typedef LONG NTSTATUS; typedef LONG KPRIORITY; #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) #define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) #define SystemProcessesAndThreadsInformation 5 typedef struct _CLIENT_ID { DWORD UniqueProcess; DWORD UniqueThread; } CLIENT_ID; typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING; typedef struct _VM_COUNTERS { SIZE_T PeakVirtualSize; SIZE_T VirtualSize; ULONG PageFaultCount; SIZE_T PeakWorkingSetSize; SIZE_T WorkingSetSize; SIZE_T QuotaPeakPagedPoolUsage; SIZE_T QuotaPagedPoolUsage; SIZE_T QuotaPeakNonPagedPoolUsage; SIZE_T QuotaNonPagedPoolUsage; SIZE_T PagefileUsage; SIZE_T PeakPagefileUsage; } VM_COUNTERS; typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONG WaitTime; PVOID StartAddress; CLIENT_ID ClientId; KPRIORITY Priority; KPRIORITY BasePriority; ULONG ContextSwitchCount; LONG State; LONG WaitReason; } SYSTEM_THREADS, * PSYSTEM_THREADS; // Note that the size of the SYSTEM_PROCESSES structure is different on // NT 4 and Win2K, but we don't care about it, since we don't access neither // IoCounters member nor Threads array typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; #if _WIN32_WINNT >= 0x500 IO_COUNTERS IoCounters; #endif SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES, * PSYSTEM_PROCESSES; //--------------------------------------------------------------------------- // KillProcessTreeNtHelper // // This is a recursive helper function that terminates all the processes // started by the specified process and them terminates the process itself // // Parameters: // pInfo - processes information // dwProcessId - identifier of the process to terminate // // Returns: // Win32 error code. // static BOOL WINAPI KillProcessTreeNtHelper( IN PSYSTEM_PROCESSES pInfo, IN DWORD dwProcessId ) { if(!pInfo) { return 0; } PSYSTEM_PROCESSES p = pInfo; // kill all children first for (;;) { if (p->InheritedFromProcessId == dwProcessId) KillProcessTreeNtHelper(pInfo, p->ProcessId); if (p->NextEntryDelta == 0) break; // find the address of the next process structure p = (PSYSTEM_PROCESSES)(((LPBYTE)p) + p->NextEntryDelta); } // kill the process itself if (!KillProcess(dwProcessId)) return GetLastError(); return ERROR_SUCCESS; } //--------------------------------------------------------------------------- // KillProcessTreeWinHelper // // This is a recursive helper function that terminates all the processes // started by the specified process and them terminates the process itself // // Parameters: // dwProcessId - identifier of the process to terminate // // Returns: // Win32 error code. // static BOOL WINAPI KillProcessTreeWinHelper( IN DWORD dwProcessId ) { HINSTANCE hKernel; HANDLE (WINAPI * _CreateToolhelp32Snapshot)(DWORD, DWORD); BOOL (WINAPI * _Process32First)(HANDLE, PROCESSENTRY32 *); BOOL (WINAPI * _Process32Next)(HANDLE, PROCESSENTRY32 *); // get handle to KERNEL32.DLL hKernel = GetModuleHandle(_T("kernel32.dll")); if(!hKernel) { return 0; } // locate necessary functions in KERNEL32.DLL *(FARPROC *)&_CreateToolhelp32Snapshot = GetProcAddress(hKernel, "CreateToolhelp32Snapshot"); *(FARPROC *)&_Process32First = GetProcAddress(hKernel, "Process32First"); *(FARPROC *)&_Process32Next = GetProcAddress(hKernel, "Process32Next"); if (_CreateToolhelp32Snapshot == NULL || _Process32First == NULL || _Process32Next == NULL) return ERROR_PROC_NOT_FOUND; HANDLE hSnapshot; PROCESSENTRY32 Entry; // create a snapshot hSnapshot = _CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) return GetLastError(); Entry.dwSize = sizeof(Entry); if (!_Process32First(hSnapshot, &Entry)) { DWORD dwError = GetLastError(); CloseHandle(hSnapshot); return dwError; } // kill all children first do { if (Entry.th32ParentProcessID == dwProcessId) KillProcessTreeWinHelper(Entry.th32ProcessID); Entry.dwSize = sizeof(Entry); } while (_Process32Next(hSnapshot, &Entry)); CloseHandle(hSnapshot); // kill the process itself if (!KillProcess(dwProcessId)) return GetLastError(); return ERROR_SUCCESS; } //--------------------------------------------------------------------------- // KillProcessEx // // Terminates the specified process and, optionally, all processes started // from the specified process (the so-called process tree). // // Parameters: // dwProcessId - identifier of the process to terminate // bTree - specifies whether the entire process tree should be // terminated // // Returns: // TRUE, if successful, FALSE - otherwise. // static BOOL WINAPI KillProcessEx( IN DWORD dwProcessId, IN BOOL bTree ) { if (!bTree) return KillProcess(dwProcessId); OSVERSIONINFO osvi; DWORD dwError; // determine operating system version osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion < 5) { HINSTANCE hNtDll; NTSTATUS (WINAPI * _ZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG); // get handle to NTDLL.DLL hNtDll = GetModuleHandle(_T("ntdll.dll")); if(!hNtDll) { return 0; } // find the address of ZwQuerySystemInformation *(FARPROC *)&_ZwQuerySystemInformation = GetProcAddress(hNtDll, "ZwQuerySystemInformation"); if (_ZwQuerySystemInformation == NULL) return SetLastError(ERROR_PROC_NOT_FOUND), NULL; // obtain a handle to the default process heap HANDLE hHeap = GetProcessHeap(); NTSTATUS Status; ULONG cbBuffer = 0x8000; PVOID pBuffer = NULL; // it is difficult to say a priory which size of the buffer // will be enough to retrieve all information, so we start // with 32K buffer and increase its size until we get the // information successfully do { pBuffer = HeapAlloc(hHeap, 0, cbBuffer); if (pBuffer == NULL) return SetLastError(ERROR_NOT_ENOUGH_MEMORY), FALSE; Status = _ZwQuerySystemInformation( SystemProcessesAndThreadsInformation, pBuffer, cbBuffer, NULL); if (Status == STATUS_INFO_LENGTH_MISMATCH) { HeapFree(hHeap, 0, pBuffer); cbBuffer *= 2; } else if (!NT_SUCCESS(Status)) { HeapFree(hHeap, 0, pBuffer); return SetLastError(Status), NULL; } } while (Status == STATUS_INFO_LENGTH_MISMATCH); // call the helper function dwError = KillProcessTreeNtHelper((PSYSTEM_PROCESSES)pBuffer, dwProcessId); HeapFree(hHeap, 0, pBuffer); } else { // call the helper function dwError = KillProcessTreeWinHelper(dwProcessId); } SetLastError(dwError); return dwError == ERROR_SUCCESS; } extern "C" { int kwsysProcessWin32Kill(int pid) { return KillProcessEx(pid, 1)? 1:0; } }