У меня есть программа (запускается как фоновый процесс), в которой я установил хук для захвата событий EVENT_SYSTEM_FOREGROUND (т.е. когда пользователь переключается между окнами). Обратный вызов, который зарегистрирован для ловушки, в основном регистрирует, какое приложение (имя файла процесса) пользователь переключил от и которые они перешли в.
Я хочу добавить код, чтобы проверить, переключилось ли приложение от все еще активен (если мы не предполагаем, что они закрыли это, и именно это вывело новое окно на передний план). Я проверяю его существование, пытаясь создать дескриптор предыдущего PID, используя OpenProcess
//Check prev pid still exists - if not, assume the previous app has been closed
HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
if (hPrevProc==NULL){
prevProcStillRunning=false;
}
else{
CloseHandle(hPrevProc);
}
Предположения с кодом выше:
g_prevPid заполнен PID — я проверил это
prevProcStillRunning был инициализирован как true
Проблема с приведенным выше кодом заключается в том, что по какой-то причине, даже если пользователь вышел из приложения (например, notepad.exe). В течение до 10 секунд после выхода этот тест все еще проходит (т.е. инициализируется hPrevProc). Несмотря на то, что в диспетчере задач я вижу, что процесс Notepad.exe исчез (и да, у меня открыт только один его экземпляр), каким-то образом строка OpenProcess все еще может получить указатель на этот PID. Я предполагаю, что каким-то образом PID на самом деле все еще существует, но он может находиться в состоянии, когда он заканчивается. Я обнаружил, что если этот код вызывается еще несколько раз, в конечном итоге он вернет значение null.
Я хотел бы узнать, как лучше проверить, активен ли hPrevProc.
Я пытался проверить это с помощью GetExitCodeProcess функции, но это, кажется, просто дает мне PID, и я даже не уверен, что это правильный подход в любом случае.
Любая помощь приветствуется.
Возможно, какой-то процесс (может быть, ваш?) Все еще содержит действительный дескриптор этого процесса. До тех пор, пока CloseHandle не вызывался на всех дескрипторах, система поддерживала внутреннюю запись, позволяющую получить доступ к своим данным процесса. Это важно, потому что, как вы говорите, должна быть возможность вызова GetExitCodeProcess для закрытого процесса, также кто-то может захотеть дождаться его остановки с помощью WaitForSingleObject.
Также будьте осторожны с PID, они могут быть использованы повторно, поэтому теоретически вы можете вызывать OpenProcess для какого-то другого недавно открытого процесса.
Что касается проверки, не является ли данный процесс зомби, вы можете попробовать перечислить окна верхнего уровня с помощью EnumWindows и проверить, связано ли какое-либо из них с данным PID (для получения PID окна используйте GetWindowThreadProcessID).
Процесс существует в системе после его завершения, по крайней мере, пока есть открытый дескриптор.
Единственный надежный способ узнать, активен ли процесс, это:
OpenProcess
) -> если вы не можете это прекращаетсяGetExitCodeProcess
) -> если это не STILL_ACTIVE, процесс прекращается.Ваш код может стать:
//Check prev pid still exists - if not, assume the previous app has been closed
HANDLE hPrevProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,false,g_prevPid);
if (hPrevProc==NULL){
prevProcStillRunning=false;
}
else{
DWORD cr;
if ((GetExitCodeProcess(hPrevProc, &cr) == 0) || (cr != STILL_ACTIVE)) {
prevProcStillRunning=false;
}
CloseHandle(hPrevProc);
}
В любом случае закрытие приложения с графическим интерфейсом включает в себя различные шаги:
Событие будет отправлено, как только будет закрыто главное окно, что может произойти за некоторое время до фактической остановки приложения. Хороший пример для этого — Firefox. Если вы закроете окно и сразу попытаетесь запустить новый процесс, вы получите сообщение об ошибке, потому что даже если пользовательский интерфейс пропал, процесс еще не завершен. Что еще хуже, вы можете найти приложения, которые просто уходят в фоновый режим, когда вы закрываете пользовательский интерфейс, и позволяете пользователю снова открыть пользовательский интерфейс с помощью действия над значком в области состояния панели задач (Shell_NotifyIcon
и его обратный вызов). Это характерно для сервисов других приложений, работающих в фоновом режиме (сетевых серверов, брандмауэров и т. Д.). В этом случае пользовательский интерфейс пропал, но процесс не завершится.
TL / DR: время между отображением пользовательского интерфейса и завершением процесса, которому он принадлежит, является переменным и зависит от загрузки системы и фоновой активности процесса после закрытия пользовательского интерфейса. Вы можете попытаться использовать задержку для этого, но я не могу гарантировать что-либо об этом …