. 훅 프로시저

훅 체인에 등록되어 메시지를 감시하는 함수를 훅 프로시저(Hook Procedure)라고 한다. 훅 타입에 따라 훅 프로시저의 인수나 리턴값의 의미는 달라지지만 원형은 고정되어 있다. 다음은 WH_KEYBOARD 타입의 키보드 훅 프로시저인데 다른 타입의 훅 프로시저도 이름만 다르고 원형은 동일하다.

 

LRESULT CALLBACK KeyboardProc( int code, WPARAM wParam, LPARAM lParam);

 

훅 프로시저는 응용 프로그램이 제공하는 콜백 함수이므로 원형만 제대로 지킨다면 이름은 마음대로 정할 수 있다. 세 개의 인수를 가지는데 첫 번째 인수 code는 훅 프로시저에서 이 메시지를 어떻게 처리할 것인가를 알려주며 이 값이 음수이면 훅 프로시저는 이 메시지를 처리하지 말고 다음 훅 프로시저에게 메시지를 넘겨야 한다. wParam, lParam은 전달된 메시지에 대한 추가 정보들인데 실제 의미는 훅 타입에 따라 달라지므로 각 타입별로 레퍼런스를 참고해야 한다. 훅 프로시저를 설치할 때는 다음 함수를 사용한다.

 

HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );

 

첫 번째 인수 idHook은 설치하고자 하는 훅의 타입을 지정하며 WH_로 시작되는 매크로 상수중 하나를 써 주면 된다.  lpfn은 훅 프로시저의 번지이며 hMod는 훅 프로시저를 가진 인스턴스 핸들이다. dwThreadId는 훅 프로시저가 감시할 스레드의 ID이되 이 값이 0이면 시스템의 모든 스레드에서 발생하는 메시지가 훅 프로시저로 전달된다. 자신의 메시지를 훅킹할 때는 GetCurrentThreadId 함수로 현재 스레드의 ID를 넘겨주면 된다. 시스템의 모든 메시지를 감시하고자 한다거나 다른 프로그램의 메시지를 감시하고자 할 경우 lpfn은 반드시 분리된 DLL에 있어야 하며 이때 hMod는 이 DLL의 핸들이어야 한다. 다음은 지역 훅과 전역 훅을 설치하는 일반적인 방법이다.

 

지역 훅 : SetWindowsHookEx(idHook, lpfn, NULL, GetCurrentThreadId());

전역 훅 : SetWindowsHookEx(idHook, lpfn, hDll, 0);

 

SetWindowsHookEx 함수는 훅 프로시저를 설치한 후 HHOOK 타입의 훅 핸들을 리턴해 주는데 이 핸들은 해제를 위해 전역변수에 잘 보관해 두어야 한다. 만약 에러가 발생했다면 NULL을 리턴한다. 훅 프로시저를 해제하는 함수는 다음과 같다.

 

BOOL UnhookWindowsHookEx( HHOOK hhk );

 

해제하고자 하는 훅 핸들을 넘겨주기만 하면 된다. 훅을 설치한 프로그램은 종료되기 전에 반드시 훅 프로시저를 해제해 주어야 한다. 훅 프로시저가 설치되면 해당 타입의 메시지는 목표 윈도우로 보내지기 전에 훅 프로시저에게 먼저 전달되는데 훅 프로시저는 메시지를 살펴본 후 특별한 이유가 없으면 메시지를 훅 체인의 다음 훅 프로시저에게 전달해 주어야 한다. 이때는 다음 함수를 사용한다.

 

LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM lParam );

 

hhk는 현재 처리하고 있는 훅의 핸들인데 SetWindowsHookEx 함수가 리턴한 값이다. 나머지 세 인수는 운영체제가 훅 프로시저에게 전달해준 인수들이다. 훅 체인에 포함된 훅 프로시저의 목록은 운영체제가 직접 관리하기 때문에 훅을 설치한 응용 프로그램은 다음 훅 프로시저의 번지를 따로 저장할 필요없이 이 함수만 호출해 주면 훅 체인을 따라 모든 훅 프로시저가 순서대로 호출되며 최종적으로 목표 윈도우로 메시지가 전달될 것이다.

훅 프로시저는 전달 받은 메시지를 다음 훅 프로시저에게 꼭 전달해 주어야 할 의무는 없으며 메시지를 아예 없애버리려면 전달하지 않아도 상관없으며 원하는 대로 변경할 수도 있다. 물론 이 때는 메시지를 없애버리거나 변경한 후의 효과에 대해 확실히 책임질 수 있어야 한다. 이 함수는 훅 체인에서 다음 훅 프로시저를 호출하고 훅 프로시저가 리턴하는 값을 다시 리턴해 주는데 현재의 훅 프로시저는 이 리턴값을 또 그대로 리턴해 주어야 한다. 그래서 훅 프로시저의 끝은 보통 return CallNextHookEx(...) 호출문이 온다.

다음은 윈도우즈가 제공하는 훅 타입들이다. 훅 타입에 따라 감시하고 취급할 수 있는 메시지의 종류가 달라진다.

 

 

훅 타입

설명

WH_CALLWNDPROC,

WH_CALLWNDPROCRET

SendMessage 함수로 메시지를 보내기 전에 WH_CALLWNDPROC 훅 프로시저가 호출되며 윈도우 프로시저가 메시지를 처리한 후에 WH_CALLWNDPROCRET 훅 프로시저가 호출된다. WH_CALLWNDPROCRET 훅은 훅 프로시저에게 CWPRETSTRUCT 구조체를 전달하는데 이 구조체에는 메시지와 메시시를 처리한 리턴값을 담고 있다.

WH_CBT

윈도우를 생성, 파괴, 활성화, 최대, 최소, 이동, 크기변경하기 전에, 시스템 명령을 처리하기 전에, 마우스나 키보드 메시지를 메시지 큐에서 제거하기 전에 이 훅 프로시저가 호출된다. 이 훅은 컴퓨터를 이용한 훈련 프로그램(Computer Based Training)에서 주로 사용된다.

WH_DEBUG

다른 타입의 훅 프로시저를 호출하기 전에 이 타입의 훅 프로시저를 호출하며 다른 타입의 훅 프로시저 호출을 허가할 것인지를 결정한다.

WH_GETMESSAGE

GetMessage나 PeekMessage 함수로 조사되는 메시지를 감시한다.

WH_JOURNALRECORD

키보드나 마우스를 통해 입력되는 이벤트를 감시하고 기록한다. 기록된 이벤트는 WH_JOURNALPLAYBACK 훅에서 재생할 수 있다. 이 훅은 전역으로만 설치할 수 있으며 특정 스레드에만 설치할 수는 없다.

WH_JOURNALPLAYBACK

시스템 메시지 큐에 메시지를 삽입할 수 있도록 한다. 이 훅에서 WH_JOURNALRECORD 훅에서 기록한 키보드 마우스 입력을 재생할 수 있다. 이 훅이 설치되어 있으면 마우스나 키보드 입력은 금지된다. 이 훅은 전역으로만 설치할 수 있으며 특정 스레드에만 설치할 수는 없다.

WH_KEYBOARD

WM_KEYDOWN, WM_KEYUP 등의 키보드 메시지를 감시한다.

WH_MOUSE

마우스 메시지를 감시한다.

WH_MSGFILTER,

WH_SYSMSGFILTER

메뉴, 스크롤 바, 메시지 박스, 대화상자 등에 의해 처리되는 메시지와 사용자의 Alt+Tab키, Alt+Esc키 입력에 의한 포커스 이동을 감시한다. WH_MSGFILTER훅은 훅 프로시저를 설치한 프로그램에 대해서만 동작하며 WH_SYSMSGFILTER 훅은 모든 프로그램에 대해서 동작한다.

WH_SHELL

쉘 프로그램이 활성화되거나 새로운 최상위 윈도우가 만들어지거나 파괴될 때 이 훅 프로시저가 호출된다.

WH_FOREGROUNDIDLE

포그라운드 스레드가 한가해질 때 이 훅 프로시저가 호출된다. 아이들 시에 우선 순위가 낮은 작업을 하고 싶을 때 이 훅을 사용한다.

WH_KEYBOARD_LL

스레드의 입력큐에 붙여지는 키보드 입력 메시지를 감시한다. WH_KEYBOARD보다 더 저수준의 메시지를 받을 수 있지만 NT 4.0 SP 3 이후에만 사용할 수 있다.

WH_MOUSE_LL

스레드의 입력큐에 붙여지는 마우스 입력 메시지를 감시한다.

 

각 훅 타입에 따라 사용되는 구조체나 리턴값, 훅 프로시저의 인수가 다르므로 상세한 정보는 레퍼런스를 참조하기 바란다.