2008. 8. 4. 23:22

Win32 Hook (MSDN' 1부)

MSDN의 훅에 관련된 내용을 번역한 것입니다.
훅에 관해 자세히 모르는 관계로 잘못된 부분이 많겠지만
대강의 개념을 이해할 수는 있을 것입니다.
잘못된 부분은 지적을 해주시고
다른 참고 자료를 가지신 분이 계시면 알려 주세요...
이게 내용이 길어서 그런지 한번에 올리려고 하니 계속 에러가 나는군요.. -_-
그리고 표는 깨져서 보일 테니 MSDN의 원본을 참조하세요
==================================================
Win32 Hooks
Created: July 29, 1993
Revised: February 1994
Translated : February 14, 2001 by hgycap!

요약
이 문서는 Win32 API에서 사용하는 훅(hook)에 대해 설명하고 있다. 여기서 다룰 내용
은 훅 함수, 필터 함수 그리고 다음과 같은 종류의 훅이다.

WH_CALLWNDPROC
WH_CBT
WH_DEBUG
WH_FOREGROUNDIDLE
WH_GETMESSAGE
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD
WH_MOUSE
WH_MSGFILTER
WH_SHELL
WH_SYSMSGFILTER
용어 이 문서에서 말하는 "윈도우"라는 용어는 16 비트 윈도우, 윈도우 NT 등을 포함
하는 모든 윈도우 운영 체제를 포함한다. "윈도우 3.1"이라고 말하는 것은 그 버전을
지칭하는 말이다.

소개
윈도우 운영 체제에서 훅은 이벤트(메시지, 마우스 움직임, 키보드 입력)가 응용 프로
그램에 도착하기 전에 이벤트를 가로채는 방법을 제공한다. 이 방법을 통해 이벤트에
반응하여 동작하거나 이벤트를 수정 또는 삭제할 수 있다. 이벤트를 받는 함수를 필
터 함수(filter function)라고 부르며 함수가 가로채는 이벤트의 종류에 따라 나뉘어
진다. 예를 들어 어떤 필터 함수는 모든 종류의 키보드와 마우스 이벤트를 받을 수 있
다. 윈도우가 필터 함수를 부르기 위해서는 필터 함수가 윈도우의 훅에 설치, 다른 말
로 하자면 첨가되어 있어야 한다. 하나 이상의 필터 함수를 훅에 첨가하는 것을 "훅
을 설정한다(setting)"고 말한다. 하나의 훅에 둘 이상의 필터 함수가 첨가되어 있는
경우, 윈도우는 일련의 필터 함수로 연결하여 관리한다. 가장 최근에 설치된 함수는
함수열(chain)의 시작 부분에 위치하고 가장 먼저 설치된 함수는 함수열의 끝에 위치
한다.

훅이 하나 이상의 필터 함수를 가지고 있고 어떤 이벤트가 이 훅을 구동시킬 경우, 윈
도우는 필터 함수열의 첫 번째 필터 함수를 호출한다. 이러한 과정을 훅을 호출한다
(calling)고 말한다. 예를 들어 CBT 훅에 필터 함수가 설정되어 있고 이 훅을 구동하
는 이벤트(예를 들면 윈도우가 생성되려고 하는 경우)가 발생하였다면 윈도우는 필터
함수열의 첫 번째 필터 함수를 호출함으로써 훅을 호출한다.

필터 함수를 첨가하거나 제거하기 위해 응용 프로그램은 SetWindowsHookEx와
UnhookWindowsHookEx 함수를 사용한다.

훅은 윈도우 기반 응용 프로그램에 놀라운 능력을 제공한다. 다음의 응용 프로그램은
훅을 사용하여 구현할 수 있다:

응용 프로그램의 다이알로그 박스, 메시지 박스, 스크롤 바, 메뉴 등에 주어지는 모
든 메시지를 처리하거나 수정한다. (WH_MSGFILTER)
 
시스템의 다이알로그 박스, 메시지 박스, 스크롤 바, 메뉴 등에 주어지는 모든 메시지
를 처리하거나 수정한다. (WH_SYSMSGFILTER)


GetMessage나 PeekMessage 함수가 호출되는 경우 시스템의 모든 메시지를 처리하거나
수정한다. (WH_GETMESSAGE)


SendMessage 함수가 호출되는 경우 모든 메시지를 처리하거나 수정한다.
(WH_CALLWNDPROC)


키보드와 마우스의 이벤트를 기록하거나 재생한다. (WH_JOURNALRECORD,
WH_JOURNALPLAYBACK)


키보드 이벤트를 처리, 수정 또는 제거한다. (WH_KEYBOARD)


마우스 이벤트를 처리, 수정 또는 제거한다. (WH_MOUSE)


특정한 system action에 반응하도록 하여 응용 프로그램이 컴퓨터 기반 학습
(Computer Based Training, CBT)이 가능하도록 만들어준다. (WH_CBT)


다른 필터가 호출되지 않도록 한다. (WH_DEBUG)
응용 프로그램은 다음과 같은 경우 훅을 사용해 왔다:

메뉴, 다이알로그 박스, 메시지 박스 등에 F1 도움말 키를 지원하도록 한다.
(WH_MSGFILTER)


마우스와 키보드의 이벤트를 기록하고 재생하는 방법을 제공한다. 이를 종종 매크로라
고 한다. 예를 들어 Windows Recorder 보조 프로그램은 기록과 재생 기능을 위해 훅
을 사용한다. (WH_JOURNALRECORD, WH_JOURNALPLAYBACK).


메시지를 감시하여 어떤 메시지가 어떤 윈도우로 가는지 또는 특정 메시지가 어떤 동
작을 일으키는지 감시한다. (WH_GETMESSAGE, WH_CALLWNDPROC) 윈도우 NT의 Win32 SDK
에 있는 Spy 프로그램은 훅을 사용한다. Spy 프로그램의 소스는 SDK에 있다.


마우스나 키보드 입력을 흉내 낼 수 있다. (WH_JOURNALPLAYBACK) 훅은 이러한 행동을
흉내내기를 보장해줄 수 있는 유일한 방법이다. 이러한 이벤트를 SendMessage나
PostMessage 함수를 사용하여 흉내내려고 한다면 윈도우의 내부는 키보드나 마우스의
상태를 갱신하지 않아서 예기치 않는 행동을 보여주는 경우가 있다. 키보드나 마우스
이벤트를 재생하기 위해 훅을 사용하는 경우, 이들 이벤트는 실제의 키보드나 마우스
이벤트와 동일하게 동작한다. Excel 프로그램은 SEND.KEYS 매크로 함수를 구현하기 위
해 훅을 사용한다.


윈도우 환경에서 동작하는 응용 프로그램을 위한 CBT를 제공한다. WH_CBT 훅은 CBT 응
용 프로그램을 쉽게 만들 수 있는 방법을 제공한다.
훅의 사용 방법
훅을 사용하기 위해서는 다음 사항을 알아야 한다:

훅의 필터 함수열에 필터 함수를 추가하거나 제거하기 위해 윈도우의 훅 함수를 어떻
게 사용하는가?


설치하는 필터 함수는 어떤 동작을 수행하여야 하는가?


어떤 종류의 훅이 존재하며 이들은 어떤 일을 하는가 그리고 이들 훅은 필터 함수로
어떤 정보를 전달하는가?
윈도우 훅 함수
윈도우 기반의 응용 프로그램은 SetWindowsHookEx, UnhookWindowsHookEx,
CallNextHookEx 함수를 사용하여 필터 함수열을 관리한다. 3.1 버전 이전의 윈도우는
SetWindowsHook, UnhookWindowsHook, DefHookProc 함수를 사용하여 훅 관리를 했다.
이들 함수가 Win32에서도 존재하지만 새로운 버전의 Ex 계열 함수에 비해 할 수 있는
일이 적다. 기존의 코드도 새로운 함수를 사용하도록 바꾸는 것이 좋으며 앞으로도 새
로운 함수를 쓰는 것이 좋다.

SetWindowsHookEx와 UnhookWindowsHookEx 함수는 아래에 설명되어 있다.
CallNextHookEx 함수에 대해서는 별도의 문서 "필터 함수열에서 다음 함수의 호출
Calling the next function in the filter function chain"을 참고하면 된다.

SetWindowsHookEx
SetWindowsHookEx 함수는 훅에 필터 함수를 추가한다. 이 함수는 4개의 인자를 가지
고 있다:

HHOOK SetWindowsHookEx(
        int idHook,      // type of hook to install
        HOOKPROC lpfn,   // address of hook procedure
        HINSTANCE hMod,  // handle to application instance
        DWORD dwThreadId  // identity of thread to install hook for
);

필터 함수를 추가할 훅에 대한 정수값 코드. 코드 값은 WINUSER.H에 정의되어 있고 이
후에 설명할 것이다.


필터 함수의 주소. 필터 함수는 응용 프로그램이나 DLL의 모듈 정의 파일 EXPORTS 구
문에 필터 함수를 첨가하거나 적절한 컴파일러 플래그를 써서 export시켜야 한다.


필터 함수를 포함하는 모듈의 인스턴스 핸들. Win32에서는 (Win16과는 다르게) 특정
쓰레드에 사용되는 훅의 경우 이 값이 대부분 NULL이다. 하지만 꼭 NULL이어야 하는
것은 아니다. 시스템 훅이나 다른 프로세스에 있는 쓰레드에 대한 훅인 경우에는 필
터 함수가 있는 DLL의 인스턴스 핸들을 사용하여야 한다.


훅이 설치될 쓰레드의 ID. 쓰레드의 ID가 영(zero)이 아닌 경우, 설치된 필터 함수는
특정 쓰레드의 context에서만 호출된다. 쓰레드의 ID가 영(zero)인 경우, 설치된 필
터 함수는 시스템 전체 범위에 해당되며 시스템의 모든 쓰레드 context에서 호출된
다. 응용 프로그램이나 라이브러리는 훅을 설치할 쓰레드의 핸들을 얻기 위해
GetCurrentThreadId 함수를 사용할 수 있다.
몇몇 훅은 시스템 범위에서만 사용될 수 있고 몇몇 훅은 특정 쓰레드에서만 사용될
수 있다. 하지만 아래 표에서처럼 대부분이 시스템이나 쓰레드 범위에서 사용될 수 있
다.

Hook
 Scope
 
WH_CALLWNDPROC
 Thread or System
 
WH_CBT
 Thread or System
 
WH_DEBUG
 Thread or System
 
WH_GETMESSAGE
 Thread or System
 
WH_JOURNALRECORD
 System Only
 
WH_JOURNALPLAYBACK
 System Only
 
WH_FOREGROUNDIDLE
 Thread or System
 
WH_SHELL
 Thread or System
 
WH_KEYBOARD
 Thread or System
 
WH_MOUSE
 Thread or System
 
WH_MSGFILTER
 Thread or System
 
WH_SYSMSGFILTER
 System Only
 

 

양 쪽 모두에 사용되는 경우 쓰레드 훅이 먼저 호출되고 시스템 훅이 다음에 호출된
다.

여러 가지 이유로 시스템 훅보다는 쓰레드 훅을 사용하는 것이 좋다. 쓰레드 훅의 장
점에는 다음과 같은 것들이 있다:

훅 사용과 무관한 응용 프로그램에 오버헤드를 주지 않는다.


훅을 위한 모든 이벤트가 직렬화되지 않아도 된다. 예를 들어, 응용 프로그램이 시스
템 키보드 훅을 설치하면 모든 응용 프로그램을 위한 모든 키보드 메시지가 키보드 필
터 함수로 집중되어 시스템에서 제공하는 다중 입력 큐 기능을 쓸 수 없게 된다. 만
약 필터 함수가 키보드 이벤트 처리를 멈추면, 실제로는 그렇지 않다고 해도 시스템
이 정지한 것처럼 보일 것이다. 사용자는 이 때에도 CTRL+ALT+DEL 키를 사용하여 log-
out하면 이 문제를 해결할 수 있지만 이러한 혼란이 달가울 리는 없다. 또한 사용자
가 log-out과 log-on을 통해 시스템을 원상태로 할 수 있다는 사실을 모를 수도 있다.


필터 함수 구현을 별도의 DLL로 만들 필요가 없다. 모든 시스템 훅과 다른 응용 프로
그램의 특정 쓰레드를 위한 훅은 DLL로 구현하여야 한다.


서로 다른 프로세스에서 사용되고 있는 동일한 DLL들이 데이터를 공유하지 않아도 된
다. 시스템 범위의 필터 함수는 DLL로 구현하여야 하고 따라서 당여히 다른 프로세스
와 공유할 필요가 있는 데이터는 공유하여야 한다. 하지만 DLL은 기본적으로 데이터
를 공유하지 않으므로 시스템 범위의 필터 함수를 작성할 때는 주의가 필요하다. 데이
터 공유를 잘못한 경우에는 프로세스가 파괴될 수 있다.
SetWindowsHookEx 함수는 설치된 훅에 대한 핸들(HHOOK 형)을 반환한다. 응용 프로그
램이나 라이브러리는 이 핸들을 UnhookWindowsHookEx 함수를 사용하여 훅을 해제할
때 사용한다. 훅에 필터 함수를 추가할 수 없는 경우 SetWindowsHookEx 함수는 NULL
을 반환한다. 또한 SetWindowsHookEx 함수는 수행에 실패한 원인을 나타내기 위해 아
래의 값들 중 하나로 last error를 설정한다. 이들 정보를 얻기 위해서는
GetLastError 함수를 사용하면 된다.

ERROR_INVALID_HOOK_FILTER: 훅 코드가 유효하지 않다.
 
ERROR_INVALID_FILTER_PROC: 필터 함수가 유효하지 않다.
 
ERROR_HOOK_NEEDS_HMOD: 전역적인 즉, 시스템 범위의 훅이 hInstance 인수를 NULL로
하여 설정되었거나, 쓰레드 훅이 훅을 설치하는 응용 프로그램과 동일한 쓰레드에 있
지 않다.


ERROR_GLOBAL_ONLY_HOOK: 시스템 훅으로만 사용할 수 있는 훅을 쓰레드 훅으로 설치하
였다.


ERROR_INVALID_PARAMETER: 쓰레드 ID가 유효하지 않다.


ERROR_JOURNAL_HOOK_SET: journal 훅 형식으로 이미 설치된 필터 함수가 있다.
journal record나 journal playback 훅은 한 번에 하나만 설치될 수 있다. 화면 보호
기가 동작하고 있는 경우 응용 프로그램이 journal 훅을 설치하고자 하는 경우 이런
에러가 발생할 수도 있다.


ERROR_MOD_NOT_FOUND: 전역 훅을 위한 hInstance 인수가 라이브러리가 아니다. (실제
로 이 값은 모듈 핸들을 찾을 수 없을 경우에 해당한다.)


Any other value: 보안 문제로 훅이 설치될 수 없거나 시스템의 메모리가 부족하다.
윈도우는 필터 함수열을 내부적으로 유지하고 다음 필터 함수를 찾기 위해 필터 함수
에 의존하지 않는다. 윈도우 3.1 이전 버전에서는 필터 함수들 자체가 연결되어 있었
다. 따라서 훅은 윈도우 3.1 버전보다 훨씬 견고해졌다. 또한 필터 함수열이 내부적으
로 관리됨으로써 성능도 향상되었다.



윈도우 3.1에서의 필터 함수열

UnhookWindowsHookEx
훅의 함수열에서 필터 함수를 제거하기 위해서는 UnhookWindowsHookEx 함수를 사용한
다. 이 함수는 SetWindowsHookEx 함수에서 반환된 훅의 핸들을 사용하고 훅이 제거되
었는 지의 여부를 반환한다. 영(zero)을 반환하는 경우 제거에 실패한 것으로
GetLastError 함수를 사용하여 자세한 정보를 얻을 수 있다.

출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNo=20&no=1497&ref=659