15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Использование Ловушек.

Установка и удаление ловушек

Установить ловушку можно при помощи функции SetWindowsHookEx, указав при этом: 1) тип ловушки; 2) диапазон её действия: на всю систему (глобальная ловушка) или только на текущий поток (локальная); 3) указатель на точку входа в процедуру.

Если ловушка будет глобальной, то её процедуру необходимо разместить в динамически загружаемой библиотеке (DLL). Поэтому, перед тем, как установить глобальную ловушку, приложению понадобится дескриптор модуля DLL. Чтобы получить дескриптор модуля DLL, необходимо воспользоваться функцией LoadLibrary с именем DLL. После того, как дескриптор будет получен, можно воспользоваться функцией GetProcAddress, чтобы получить указатель на точку входа в процедуру ловушки. В заключение, вызывается SetWindowsHookEx, чтобы прописать адрес процедуры ловушки в цепочке ловушек. Описанная последовательно выглядит следующим образом:

HOOKPROC hkprcSysMsg; 
static HINSTANCE hinstDLL; 
static HHOOK hhookSysMsg; 
 
hinstDLL = LoadLibrary((LPCTSTR) "c:\\windows\\sysmsg.dll"); 
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc"); 
hhookSysMsg = SetWindowsHookEx(WH_SYSMSGFILTER,hkprcSysMsg,hinstDLL,0); 

Удалить локальную ловушку (удалить её адрес из цепочки ловушек) можно функцией UnhookWindowsHookEx, указав ей дескриптор удаляемой процедуры ловушки.

Глобальную ловушку можно тоже удалить функцией UnhookWindowsHookEx, однако она не освобождает DLL, содержащую процедуру ловушки. Причина заключается в том, что глобальная ловушка вызывается в контексте процесса для каждого приложения, делая неявный вызов функции LoadLibrary для всех этих процессов. Так как использовать функцию FreeLibrary для другого процесса невозможно, то освободить DLL не получится. Это не страшно, так как система автоматически освобождает DLL, после того, как все процессы, связанные с DLL, завершатся либо будет вызвана FreeLibrary и все процессы, которые вызывают процедуру ловушки продолжат обработку за пределами DLL.

Альтернативный способ установки глобальной ловушки заключается в том, чтобы поместить функцию установки ловушки в DLL вместе с процедурой ловушки. При таком подходе, приложению, которое устанавливает ловушку не нужно получать дескриптор модуля DLL. Связавшись с DLL, приложение получает доступ к функции инталяции. Функция инсталяции может легко получить дескриптор модуля DLL, а так же другие значения, необходимые для вызова функции SetWindowsHookEx. DLL так же может функцию, которая удаляет процедуру глобальной ловушки; а приложение может вызывать эту функцию при завершении своей работы.

Отслеживание системных событий

Следующий пример показывает как использовать различные локальные ловушки, для того, чтобы отслеживать при помощи каких событий система взаимодействует с текущим потоком. В примере используются следующие типы ловушек:

  • WH_CALLWNDPROC
  • WH_CBT
  • WH_DEBUG
  • WH_GETMESSAGE
  • WH_KEYBOARD
  • WH_MOUSE
  • WH_MSGFILTER

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

Пример

#define NUMHOOKS 7

// Глобальные переменные

typedef struct _MYHOOKDATA
{
    int nType;
    HOOKPROC hkprc;
    HHOOK hhook;
} MYHOOKDATA;

MYHOOKDATA myhookdata[NUMHOOKS];

LRESULT WINAPI MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam,
    LPARAM lParam)
{
    static BOOL afHooks[NUMHOOKS];
    int index;
    static HMENU hmenu;

    switch (uMsg)
    {
        case WM_CREATE:
 
            // Сохраняем дескриптор меню.

            hmenu = GetMenu(hwndMain);

            // Инициализируем структуры данными ловушки. Пункты меню
            // определены в заголовочном файле с номерами с 0 по 6
 
            myhookdata[IDM_CALLWNDPROC].nType = WH_CALLWNDPROC;
            myhookdata[IDM_CALLWNDPROC].hkprc = CallWndProc;
            myhookdata[IDM_CBT].nType = WH_CBT;
            myhookdata[IDM_CBT].hkprc = CBTProc;
            myhookdata[IDM_DEBUG].nType = WH_DEBUG;
            myhookdata[IDM_DEBUG].hkprc = DebugProc;
            myhookdata[IDM_GETMESSAGE].nType = WH_GETMESSAGE;
            myhookdata[IDM_GETMESSAGE].hkprc = GetMsgProc;
            myhookdata[IDM_KEYBOARD].nType = WH_KEYBOARD;
            myhookdata[IDM_KEYBOARD].hkprc = KeyboardProc;
            myhookdata[IDM_MOUSE].nType = WH_MOUSE;
            myhookdata[IDM_MOUSE].hkprc = MouseProc;
            myhookdata[IDM_MSGFILTER].nType = WH_MSGFILTER;
            myhookdata[IDM_MSGFILTER].hkprc = MessageProc;

            // Выставляем все флаги массива в FALSE.

            memset(afHooks, FALSE, sizeof(afHooks));

            return 0;
 
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                 // Пользователь выбирает ловушку из меню.

                case IDM_CALLWNDPROC:
                case IDM_CBT:
                case IDM_DEBUG:
                case IDM_GETMESSAGE:
                case IDM_KEYBOARD:
                case IDM_MOUSE:
                case IDM_MSGFILTER:

                    // Используем номера пунктов меню в качестве индекса
                    // в массиве структур с данными ловушки.

                    index = LOWORD(wParam); 

                    // Если ловушка выбранного типа не установлена,
                    // то устанавливаем её и добавляем галочку к
                    // соответствующему пункту меню.

                    if (!afHooks[index])
                    {
                        myhookdata[index].hhook = SetWindowsHookEx(
                            myhookdata[index].nType,
                            myhookdata[index].hkprc,
                            (HINSTANCE) NULL, GetCurrentThreadId());
                        CheckMenuItem(hmenu, index,
                            MF_BYCOMMAND | MF_CHECKED);
                        afHooks[index] = TRUE;
                    }

                    // Если ловушка выбранного типа уже установлена,
                    // то удаляем её и убираем галочку в соответствующем
                    // пункте меню.

                    else
                    {
                        UnhookWindowsHookEx(myhookdata[index].hhook);
                        CheckMenuItem(hmenu, index,
                            MF_BYCOMMAND | MF_UNCHECKED);
                        afHooks[index] = FALSE;
                    }

                default:
                    return (DefWindowProc(hwndMain, uMsg, wParam,
                        lParam));
            }
            break;

            //
            // Обрабатываем другие сообщения.
            //

        default:
            return DefWindowProc(hwndMain, uMsg, wParam, lParam);
    }
    return NULL;
}

/****************************************************************
  Процедура ловушки WH_CALLWNDPROC
 ****************************************************************/

LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szCWPBuf[256];
    CHAR szMsg[16];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
                wParam, lParam);

    // Вызываем функцию, которая преобразует константу сообщения в
    // строку и копирует её буфер.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);

    switch (nCode)
    {
        case HC_ACTION:
	   hResult = StringCchPrintf(szCWPBuf, 256/sizeof(TCHAR),
           "CALLWNDPROC - tsk: %ld, msg: %s, %d times   ",
           wParam, szMsg, c++);
	   if (FAILED(hResult))
	     {
	     // обработчик ошибки
	     }
	   hResult = StringCchLength(szCWPBuf, 256/sizeof(TCHAR), pcch);
	   if (FAILED(hResult))
	     {
	     // обработчик ошибки
	     }
           TextOut(hdc, 2, 15, szCWPBuf, *pcch);
           break;
 
        default:
            break;
    }

    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[CALLWNDPROC].hhook, nCode,
        wParam, lParam);
} 

/****************************************************************
  Процедура ловушки WH_GETMESSAGE
 ****************************************************************/

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szMSGBuf[256];
    CHAR szRem[16];
    CHAR szMsg[16];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0) // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
            wParam, lParam);

    switch (nCode)
    {
      case HC_ACTION:
        switch (wParam)
         {
           case PM_REMOVE:
	     hResult = StringCchCopy(szRem, 16/sizeof(TCHAR), "PM_REMOVE");
	     if (FAILED(hResult))
		{
		// обработчик ошибки
		}
             break;
 
           case PM_NOREMOVE:
	     hResult = StringCchCopy(szRem, 16/sizeof(TCHAR), "PM_NOREMOVE");
		if (FAILED(hResult))
		  {
		  // обработчик ошибки
		  }
                  break;
 
             default:
		hResult = StringCchCopy(szRem, 16/sizeof(TCHAR), "Unknown");
		if (FAILED(hResult))
		  {
		  // обработчик ошибки
		  }
                break;
         }

         // Вызываем функцию, которая преобразует константу сообщения в
         // строку и копирует её буфер.

         LookUpTheMessage((PMSG) lParam, szMsg);

         hdc = GetDC(hwndMain);
	 hResult = StringCchPrintf(szMSGBuf, 256/sizeof(TCHAR),
                "GETMESSAGE - wParam: %s, msg: %s, %d times   ",
                szRem, szMsg, c++);
	 if (FAILED(hResult))
	    {
	    // обработчик ошибки
	    }
	 hResult = StringCchLength(szMSGBuf, 256/sizeof(TCHAR), pcch);
	 if (FAILED(hResult))
	    {
	    // обработчик ошибки
	    } 
         TextOut(hdc, 2, 35, szMSGBuf, *pcch);
         break;

        default:
            break;
    } 

    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[GETMESSAGE].hhook, nCode,
        wParam, lParam); 
} 
 
/****************************************************************
  Процедура ловушки WH_DEBUG
 ****************************************************************/

LRESULT CALLBACK DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szBuf[128];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;
 
    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[DEBUG].hhook, nCode,
            wParam, lParam);

    hdc = GetDC(hwndMain);

    switch (nCode)
    {
      case HC_ACTION:
	hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),   
                "DEBUG - nCode: %d, tsk: %ld, %d times   ", 
                nCode,wParam, c++);
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
	hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), pcch);
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        TextOut(hdc, 2, 55, szBuf, *pcch);
        break;

      default:
        break;
    }

    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[DEBUG].hhook, nCode, wParam,
        lParam);
}
 
/**************************************************************** 
  Процедура ловушки WH_CBT
 ****************************************************************/ 

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szBuf[128];
    CHAR szCode[128];
    HDC hdc;
    static int c = 0;
    size_t cch; 
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam,
            lParam);

    hdc = GetDC(hwndMain);

    switch (nCode)
    {
      case HCBT_ACTIVATE:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_ACTIVATE");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_CLICKSKIPPED:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_CLICKSKIPPED");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_CREATEWND:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_CREATEWND");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_DESTROYWND:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_DESTROYWND");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_KEYSKIPPED:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_KEYSKIPPED");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_MINMAX:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_MINMAX");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_MOVESIZE:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_MOVESIZE");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_QS:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_QS");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_SETFOCUS:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_SETFOCUS");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case HCBT_SYSCOMMAND:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_SYSCOMMAND");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      default:
	hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "Unknown");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;
    }

    hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),
        "CBT -  nCode: %s, tsk: %ld, %d times   ",
        szCode, wParam, c++);

    if (FAILED(hResult))
	{
	// обработчик ошибки
	}
    hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), pcch);
    if (FAILED(hResult))
	{
	// обработчик ошибки
	} 
    TextOut(hdc, 2, 75, szBuf, *pcch);
    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[CBT].hhook, nCode, wParam, lParam);
} 

/****************************************************************
  Процедура ловушки WH_MOUSE
 ****************************************************************/

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szBuf[128];
    CHAR szMsg[16];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[MOUSE].hhook, nCode,
            wParam, lParam);

    // Вызываем функцию, которая преобразует константу сообщения в
    // строку и копирует её буфер.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);
	hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),
        "MOUSE - nCode: %d, msg: %s, x: %d, y: %d, %d times   ",
        nCode, szMsg, LOWORD(lParam), HIWORD(lParam), c++);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
	hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), pcch);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
    TextOut(hdc, 2, 95, szBuf, *pcch);
    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[MOUSE].hhook, nCode, wParam,
        lParam);
}
 
/****************************************************************
  Процедура ловушки WH_KEYBOARD
 ****************************************************************/

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szBuf[128];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode,
            wParam, lParam);

    hdc = GetDC(hwndMain);
	hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),
           "KEYBOARD - nCode: %d, vk: %d, %d times ", nCode, wParam, c++);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
	hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), pcch);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
    TextOut(hdc, 2, 115, szBuf, *pcch);
    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[KEYBOARD].hhook, nCode, wParam,
        lParam);
}
 
/****************************************************************
  Процедура ловушки WH_MSGFILTER
 ****************************************************************/

LRESULT CALLBACK MessageProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    CHAR szBuf[128];
    CHAR szMsg[16];
    CHAR szCode[32];
    HDC hdc;
    static int c = 0;
    size_t cch;
	size_t * pcch;
	HRESULT hResult;

    if (nCode < 0)  // не обрабатываем сообщение
        return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
            wParam, lParam);

    switch (nCode)
    {
      case MSGF_DIALOGBOX:
	hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_DIALOGBOX");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case MSGF_MENU:
	hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_MENU");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      case MSGF_SCROLLBAR:
	hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_SCROLLBAR");
	if (FAILED(hResult))
	  {
	  // обработчик ошибки
	  }
        break;

      default:
	hResult = StringCchPrintf(szCode, 128/sizeof(TCHAR), "Unknown: %d", nCode);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
        break;
    }

    // Вызываем функцию, которая преобразует константу сообщения в
    // строку и копирует её буфер.

    LookUpTheMessage((PMSG) lParam, szMsg);

    hdc = GetDC(hwndMain);
	hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),
        "MSGFILTER  nCode: %s, msg: %s, %d times    ",
        szCode, szMsg, c++);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
	hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), pcch);
	if (FAILED(hResult))
	{
	// обработчик ошибки
	}
    TextOut(hdc, 2, 135, szBuf, *pcch);
    ReleaseDC(hwndMain, hdc);
    return CallNextHookEx(myhookdata[MSGFILTER].hhook, nCode,
        wParam, lParam);
}