Багато з нас стикалися з ситуацією, коли при знаходженні небажаних процесів в пам'яті комп'ютера, ми не могли їх завершити через диспетчер задач, оскільки таких процесів зазвичай два: кожен “слідкує” чи не завершився часом інший, і якщо завершився — повторно його запускає. А швидкості рук для завершення їх до того, як вони запускають один одного не достатньо. Тому ми сьогодні поговоримо про програмний спосіб завершення виконання таких процесів.
Визначаємо усі процеси в системі
Для того щоб розпізнати, які саме процеси виконуються в системі, ми будемо використовувати три функції: 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, можна перевірити, чи поточне ім'я процесу відповідає одному з тих, які необхідно завершити; або ж програму можна зробити віконною, що полегшить додавання нових програм в список і т.д.