Щоб акуратно завершити додаток, давши йому зробити необхідне очищення, ми повинні послати повідомлення WM_CLOSE всім видимим вікнам верхнього рівня, що належить додатком. Посилка повідомлення WM_CLOSE еквівалентна команді Закрити системного меню вікна. Якщо додаток написано правильно, то воно звільнить ресурси і завершиться. Ми повинні бути готові до того, що процес завершення може затягнутися. Hапример, додаток може запитати у користувача, чи хоче він зберегти змінені файли. Якщо програма не завершиться за розумний час, нам не залишиться нічого, крім як застосувати "важку артилерію" - функцію TerminateProcess.
Щоб надати максимальну гнучкість в організації очікування, ми вирішили написати функцію зупинки додатки відповідно до наведеного нижче прототипом.
Параметр hWnd ідентифікує додаток, яке ми хочемо завершити, це може бути будь-яке вікно, яке належить додатком. Параметр pfnWaitCallback задає для користувача функцію, яка буде періодично викликатися в процесі очікування. Програма може використовувати цю фунцию для обробки повідомлень, поки функція очікує завершення зупиняє процес. Крім того, призначена для користувача функція визначає, коли вже досить чекати і пора застосувати грубу силу: яке значення функції визначає подальшу поведінку KillApplication.
Негайно завершити процес за допомогою TerminateProcess
Спочатку функція визначає ідентифікатори процесу і потоку, яким належить вказане вікно - вони нам стануть в нагоді в подальшому для ідентифікації зупинятися додатки. Потім функція перевіряє, чи не належить вказане вікно 16-бітної задачі в Windows NT. 16-бітові завдання вимагають спеціальної обробки і ми розглянемо їх окремо.
У 32-бітному випадку, функція відкриває описатель (handle) процесу з правами доступу SYNCHRONIZE і PROCESS_TERMINATE. Якщо ми не маємо таких прав по відношенню до завершують процес, функція відразу ж завершується з помилкою. Потім функція розсилає повідомлення WM_CLOSE всіх вікон верхнього рівня, що належить цьому процесу, для чого ми використовуємо EnumWindows. вказавши в якості опції перерахування функцію KillAppEnumWindows. текст якої наведено нижче.
Після того, як повідомлення розіслані, функція переходить в цикл очікування. Кожні 100 мілісекунд вона викликає призначену для користувача функцію, даючи їй можливість обробити накопичені повідомлення і прийняти рішення, чи варто продовжувати очікування. Вихід з циклу відбувається, коли трапиться одне з двох подій: процес завершиться і WaitForSingleObject поверне WAIT_OBJECT_0, або призначена для користувача функція поверне значення, відмінне від KILLAPP_WAIT. Якщо повертається значенням для користувача функції було KILLAPP_TERMINATE, то процес завершується примусово за допомогою TerminateProcess.
Як уже зазначалося, 16-бітові завдання обробляються особливим чином. Головна їхня відмінність від 32-бітових завдань для нас полягає в тому, що кілька 16-бітових завдань можуть розділяти один процес WOW VDM, тому та логіка, яку ми використовували для завершення 32-бітних додатків є непридатною. Перш за все, ось код функції IsWOWProcess. яка використовується для того, щоб відрізнити 16-бітові завдання:
IsWOWProcess перераховує все віртуальні DOS-машини WOW за допомогою функції VDMEnumProcessWOW з VDMDBG.DLL. Якщо вікно, вказане в якості параметра KillApplication. належить одному з цих процесів, значить, ми маємо справу з 16-бітної завданням.
Для 16-бітних завдань ми теж розсилаємо повідомлення WM_CLOSE всіх вікон верхнього рівня цього завдання, але тепер ми орієнтуємося не на основі ідентифікатора процесу, а за ідентифікатором потоку, оскільки процес віртуальної DOS-машини може містити кілька 16-бітових завдань, кожна з яких виконується в своєму потоці. Для розсилки WM_CLOSE ми використовуємо EnumWindows з функцією KillAppEnumWindows16 в якості опції перерахування:
У разі 16-бітних завдань ми не можемо використовувати описувач процесу для очікування завершення завдання, оскільки процес віртуальної DOS-машини може й не завершитися, якщо в ньому залишилися інші завдання. Для визначення факту завершення завдання ми використовуємо функцію IsWOWTask. яка в якості побічного ефекту повертає ідентифікатор завдання.
IsWOWTask покладається на функцію VDMEnumTaskWOW. експортовану з VDMDBG.DLL, яка була розглянута в статті Як перерахувати 16-бітові завдання під Windows NT ?.
Як і в випадку 32-бітних додатків, ми викликаємо для користувача функцію з інтервалом в 100 мілісекунд. Якщо для користувача функція вирішує завершити завдання примусово, ми викликаємо функцію TerminateWOWTask. яка є ні що інше як обгортка навколо VDMTerminateTaskWOW.
На закінчення розглянемо кілька варіантів реалізації користувальницької функції для використання спільно з KillApplication. Перший варіант функції реалізує очікування з фіксованим таймаут:
Передбачається, що в якості параметра lParam для KillApplication буде вказано час закінчення очікування. Наприклад, такий виклик призведе до 15-секундному очікуванню:
Більш складний варіант для користувача функції, наведений нижче, обробляє повідомлення в процесі очікування, так що функцію можна викликати безпосередньо в потоці призначеного для користувача інтерфейсу, не побоюючись того, що він буде заблокований:
Ще більш складний варіант, який обробляє повідомлення і відображає діалогове вікно після закінчення 15 секунд очікування, ви можете знайти в вихідному коді демонстраційного додатки Process Viewer, яке супроводжує цю статтю.
- Q178893 HOWTO: Terminate an Application "Cleanly" in Win32. Microsoft Knowledge Base.