Использование перехватчиков
В следующих примерах кода показано, как выполнять следующие задачи, связанные с перехватчиками:
Установка и освобождение процедур перехватчика
Вы можете установить процедуру перехвата, вызвав функцию SetWindowsHookEx и указав тип перехвата, определив, должна ли процедура быть связана со всеми потоками в том же рабочем столе, что и вызывающий поток, или с конкретным потоком, а также указав указатель на точку входа процедуры.
Необходимо разместить глобальную процедуру перехватчика в библиотеке DLL отдельно от приложения, устанавливающем процедуру перехватчика. Приложение, выполняющее установку, должно сначала получить дескриптор модуля DLL, прежде чем сможет установить процедуру перехватчика. Чтобы получить дескриптор модуля DLL, вызовите функцию LoadLibrary с именем библиотеки DLL. Получив дескриптор, вы можете вызвать функцию GetProcAddress, чтобы получить указатель на процедуру перехватчика. Наконец, используйте SetWindowsHookEx, чтобы установить адрес процедуры перехватчика в соответствующую цепочку перехватчиков. SetWindowsHookEx передает дескриптор модуля, указатель на точку входа процедуры перехватчика и 0 для идентификатора потока, указывая, что процедура перехватчика должна быть связана со всеми потоками в одном рабочем столе с вызывающим потоком. Эта последовательность показана в следующем примере.
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;
hinstDLL = LoadLibrary(TEXT("c:\\myapp\\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
Пользователь может установить и удалить процедуру хука через меню. После установки процедуры перехватчика и события, отслеживаемого процедурой, процедура записывает сведения о событии в клиентскую область главного окна приложения.
#include <windows.h>
#include <strsafe.h>
#include "app.h"
#pragma comment( lib, "user32.lib")
#pragma comment( lib, "gdi32.lib")
#define NUMHOOKS 7
// Global variables
typedef struct _MYHOOKDATA
{
int nType;
HOOKPROC hkprc;
HHOOK hhook;
} MYHOOKDATA;
MYHOOKDATA myhookdata[NUMHOOKS];
HWND gh_hwndMain;
// Hook procedures
LRESULT WINAPI CallWndProc(int, WPARAM, LPARAM);
LRESULT WINAPI CBTProc(int, WPARAM, LPARAM);
LRESULT WINAPI DebugProc(int, WPARAM, LPARAM);
LRESULT WINAPI GetMsgProc(int, WPARAM, LPARAM);
LRESULT WINAPI KeyboardProc(int, WPARAM, LPARAM);
LRESULT WINAPI MouseProc(int, WPARAM, LPARAM);
LRESULT WINAPI MessageProc(int, WPARAM, LPARAM);
void LookUpTheMessage(PMSG, LPTSTR);
LRESULT WINAPI MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static BOOL afHooks[NUMHOOKS];
int index;
static HMENU hmenu;
gh_hwndMain = hwndMain;
switch (uMsg)
{
case WM_CREATE:
// Save the menu handle
hmenu = GetMenu(hwndMain);
// Initialize structures with hook data. The menu-item identifiers are
// defined as 0 through 6 in the header file app.h. They can be used to
// identify array elements both here and during the WM_COMMAND message.
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;
// Initialize all flags in the array to FALSE.
memset(afHooks, FALSE, sizeof(afHooks));
return 0;
case WM_COMMAND:
switch (LOWORD(wParam))
{
// The user selected a hook command from the menu.
case IDM_CALLWNDPROC:
case IDM_CBT:
case IDM_DEBUG:
case IDM_GETMESSAGE:
case IDM_KEYBOARD:
case IDM_MOUSE:
case IDM_MSGFILTER:
// Use the menu-item identifier as an index
// into the array of structures with hook data.
index = LOWORD(wParam);
// If the selected type of hook procedure isn't
// installed yet, install it and check the
// associated menu item.
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;
}
// If the selected type of hook procedure is
// already installed, remove it and remove the
// check mark from the associated menu item.
else
{
UnhookWindowsHookEx(myhookdata[index].hhook);
CheckMenuItem(hmenu, index,
MF_BYCOMMAND | MF_UNCHECKED);
afHooks[index] = FALSE;
}
default:
return (DefWindowProc(hwndMain, uMsg, wParam,
lParam));
}
break;
//
// Process other messages.
//
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}
/****************************************************************
WH_CALLWNDPROC hook procedure
****************************************************************/
LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szCWPBuf[256];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
size_t cch;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_CALLWNDPROC].hhook, nCode, wParam, lParam);
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(gh_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))
{
// TODO: writer error handler
}
hResult = StringCchLength(szCWPBuf, 256/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 15, szCWPBuf, cch);
break;
default:
break;
}
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_CALLWNDPROC].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_GETMESSAGE hook procedure
****************************************************************/
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;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_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))
{
// TODO: write error handler
}
break;
case PM_NOREMOVE:
hResult = StringCchCopy(szRem, 16/sizeof(TCHAR), "PM_NOREMOVE");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
default:
hResult = StringCchCopy(szRem, 16/sizeof(TCHAR), "Unknown");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
}
// Call an application-defined function that converts a
// message constant to a string and copies it to a
// buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(gh_hwndMain);
hResult = StringCchPrintf(szMSGBuf, 256/sizeof(TCHAR),
"GETMESSAGE - wParam: %s, msg: %s, %d times ",
szRem, szMsg, c++);
if (FAILED(hResult))
{
// TODO: write error handler
}
hResult = StringCchLength(szMSGBuf, 256/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 35, szMSGBuf, cch);
break;
default:
break;
}
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_GETMESSAGE].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_DEBUG hook procedure
****************************************************************/
LRESULT CALLBACK DebugProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
size_t cch;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_DEBUG].hhook, nCode,
wParam, lParam);
hdc = GetDC(gh_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))
{
// TODO: write error handler
}
hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 55, szBuf, cch);
break;
default:
break;
}
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_DEBUG].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_CBT hook procedure
****************************************************************/
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
CHAR szCode[128];
HDC hdc;
static int c = 0;
size_t cch;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_CBT].hhook, nCode, wParam,
lParam);
hdc = GetDC(gh_hwndMain);
switch (nCode)
{
case HCBT_ACTIVATE:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_ACTIVATE");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_CLICKSKIPPED:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_CLICKSKIPPED");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_CREATEWND:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_CREATEWND");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_DESTROYWND:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_DESTROYWND");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_KEYSKIPPED:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_KEYSKIPPED");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_MINMAX:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_MINMAX");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_MOVESIZE:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_MOVESIZE");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_QS:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_QS");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_SETFOCUS:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_SETFOCUS");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case HCBT_SYSCOMMAND:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "HCBT_SYSCOMMAND");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
default:
hResult = StringCchCopy(szCode, 128/sizeof(TCHAR), "Unknown");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
}
hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR), "CBT - nCode: %s, tsk: %ld, %d times ",
szCode, wParam, c++);
if (FAILED(hResult))
{
// TODO: write error handler
}
hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 75, szBuf, cch);
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_CBT].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_MOUSE hook procedure
****************************************************************/
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
CHAR szMsg[16];
HDC hdc;
static int c = 0;
size_t cch;
HRESULT hResult;
if (nCode < 0) // do not process the message
return CallNextHookEx(myhookdata[IDM_MOUSE].hhook, nCode,
wParam, lParam);
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(gh_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))
{
// TODO: write error handler
}
hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 95, szBuf, cch);
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_MOUSE].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_KEYBOARD hook procedure
****************************************************************/
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CHAR szBuf[128];
HDC hdc;
static int c = 0;
size_t cch;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_KEYBOARD].hhook, nCode,
wParam, lParam);
hdc = GetDC(gh_hwndMain);
hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR), "KEYBOARD - nCode: %d, vk: %d, %d times ", nCode, wParam, c++);
if (FAILED(hResult))
{
// TODO: write error handler
}
hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 115, szBuf, cch);
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_KEYBOARD].hhook, nCode, wParam, lParam);
}
/****************************************************************
WH_MSGFILTER hook procedure
****************************************************************/
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;
HRESULT hResult;
if (nCode < 0) // do not process message
return CallNextHookEx(myhookdata[IDM_MSGFILTER].hhook, nCode,
wParam, lParam);
switch (nCode)
{
case MSGF_DIALOGBOX:
hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_DIALOGBOX");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case MSGF_MENU:
hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_MENU");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
case MSGF_SCROLLBAR:
hResult = StringCchCopy(szCode, 32/sizeof(TCHAR), "MSGF_SCROLLBAR");
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
default:
hResult = StringCchPrintf(szCode, 128/sizeof(TCHAR), "Unknown: %d", nCode);
if (FAILED(hResult))
{
// TODO: write error handler
}
break;
}
// Call an application-defined function that converts a message
// constant to a string and copies it to a buffer.
LookUpTheMessage((PMSG) lParam, szMsg);
hdc = GetDC(gh_hwndMain);
hResult = StringCchPrintf(szBuf, 128/sizeof(TCHAR),
"MSGFILTER nCode: %s, msg: %s, %d times ",
szCode, szMsg, c++);
if (FAILED(hResult))
{
// TODO: write error handler
}
hResult = StringCchLength(szBuf, 128/sizeof(TCHAR), &cch);
if (FAILED(hResult))
{
// TODO: write error handler
}
TextOut(hdc, 2, 135, szBuf, cch);
ReleaseDC(gh_hwndMain, hdc);
return CallNextHookEx(myhookdata[IDM_MSGFILTER].hhook, nCode, wParam, lParam);
}