How to kill a process given a name






4.55/5 (31 votes)
Dec 16, 2002
3 min read

464862
Shows how to kill a process given only a process name, and how to make it work for all Windows OS's (except 3.1)
Introduction
The idea is to create a class that will kill a process given the process name, and work on every single Windows platform (except 3.1), without requiring additional redistributable components. So the tricky part here isn't actually killing the process (there's a great KB article on how to do this correctly.) The problem is that you need a PID to do that, and you don't have that intrinsically, given only a name. So the trick is now how to enumerate the processes, get the PID, and not require any additional components while we're at it.
This is actually easier than it seems, because all Windows except NT4 have ToolHelp32 API which accomplishes just that goal. NT4, however, is a pain in the behind, because it does have PSAPI to do this also, but it comes on a redistributable dll, which means additional installation hassles. So I looked on Google, and found Alex Fedotov's terrific article, which lists five different ways to skin a rabb.. er, I mean enumerate a process. Method 3 was the one I needed, so I filched it, and used it in my code. Method 3 involves ZwQuerySystemInformation
, an undocumented NT API that's available for NT4, 2000, and onward.
At this point, you might go over to Google (or to NT4 DDK), discover that function, and also discover that Win2K has different internal structures used with this function than NT4. That's ok, because we don't use this on Win2K, or anything else besides NT4. Basically the idea is we try to load ToolHelp32 functions first, and only if that fails (which automatically means we're on NT4) do we try to use the Zw function.
Anyway, without further ado, here's the code. By the way, I tested it on 98, ME, NT4, 2k, and XP, different language versions too, and all seemed to work just fine. Share and enjoy. :)
Code
I hope you'll forgive me for the laziness, but I didn't feel like making a sample project for such a small application. Really, I've written no new code here, just packaged Alex's, moliate's and Microsoft's code a little bit better. So here it is:
#include <tlhelp32.h> // // Some definitions from NTDDK and other sources // 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; class CKillProcess { private: //Functions loaded from Kernel32 typedef HANDLE (WINAPI *PFCreateToolhelp32Snapshot)( DWORD dwFlags, DWORD th32ProcessID ); typedef BOOL (WINAPI *PFProcess32First)( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ); typedef BOOL (WINAPI *PFProcess32Next)( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ); // Native NT API Definitions typedef NTSTATUS (WINAPI * PFZwQuerySystemInformation) (UINT, PVOID, ULONG, PULONG); typedef HANDLE (WINAPI* PFGetProcessHeap)(VOID); typedef LPVOID (WINAPI* PFHeapAlloc) (HANDLE,DWORD,SIZE_T); typedef BOOL (WINAPI* PFHeapFree)(HANDLE,DWORD,LPVOID); public: CKillProcessHelper() : FCreateToolhelp32Snapshot(NULL), FProcess32First(NULL), FProcess32Next(NULL), m_hKernelLib(NULL), m_hNTLib(NULL) { m_hKernelLib = ::LoadLibraryA("Kernel32"); if (m_hKernelLib) { // Find ToolHelp functions FCreateToolhelp32Snapshot = (PFCreateToolhelp32Snapshot) ::GetProcAddress(m_hKernelLib, _TEXT("CreateToolhelp32Snapshot")); FProcess32First = (PFProcess32First) ::GetProcAddress(m_hKernelLib, _TEXT("Process32First")); FProcess32Next = (PFProcess32Next) ::GetProcAddress(m_hKernelLib, _TEXT("Process32Next")); } if(!FCreateToolhelp32Snapshot || !FProcess32First || !FProcess32Next) { // i.e. we couldn't find the ToolHelp functions, //so we must be on NT4. Let's load the // undocumented one instead. if(!m_hKernelLib) return; // can't do anything at all without //the kernel. m_hNTLib = ::LoadLibraryA("ntdll.dll"); if(m_hNTLib) { FQuerySysInfo = (PFZwQuerySystemInformation) ::GetProcAddress(m_hNTLib, _TEXT("ZwQuerySystemInformation")); // load some support funcs from the kernel FGetProcessHeap = (PFGetProcessHeap) ::GetProcAddress(m_hKernelLib, _TEXT("GetProcessHeap")); FHeapAlloc = (PFHeapAlloc) ::GetProcAddress(m_hKernelLib, _TEXT("HeapAlloc")); FHeapFree = (PFHeapFree) ::GetProcAddress(m_hKernelLib, _TEXT("HeapFree")); } } } ~CKillProcessHelper() { if(m_hKernelLib) FreeLibrary(m_hKernelLib); if(m_hNTLib) FreeLibrary(m_hNTLib); } bool KillProcess(IN const char* pstrProcessName) { DWORD dwId; HANDLE hProcess = FindProcess(pstrProcessName, dwId); BOOL bResult; if(!hProcess) return false; // TerminateAppEnum() posts WM_CLOSE to all windows whose PID // matches your process's. ::EnumWindows((WNDENUMPROC) CKillProcessHelper::TerminateAppEnum, (LPARAM) dwId); // Wait on the handle. If it signals, great. //If it times out, then you kill it. if(WaitForSingleObject(hProcess, 5000) !=WAIT_OBJECT_0) bResult = TerminateProcess(hProcess,0); else bResult = TRUE; CloseHandle(hProcess); return bResult == TRUE; } private: HANDLE FindProcess(IN const char* pstrProcessName, OUT DWORD& dwId) { if(!m_hKernelLib) return NULL; if(FCreateToolhelp32Snapshot && FProcess32First && FProcess32Next) // use toolhelpapi return THFindProcess(pstrProcessName, dwId); if(FQuerySysInfo && FHeapAlloc && FGetProcessHeap && FHeapFree) // use NT api return NTFindProcess(pstrProcessName, dwId); // neither one got loaded. Strange. return NULL; } HANDLE THFindProcess(IN const char* pstrProcessName, OUT DWORD& dwId) { HANDLE hSnapShot=NULL; HANDLE hResult = NULL; PROCESSENTRY32 processInfo; char* pstrExeName; bool bFirst = true; ::ZeroMemory(&processInfo, sizeof(PROCESSENTRY32)); processInfo.dwSize = sizeof(PROCESSENTRY32); hSnapShot = FCreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0); if(hSnapShot == INVALID_HANDLE_VALUE) return NULL; // ok now let's iterate with Process32Next until we // match up the name of our process while((bFirst ? FProcess32First(hSnapShot, &processInfo) : FProcess32Next(hSnapShot, &processInfo))) { bFirst = false; // we need to check for path... and extract // just the exe name pstrExeName = strrchr(processInfo.szExeFile, '\\'); if(!pstrExeName) pstrExeName = processInfo.szExeFile; else pstrExeName++; // skip the \ // ok now compare against our process name if(stricmp(pstrExeName, pstrProcessName) == 0) // wee weee we found it { // let's get a HANDLE on it hResult=OpenProcess( SYNCHRONIZE|PROCESS_TERMINATE, TRUE, processInfo.th32ProcessID); dwId = processInfo.th32ProcessID; break; } } // while(Process32Next(hSnapShot, &processInfo){ if(hSnapShot) CloseHandle(hSnapShot); return hResult; } HANDLE NTFindProcess(IN const char* pstrProcessName, OUT DWORD& dwId) { HANDLE hHeap = FGetProcessHeap(); NTSTATUS Status; ULONG cbBuffer = 0x8000; PVOID pBuffer = NULL; HANDLE hResult = NULL; // it is difficult to say a priory which size of // the buffer will be enough to retrieve all // information, so we startwith 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), NULL; Status = FQuerySysInfo( 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); PSYSTEM_PROCESSES pProcesses = (PSYSTEM_PROCESSES)pBuffer; for (;;) { PCWSTR pszProcessName = pProcesses->ProcessName.Buffer; if (pszProcessName == NULL) pszProcessName = L"Idle"; CHAR szProcessName[MAX_PATH]; WideCharToMultiByte(CP_ACP, 0, pszProcessName, -1,szProcessName, MAX_PATH, NULL, NULL); if(stricmp(szProcessName, pstrProcessName) == 0) // found it { hResult=OpenProcess( SYNCHRONIZE|PROCESS_TERMINATE, TRUE, pProcesses->ProcessId); dwId = pProcesses->ProcessId; break; } if (pProcesses->NextEntryDelta == 0) break; // find the address of the next process // structure pProcesses = (PSYSTEM_PROCESSES)( ((LPBYTE)pProcesses) + pProcesses->NextEntryDelta); } HeapFree(hHeap, 0, pBuffer); return hResult; } // callback function for window enumeration static BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam ) { DWORD dwID ; GetWindowThreadProcessId(hwnd, &dwID) ; if(dwID == (DWORD)lParam) { PostMessage(hwnd, WM_CLOSE, 0, 0) ; } return TRUE ; } HMODULE m_hNTLib; HMODULE m_hKernelLib; // ToolHelp related functions PFCreateToolhelp32Snapshot FCreateToolhelp32Snapshot; PFProcess32First FProcess32First; PFProcess32Next FProcess32Next; // native NT api functions PFZwQuerySystemInformation FQuerySysInfo; PFGetProcessHeap FGetProcessHeap; PFHeapAlloc FHeapAlloc; PFHeapFree FHeapFree; };
Credits
As I said before, this code is really a compilation of three different articles:
- moliate's article on how to enumerate processes using ToolHelp API and PSAPI
- Alex Fedotov's article on how to do the same, but with Zw-function
- KB's article on how to terminate a process cleanly
Copyright
(you knew it was coming...)
The code is completely, utterly, and absolutely free. Feel free to use it in private or commercial applications, modify it, sit on it, or print it out and use it for toilet paper. God knows I've ignored enough copyrights from CodeProject to have the nerve to make my own. :) One thing though -- if this code messes up your computer or puts your project seven months behind schedule, it's not my fault. None of it is my fault. Not even that "format c:" somewhere in there. Er, just kidding. :) Oh yeah, needless to say, you can't claim this code as your own. (I.E. I'd hate for someone to reprint this code, and copyright the hell out of it. Not sure if that's possible anyhow, but it would suck. Greatly.)
Conclusion
Probably a pointless thing to say, but if you're going to rate the article badly, at least take the time to write a comment saying how I could improve it, or what you find inadequate about it.