Багато з нас стикалися з ситуацією, коли при знаходженні небажаних процесів в пам'яті комп'ютера, ми не могли їх завершити через диспетчер задач, оскільки таких процесів зазвичай два: кожен “слідкує” чи не завершився часом інший, і якщо завершився — повторно його запускає. А швидкості рук для завершення їх до того, як вони запускають один одного не достатньо. Тому ми сьогодні поговоримо про програмний спосіб завершення виконання таких процесів.

Визначаємо усі процеси в системі

Для того щоб розпізнати, які саме процеси виконуються в системі, ми будемо використовувати три функції: CreateToolhelp32Snapshot, Process32First, Process32Next. CreateToolhelp32Snapshot ця функція отримує деяку інформацію про процеси (або процес) у системі. Її необхідно викликати перед функціями Process32First, Process32Next, оскільки вони аналізують обробник який ви отримуєте завдяки CreateToolhelp32Snapshot. Передавати їй необхідно два аргументи: перший - це прапорці, які означають, яку саме інформацію про систему Ви бажаєте отримати; і наступний — ідентифікатор процесу, про який ми бажаємо отримати інформацію. В якості прапорця (перший аргумент) ми будемо передавати значення TH32CS_SNAPALL, що означає отримати інформацію про усі процеси (в даному випадку другий аргумент функцією ігнорується, тому ми встановлюємо його в 0). Функції Process32First, Process32Next призначенні отримати інформацію про один окремий процес. Перша - Process32First, отримує інформацію про найперший процес у черзі, а функція Process32Next призначена для отримування інформації про наступні. Передаються їм однакова кількість аргументів: перший це обробник (себто HANDLE) який ми отримали за допомогою функції CreateToolhelp32Snapshot, а другий аргумент функції — це об'єкт типу LPPROCESSENTRY32 (який являється вказівником на структуру tagPROCESSENTRY32). Організація програми повинна виглядати наступним чином:
/* отримуємо необхідний обробник */
HANDLE snap = CreateToolhelp32Snapshot (TH32CS_SNAPALL, 0) ;

if (snap==NULL)
{
    /* помилка */
    return -1 ;
}

LPPROCESSENTRY32 prc = new tagPROCESSENTRY32 ;

if (!Process32First (snap, prc))
{
    /* помилка */
    return -1 ;
}

do
{
    /* код обробки інформації про процеси*/
}
while (!Process32Next(snap, prc)) ;
Отже функції Process32First і Process32Next повертають true у разі успішного виконання операції. Всередині циклу з після умовою (себто конструкція “do{}while();”) ми повинні написати необхідний нам код для оброки інформації про процеси. В даному випадку, зробити рішення — зупинити виконання даного процесу чи ні. Структура tagPROCESSENTRY32 (або ж пам'ять, яка розіменовується через вказівник LPPROCESSENTRY32 за допомогою оператора “->”) виглядає наступним чином (згідно MSDN):
typedef struct tagPROCESSENTRY32 {
    DWORD       dwSize;
    DWORD       cntUsage;
    DWORD       th32ProcessID;
    ULONG_PTR   th32DefaultHeapID;
    DWORD       th32ModuleID;
    DWORD       cntThreads;
    DWORD       th32ParentProcessID;
    LONG        pcPriClassBase;
    DWORD       dwFlags;
    TCHAR       szExeFile[MAX_PATH];
} PROCESSENTRY32, *PPROCESSENTRY32;
Найбільш нас цікавлять змінні th32ProcessID і szExeFile. Змінна Th32ProcessID містить ідентифікатор процесу, а szExeFile містить ім'я виконуваного файлу. За допомогою значень цих змінних ми і будемо приймати рішення про завершення процесу.

Примусове завершення процесу

Для завершення процесу в ОС Віндовс необхідно використовувати дві функції: OpenProcess і TerminateProcess. OpenProcess це функція, яка відкриває процес з певними правами доступу. Ми можемо думати про функцію OpenProcess, як про “рідну” функцію ОС Віндовс для відкриття файлу — OpenFile. Вона повертає дескриптор процесу в разі успішного виконання, або ж NULL у разі помилки. Передаються даній функції три параметри: перший типу DWORD, який означає бажаний рівень доступу до процесу (для нашого випадку це PROCESS_TERMINATE), другий встановлюємо в false, а третім передаємо ідентифікатор процесу у якості типу DWORD. У разі неуспішного завершення виконання функції отримуємо вказівник NULL, в іншому випадку функція повертає обробник (HANDLE) на процес. Функція TerminateProcess, як очевидно, завершує необхідний нам процес. Передаються їй два параметри: перший - це вказівник типу HANDLE на відкритий дескриптор процесу, другий — значення, яке буде використовуватися в якості поверненого значення процесу, який ми закриваємо функцією TerminateProcess.

Програма

І проста програма, яка буде завершувати процес з заданим нами ім'ям, кожних 100 мілісекунд буде виглядати наступним чином:
#include <iostream> /* I/O */
#include <windows.h>
#include <TlHelp32.h> /* функції CreateToolhelp32Snapshot, 
                      **         Process32First і Process32Next */
#include <string> /* std::string */
using namespace std ;

/* головна функція програми */
int main (int argc, char** argv)
{
	HANDLE snap ;
	
	/* в даній структурі міститься інформація про процес */
	LPPROCESSENTRY32 prc = new tagPROCESSENTRY32 ;
	
	/* змінна яка буде містити поточну назву процесу */
	string pname ;
	
	/* процес який необхідно "вбити" 
        ** заміняємо "Plugin.exe" на потрібне */
	string kill1 = "Plugin.exe" ;

    /* перевіряти чи не потрібний процес виконується ми будемо 
    ** кожні 100 мілісекунд до поки не завершимо програму через
    ** диспетчер задач або комбінацією клавіш Ctrl+C у терміналі */
	for (;;)
	{
        /* отримуємо необхідний дескриптор стану системи */
        snap = CreateToolhelp32Snapshot (TH32CS_SNAPALL, 0) ;

	    /* отримуємо найперший процес у черзі */
		Process32First (snap, prc) ;
		
		/* цикл обробки усіх процесів */
		do
		{
		    /* отримуємо назву поточного процесу і 
		    ** копіюємо значення в змінну типу string */
			pname = prc->szExeFile ;
			
			/* перевіряємо, чи це дійсно процес, 
			** який необхідно вбити */
			if (pname==kill1)
			{
			    /* виводимо повідомлення на екран */
				cout << "find bad process: " << prc->th32ProcessID << " " << prc->szExeFile << endl ;
				
				/* відкриваємо процес з правом на завершення */
				HANDLE ph = OpenProcess (PROCESS_TERMINATE, 
				                         FALSE, 
				                         prc->th32ProcessID) ;
				
				/* перевіряємо чи ми отримали валідний дескриптор */
				if (ph==NULL)
				{
					cout << "fail to open process\n" ;
				}
				else
				{
				    /* код виходу процеса */
					int unused = 0 ;
					
					/* "вбиваємо" процес */
					if (TerminateProcess (ph, unused)==0)
					{
						cout << "Fail to terminate process\n" ;
					}
					else
					{
						cout << "process terminated!\n" ;
					}
				}
				
				/* закриваємо обробник процесу */
				CloseHandle (ph) ;
			}
		} /* отримуємо інформацію про наступний процес */
		while (Process32Next (snap, prc)) ;

        /* закриваємо дескриптор стану системи */
	    CloseHandle (snap) ;

		/* "засинаємо" на 100 мілісекунд */
		Sleep (100) ;
	}

    /* вивільняємо пам'ять */	
	delete prc ;
	
	/* завершення поточної програми */
	return 0 ;
}
Для компілювання даної програми можна скористатися Windows SDK, яке можна завантажити на офіційному сайті Майкрософт, командою (main.cxx — це ім'я файлу, в якому міститься вищенаведена програма):
cl main.cxx
Дану програму найкраще запускати від імені адміністратора (права кнопка миші по виконуваному файлі і “Запустити від імені Адміністратора”). Рядок "Plugin.exe", Ви можете замінити на будь-який інший, який Ви можете отримати з диспетчера задач натиснувши правою кнопкою миші по не бажаному процесі з списку усіх процесів, і вибравши пункт меню “Властивості” або “Свойства”. У новому діалоговому вікні ви можете знайти поле імені виконуваного файлу і вставити його замість "Plugin.exe". Також Ви можете невеликими зусиллями покращити програму, для задоволення власних проблем. Наприклад, можна замість однієї змінної типу string використовувати змінну типу vector, в якій, за допомогою алгоритму find з бібліотеки STL, можна перевірити, чи поточне ім'я процесу відповідає одному з тих, які необхідно завершити; або ж програму можна зробити віконною, що полегшить додавання нових програм в список і т.д.
Категорії: