Сообщений 3    Оценка 274        Оценить  
Система Orphus

Как определить, что приложение не отвечает?

Автор: Александр Федотов
Опубликовано: 03.11.2001
Исправлено: 13.03.2005
Версия текста: 1.0

Тестовое приложение Process Viewer
Тестовое приложение HUNGTEST

Когда-то, во времена Windows 3.1, все приложения имели одну общую очередь событий ввода. Если одно из приложений по каким-то причинам переставало обрабатывать сообщения, это легко могло привести к зависанию всей системы. Времена изменились, и теперь, в 32-битной и многопоточной Windows, каждый поток имеет свою очередь ввода. Однако, зависающие приложения не исчезли. Как отличить приложения, которые добросовестно обрабатывают сообщения от тех, что перестали реагировать на запросы пользователя, мы и обсудим в этой статье.

Мы рассмотрим два способра решения этой задачи. Один из них, с использованием функции SendMessageTimeout, - тот, который Microsoft рекомендует в своей базе знаний. Другой - тот, который на самом деле используют приложения Microsoft. В обоих случаях функция для определения зависших приложений будет иметь следующий прототип:

BOOL IsAppHung(
    IN HWND hWnd,      // идентификатор окна
    OUT PBOOL pbHung   // указатель на флаг
    );

Здесь, hWnd - идентификатор главного окна приложения, а pbHung - указатель на переменную типа BOOL, которую функция устанавливает в TRUE, если приложение действительно не отвечает.

Использование функции SendMessageTimeout

Функция SendMessageTimeout посылает сообщение указанному окну. Функция примечательна тем, что в случае, если окно принадлежит другому потоку, она не возвращает управления до тех пор, пока окно не обработает сообщение, либо не истечет указанный интервал времени. Кроме того, если указан флаг SMTO_ABORTIFHUNG, и похоже, что вызываемое приложение зависло, функция возвращает управление сразу же, не дожидаясь, когда истечет таймаут. Именно это свойство SendMessageTimeout позволяет использовать ее для определения зависших приложений.

BOOL IsAppHung_SMTO(
    IN HWND hWnd,
    OUT PBOOL pbHung
    )
{
    _ASSERTE(pbHung != NULL);

    *pbHung = FALSE;

    if (!IsWindow(hWnd))
        return SetLastError(ERROR_INVALID_PARAMETER), FALSE;

    DWORD_PTR dwResult;
    if (!SendMessageTimeout(hWnd, WM_NULL, 0, 0, 
                            SMTO_ABORTIFHUNG|SMTO_BLOCK, 500,
                            &dwResult))
        *pbHung = TRUE;

    return TRUE;
}

Мы посылаем сообщение WM_NULL главному окну проверяемого приложения. Если это сообщение будет успешно доставлено окну, то окно его просто проигнорирует, если же SendMessageTimeout вернет ошибку, мы считаем, что приложение зависло.

Использование недокументированных функций IsHungAppWindow и IsHungThread

Как уже было замечено, программы Microsoft, в частности Windows NT Task Manager и Windows 95 Task List не используют SendMessageTimeout для определения зависших приложений. Вместо этого они пользуются двумя недокументированными функциями.

В Windows NT/2000 библиотека USER32.DLL экспортирует функцию IsHungAppWindow, прототип которой приведен ниже. Task Manager использует эту функцию, чтобы обнаружить приложения, которые перестали отвечать на сообщения.

BOOL WINAPI IsHungAppWindow(
    IN HWND hWnd          // идентификатор окна
    );

В Windows 9x/Me USER32.DLL экспортирует аналогичную функцию под названием IsHungThread, которая отличается от своего NT-собрата тем, что принимает не идентификатор окна, а идентификатор потока.

BOOL WINAPI IsHungThread(
    IN DWORD dwThreadId   // идентификатор потока
    );

Вооруженные этими знаниями, мы можем написать другую функцию для определения зависших приложений:

BOOL IsAppHung_Undoc(
    IN HWND hWnd,
    OUT PBOOL pbHung
    )
{
    if (!IsWindow(hWnd))
        return SetLastError(ERROR_INVALID_PARAMETER), FALSE;
    
    OSVERSIONINFO osvi;
    osvi.dwOSVersionInfoSize = sizeof(osvi);

    // определяем версию операционной системы
    GetVersionEx(&osvi);

    // получаем описатель USER32.DLL
    HINSTANCE hUser = GetModuleHandle(_T("user32.dll"));
    _ASSERTE(hUser != NULL);

    if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
    {
        BOOL (WINAPI * _IsHungAppWindow)(HWND);

        // находим функцию IsHungAppWindow
        *(FARPROC *)&_IsHungAppWindow =
            GetProcAddress(hUser, "IsHungAppWindow");
        if (_IsHungAppWindow == NULL)
            return SetLastError(ERROR_PROC_NOT_FOUND), FALSE;

        // вызываем IsHungAppWindow
        *pbHung = _IsHungAppWindow(hWnd);
    }
    else
    {
        DWORD dwThreadId = GetWindowThreadProcessId(hWnd, NULL);

        BOOL (WINAPI * _IsHungThread)(DWORD);

        // находим функцию IsHungThread
        *(FARPROC *)&_IsHungThread =
            GetProcAddress(hUser, "IsHungThread");
        if (_IsHungThread == NULL)
            return SetLastError(ERROR_PROC_NOT_FOUND), FALSE;

        // вызываем IsHungThread
        *pbHung = _IsHungThread(dwThreadId);
    }

    return TRUE;
}

Новая функция начинает свою работу с определения версии Windows. Если программа выполняется на Windows NT, то она находит и вызывает функцию IsHungAppWindow. При работе на Windows 9x, функция сперва определяет идентификатор потока, которому принадлежит указанное окно, а затем вызывает IsHungThread. Все очень просто.

Заключение

Таким образом, мы рассмотрели два способа для определения приложений, которые перестали отвечать на запросы пользователей. В процессе тестирования я обнаружил, что функция SendMessageTimeout для только что повисшего приложения возвращает управление с небольшой задержкой, хотя и сообщает о том, что приложение зависло. Через некоторое время, когда система убеждается, что приложение действительно повисло, она начинает возвращать управление сразу. IsHungAppWindow и IsHungThread не имеют этого недостатка.

Чтобы проверить описанные в этой статье функции, вы можете воспользоваться тестовым приложением Process Viewer. В качестве зависающего приложения можно использовать небольшую программу HUNGTEST, которая также прилагается к этой статье.

Cсылки

  1. Q231844 HOWTO: Detect If an Application Has Stopped Responding, Microsoft Knowledge Base.
  2. How the Windows Task Manager determines when the application is not responding?, Ashot Oganesyan.

Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 3    Оценка 274        Оценить