아래의 번역된 글을 이용하여 글을 올린다.

번역문 : http://cafe.naver.com/ArticleRead.nhn?articleid=213&sc=e0d437180f462b9619&clubid=10426365

역    자 : sobahoko
==================================================================================================


원문
: Window Procedures as Class Member Functions 

원문위치:
http://www.rpi.edu/~pudeyo/articles/wndproc/index.html  

원저자
: Oleg Pudeyev 

원문
마지막
수정일:
2003

1

29
 

번역자
: sobahoko 

번역시작일
: 2006.3.11 

번역
마지막
수정일
: 2006.3.18  

클래스 멤버함수를 윈도우 프로시저로 사용하기

 

Oleg
Pudeyev
 

 


문서에서,
(Oleg
Pudeyev)

클래스
멤버함수를
윈도우
프로시저로
사용하는
몇가지
방법에
대해
살펴볼
것이다.
나는
특정
메시지를
특정
멤버함수에
멥핑하는
방법에
대해서는
언급하지
않을
것이다.
모든
예제
코드에서,
하나의
윈도우
프로시저가
모든
메시지를
처리할것이며,
기본적인
처리를
위해서는
DefWindowProc
함수를
호출할
것이다.
여기서
살펴볼
방법들은
쉬운것에서부터
시작하여
복잡한

순으로
나열된다.

문서의
목차는
다음과
같다.

기본
코드 

정직한
방법 

전역
변수 

윈도우
고유
데이터 

CBT
Hooks 

전역
헨들
 

MFC
접근법 

ATL
접근법 

 

 

기본
코드 

나는

글을
진행하기
위해
간단한
Win32
프로그램을
출발점으로
사용하려고
한다.

프로그램은
윈도우를
열고
Hello,
World!
”를
WM_PAINT
메시지에
반응하여
출력하는
기능을
가지고
있다.
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/basecode.cpp
)
에서


있다

 

정직한
방법 

비록
제대로
동작하지
않지만,
윈도우
프로시저를
클래스
내부에
위치시키는
방법은
정직하게
다음처럼
하는
것이다

 

class
Window 


       LRESULT
CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); 

       //
… 

}; 

 

LRESULT
CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 


       //
Access member variables here 


 

//
… 

WNDCLASS
wc = { 

       //
… 

       Window::WndProc, 

       //
… 

}; 

//
… 

 


코드의
전체
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/obvious.cpp
)
에서


있다.
MSVC7
에서
컴파일러는
컴파일에
실패하고
다음
에러
메시지를
낸다

 

obvious.cpp(81)
: error C2440: ‘initializing’ : cannot convert from 

                               ‘LRESULT
(__stdcall Window::* )(HWND,UINT,WPARAM,LPARAM)’ to 

                               ‘WNDPROC’ 

     None
of the functions with this name in scope match the target type 

 

 

MSVC6

다음
에러
메시지를
낸다

 

obvious.cpp(82)
: error C2440: ‘initializing’ : cannot convert from 

                       ‘long
(__stdcall Window::*)(struct HWND__ *,unsigned int,unsigned int,long)’ to 

                       ‘long
(__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long)’ 

     There
is no context in which this conversion is possible 

 


에러는
멤버함수가
전역함수와
다르기
때문에
발생한다.
멤버함수는
숨겨진
this
파라메터를
가지며,
멤버
함수가
호출되었을

그것을
통해서
클래스
인스턴스를
참조한다.
클래스
멤버가
아닌
함수들은
그런
파라메터가
없으므로,
멤버함수와
다르다

 

정직한
방법을
간단히
수정 


에러
메시지는
다음
코드와
같이
멤버함수
WndProc

static
으로
만들면
사라진다

 

class
Window 


       //
… 

       static
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); 

       //
… 

}; 

 

이것으로
Window::WndProc
함수로
WNDCLASS
구조체를
초기화
하는데
사용할

있다.
그러나,
WndProc
함수가
static
이므로,

함수는
HelloString
변수와
같은
인스턴스
내부의
변수들에
접근할

없다.
그러므로
이것은
완전한
해결책이
아니다

 

 

전역
변수 

이것을
해결하는
간단한
방법은
메시지를
처리하는
클래스의
전역
인스턴스를
만들고,
메시지를

전역
인스턴스의
WndProc
함수로
전달해
주는
보조
함수를
만들어
사용하는
것이다.

코드는
아래와
같다

 

Window
w; 

LRESULT
CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 


       return
w.WndProc(hwnd, msg, wp, lp); 


 


방법은
명확하며
신뢰할
만한
방법이다.
그리고

윈도우
클래스의
인스턴스가
오직
하나일

적합한
방법이다.
특별히
응용프로그램에
오직하나의
윈도우가
존재할
,

방법은
좋은
선택이

것이다.
그러나

방법은
클래스의
인스턴스가
여러개이며
메시지가
각각의
인스턴스에서
다르게
처리되어야
하는
상황에
사용할

없다.
이것에
대한
소스코드는
여기
( http://www.rpi.edu/~pudeyo/articles/wndproc/global.cpp )
에서


있다

 

 

윈도우
고유
데이터 

Windows

의해서
생성된는
모든
윈도우의
인스턴스에는
메모리블럭을
할당할

있다.

메모리블럭과

블럭에서
참조되는
변수들을
통칭하여
윈도우
고유
데이터(Per-Window
Data)

부른다.
WNDCLASS
구조체의
cbWndExtra
멤버에
적절한
값을
주어,

데이터
영역의
크기를
지정할

있으며,
읽을때는
GetWindowLong
이나
GetWindowLongPtr

사용하고
쓸때는
SetWindowLong
이나
SetWindowLongPtr

사용한다.
Ptr

붙은
버전은
64-bit Windows

호환되기
때문에
사용이
권장되며,
상위
호환성을
염두에
둔다면
Ptr

붙은
버전을
사용해야
한다

 

우리는
윈도우
고유
데이터
영역에
객체의
포인터를
저장하여
특정
Window
객체와
윈도우를
연결시키려고
한다.
,
어떤
시점에는
연결을
위해서
다음
함수를
호출하고 

 

SetWindowLong(hwnd,
GWL_USERDATA, this); 

 


후에는
Window
포인터를
가져오기
위해서
다음함수를
호출한다는
얘기다

 

Window
*w = (Window *) GetWindowLong(hwnd, GWL_USERDATA); 

 

그러므로
전역
WndProc
함수는
다음과
같을
것이다

 

LRESULT
CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) 


       Window
*w = (Window *) GetWindowLong(hwnd, GWL_USERDATA); 

       if
(w) 

               return
w->WndProc(hwnd, msg, wp, lp); 

       else 

               return
DefWindowProc(hwnd, msg, wp, lp); 


 

if
문은
연결이
완료되기
전에
WndProc
함수가
호출되었을
경우를
처리하기위해
필요하다.
연결은
언제
완료되는가?

언제
SetWindowLong

호출하는가?
아마도
다음
코드와
같이
CreateWindow

호출한
직후에
SetWindowLong

호출하고
싶은
생각이
들것이다.
 

 

Window
w; 

HWND
hwnd = CreateWindow(TEXT(“BaseWnd”), TEXT(“Hello, World!”), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, 0); 

if
(!hwnd) 

       return
-1; 

SetWindowLong(hwnd,
GWL_USERDATA, &w); 

 

불행히도,

방법은
WM_CREATE
메시지를
포함한
많은
메시지를
처리하지못한다.

메시지들은
CreateWindow
함수가
리턴되기
전에
발생하여,

결과로
WndProc
함수가
호출되기
때문이다

그러므로
좀더
이른
시점에
연결을
완료하는
것이
필요하다

 

WM_NCCREATE

희망을
준다.
MSDN
에서는,
이것은
WM_CREATE
전에
보내지며,
우리가
this
포인터를
저장하는데
사용하려고
하는
CREATESTRUCT

포인터를
파라메터로
가진다고
말하고
있다.
CreateWindow
함수는
lpParam
파라메터가
있다.
이것은
CREATESTRUCT

lpCreateParam
멤버와
같은
파라메터이다.
Window
객체
w

포인터를
lpParam

전달함으로써,
우리는
나중에
lpCreateParam

통해서
그것을
다시
가져올

있다.
포인터를
가지고
있으므로,
우리는
WM_NCCREATE
헨들러에서
SetWindowLong
함수를
호출할

있다.
WM_CREATE
메시지가
왔을때는,
객체의
포인터를
GetWindowLong
함수를
통해
가져오고,
이것을
통해
멤버함수
호출이
가능하다.
이것에
대한
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/windowdata.cpp
)
에서


있다

 


접근법에는
한가지
단점이
있다.
그것은
WM_NCCREATE
메시지가
WndProc
함수가
받는

번째
메시지가
아니라는
사실이다.
만약
GetWindowLong
에서
반환된
값이
NULL

아님을
확인하지
않고
멤버
변수를
사용하면,
this
포인터가
NULL

,
프로그램이
다운될
것이다.
정확이
얘기하면
윈도우
프로시저는
WM_NCCREATE
전에
WM_GETMINMAXINFO
메시지를
먼저
받는다

 

 

윈도우
고유
데이터
해결책의
최적화 

static
WndProc
함수가
항상
if
문장을
평가해야
한다는
것은
쉽게


있다.
그러나
this
포인터가
설정되고
나면,
어떤
코드가
실행되어야
하는지
우리는
정확히
한다.
두개의
WndProc
함수를
사용하면
코드는
미세하게
빨라진다.
예제
코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/windowdata2.cpp
)
에서


있다

 

 

전역
임시
this
포인터
저장소 


방법은
전역
WndProc
함수가
받는
모든
메시지에
대해서
멤버
WndProc
함수가
호출되도록
하는
방법이다.

아이디어는
Magmai Kai Holmlor

것이다.
( GameDev.net thread
참조
: http://www.gamedev.net/community/forums/topic.asp?topic_id=59171


방법에서는
this
포인터를
임시
전역
변수에
저장한

전역
WndProc
함수가
처음으로
실행되었을

SetWindowLong

호출하는
방법이다.

방법이
위에서
언급한
전역변수
방법보다
나은
이유는
전역변수를
짧은
시간
동안만
사용하므로
많은
윈도우를
생성할수
있으며,
모든
윈도우들이
같은
전역변수를
사용하기
때문이다.
이것에
대한
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/gwd.cpp
)

참조할것

 


방법은
불행히도
쓰레드안정성을
가지고
있지
않다.
만약
두개의
쓰레드가
동시에
윈도우를
생성할

데이터가
훼손될

있으며
프로그램이
다운될

있다.
이것을
피하는
방법은
전역변수를
보호할
critical section

사용하는
것이다.
이것에
대한
소스코드는
여기
( http://www.rpi.edu/~pudeyo/articles/wndproc/gwd2.cpp
)

참조할

여전히

코드는
만족스럽지
않다.
다음
시나리오에서
두개의
쓰레드는
데드락상태가


있다

 


쓰레드
A

윈도우를
생성한다



윈도우의
WM_CREATE
헨들러에서,
쓰레드
A

쓰레드
B

생성하고
쓰레드
B

의해서
이벤트가
설정되기를
기다린다


쓰레드
B

다른
윈도우를
생성하고,
이벤트를
설정한다.
그리고
뭔가
다른
일을
한다

 

쓰레드
B

윈도우를
생성하려고

,
이미
쓰레드
A

잡고
있는
critical section

진입하려고
한다.
이것은
데드락을
발생시킨다.

문제는
두가지
방법으로
해결할

있다.
두가지
방법
모두
궁극적으로

쓰레드가
전역
critical section

락을
잡고
있는
시간을
줄이는
방법을
사용한다

 

1.
메시지
헨들러에서

번째
메시지가
들어오면
critical section

떠난다.

방법은
기존
코드를
그리
많이
수정하지
않아도
된다.
그러나
윈도우
객체의
멤버에
플래그를
추가하는
것이다.
이것에
대한
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/gwd2m.cpp
)

참조할
.

플래그는
윈도우
생성이
성공했든,
실패했든지간에
critical section

오직
한번만
해제되었다는
것을
보증하기
위한
것이다.

방법은
복잡하며
유지관리가
편하지
않다.
 

2.
여기
( http://www.rpi.edu/~pudeyo/articles/wndproc/gwd2c.cpp )

구현된
방법은
보조
컨테이너를
사용하여
쓰레드
아이디와
윈도우
객체
포인터가
저장된
멥에
대한
접근을
동기화시킨다.

방법은
동기화
코드를

정의된
범위에
집어
넣어
critical section lock

걸리는
시간을
최소화한다.
,
멥을
조회하고
멥에
삽입하는
짧은
시간
동안만
락을
건다.

방법에서

하나의
명확한
부작용은
thread-local storage

전혀
사용하지
않는다는
것이다

 

모든
활성
쓰레드에
대해
전역
포인터를
중복가능하게
하는
대안으로
다음코드와
같이
__declspec(thread)
수정자를
사용할

있다.
 

 

__declspec(thread)
Window *g_pWindow; 

 

불행히도
dll

저장된
데이터에
대해서는
__declspec(thread)
수정자를
사용할

없다.

방법은
오직
실행파일(exe)

존재하는
코드에
대해서만
적용가능하다

 

 

CBT
Hooks 

CBT
hooks

Window
객체의
포인터를
윈도우
고유
데이터에
저장할

있는
또다른
방법을
제시한다.
CBT hook

윈도우
프로시저에
보내지는
모든
메시지보다
앞서
호출된다.
그러므로
hook
에서
윈도우
객체
포인터를
윈도우
헨들에
연결시키는데
관한
모든
작업을
수행하는
것이
가능하다.
윈도우
프로시저는
포인터를
받기만
하면
된다

 

윈도우가
생성되기
,
다음과
같이
hook

설정한다

 

HHOOK
hHook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId()); 

 


작업은
윈도우가
생성될때마다
수행되는
것이
아니라,
쓰레드
실행중

한번만
수행되어야
한다.
그러나
코드를
단순하게
하기위해,
윈도우가
생성될때마다
수행하는
방식으로
예제를
작성하였다.
CreateWindow
함수가
호출되면,
CBTProc

호출된다.
CBTProc
내부에서,
this
포인터를
받아
전역변수,
thread-local storage
또는
다른
기법을
사용한후,
SetWindowLog

호출한다.
이방법을
보여주는
소스코드는
여기(
http://www.rpi.edu/~pudeyo/articles/wndproc/hook.cpp
)

참조할것

 

 

전역
헨들
 


다른
방법은
모든
윈도우에
대해서
헨들포인터
쌍을
저장하는

하나의
전역
멥을
사용하는
것이다.
HWND

그에
대응하는
Window
포인터로
구성된
것이
Window

생성될때마다
멥에
추가되고,
Window
객체가
소멸될

맵에서
삭제되는
방식이다.
그러면
클래스는
HWND

해당하는
Window
포인터를
돌려주는
방법을
제공할
수있다.
그런
예를
소스코드(
http://www.rpi.edu/~pudeyo/articles/wndproc/gwd2c.cpp
)

CPtrMap
에서


있다.
구현은
매우
직관적이지만
모든
메시지가
처리될때마다
포인터를
조회하고,


필요한
부가작업을
수행하는
것은
잠재적으로
효율이
떨어진다.
응용프로그램에서
많은
수의
윈도우를
열고
있을

vector

list

사용하면
선형시간(
O(N) )

소요되고,
map

사용하면
로그시간(
O(log N) )

소요된다.
hash map

상수시간
( O(1) )
조회를
수행할

있지만,
다른
단점를
가지고
있다.
MFC
에서
하는것처럼

쓰레드
마다
분리된
멥을
사용하면
조회시간을
최적화하는
것이
가능하다.
사용자
레벨에서
헨들을
포인터로
변환(
MFC

FromHandle

FromHandlePermanent
함수
)
하는
용도의
전역
헨들

방법은
윈도우
관리
코드에
의해
내부적으로
헨들을
포인터에
맵핑하는
다른
해결책과
동시에
사용하는
것이
가능하다.
또한
사용자에게
그런
변환기능을
제공하는
것이
반드시
필요한
것은
아니다.
사실
ATL
에는
그런
변환기능이
없지만
아무
문제가
없다.
마지막으로

방법
하나만으로는
실제로
동작하지
않으며
다른
방법들도
마찬가지다.
누군가
헨들과
포인터를
연결시켜야하고,
거기에는
헨들과
거기에
대응하는
포인터가
필요하다.
그러므로,
임시
윈도우
프로시저,
hook
또는
다른
방법이
앞서
언급된
연결을
확립하는데
반드시
필요하다.
간단한
예를
들어,
CreateWindow
함수를
호출한
다음
맵을
생성할
수도
있다.
CreateWindow

실행되는
동안
발생되는
메시지를
처리할
필요가
없다면,

방법도
사용할

있는
해결책이
될것이다

 

MFC
접근법 

 

MFC

윈도우
헨들을
객체에
연결시키는
방법을
알아보기위해,
나는
다음
MFC
코드를
사용했다

(http://www.rpi.edu/~pudeyo/articles/wndproc/mfc.cpp

UI

그리
멋진
것은
아니지만,

프로그램은
나의
목표를
달성하기에
충분하다.
MFC dll
내부를
탐험하기위해
디버거에서
실행시킨다.
 

 


번째
눈에
들어오는
것은
wincore.cpp
파일에
있는
AfxHookWindowCreate
함수이다.

함수는
윈도우가
CWnd::CreateEx
함수에의해서
생성될

함수내부에서
호출되어,
윈도우
객체를
MFC

hook
한다.
MFC

CBT hook

사용하여
현재
쓰레드의
윈도우
생성을
hook
하는
것을

수있다.
CBT hook

위에서
언급했기
때문에
낯설지
않을
것이다.
MFC

hook handle

저장하기
위해
thread state block

사용한다.
thread state block

thread

실행되는동안
hook

관리하기
때문이다.
MFC

어떻게
데이터를
전달하는지에
주목하기
바란다.
모든
thread

per-thread state structure

가지고
있다.
( _AFX_THREAD_STATE, afxstat.h )
여기에
모든
thread-specific MFC data

저장하는데,
현재
생성중인
윈도우에
대한
정보도
저장된다.
thread state structure

포인터를
저장하는
동적
배열에
저장된다.

배열에
대한
포인터는
TlsXXX
계열의
함수를
통해
저장되고
참조된다.
AFX thread-local storage

대한
구현상세는
afxtls_.h, afxtls.cpp
에서
볼수
있다.
hook

data exchage

얘기로
다시돌아와서,
CreateEx
함수는
CWnd
객체의
포인터를
thread state

저장하고,
_AfxCbtFilterHook
함수는
그것을
thread state
로부터
가져온다.
 

hook

이제
thread state block
으로부터
가져온
CWnd
포인터와
그것에
대응하는
HWND

가지게
된다.
CWnd::Attach
함수를
호출하여
CWnd

HWND

연결하고,
HWND

CWnd
쌍을
per-thread handle map

저장한다.
( per-thread
라는
단어에
주목하기
바란다.
이것이
multi-thread
환경에서
CWnd

그밖의
window resource wrapper
객체를
공유하지
못하는
이유이다.
특정
thread

CWnd

다른
thread

HWND-CWnd map

존재하지
않는다.
만약
CWnd
객체를
생성한
thread

아닌
다른
thread
에서
CWnd
포인터를
통해
HWND

lookup
한다면,

코드는
실패할
것이다.
그러나
HWND

다른
헨들은
thread
간에
전달
할수있으며,
CWnd::Attach
함수를
호출하여
CWnd

HWND

연결시킬

있다.
그러나
thread

다르므로,
CWnd
객체는
다른
것이
될것이다.
)
그런다음,
MFC

윈도우를
subclass
하고
thread

고유한
현재
생성중인
윈도우를
가리키는
포인터를
null

설정한다.
CWnd

이제
메시지를
처리할

있는
상태가
된다

 

윈도우가
메시지를
받으면,

메시지는
AfxWndProcBase
함수를
거쳐,
AfxWndProc
함수로
보내진다.
AfxWndProc
함수는
window handle map
으로부터,
전달받은
HWND

조회하여
CWnd

얻기위해
CWnd::FromHandlePermanent
함수를
사용한다.

map

thread-specific
하다.
그러므로
윈도우를
생성한
thread
만이

윈도우에
보내진
메시지를
처리할

있다.
그후
HWND
헨들을
받는
AfxWndProc
함수는
AfxCallWndProc
함수를
호출하는데,
이함수는
HWND
헨들대신
CWnd
포인터를
받는다는
점만
다르다.
그후
AfxCallWndProc
함수는
CWnd::WindowProc
함수에게
메시지를
보낸다.
그것을
받은
CWnd::WindowProc
함수는
CWnd::OnWndMsg
함수를
호출한다.
CWnd::OnWndMsg
함수는
지정된
윈도우의
메시지맵에서
헨들러를
찾고,
메시지
파라메터를
decode
하고,
헨들러를
호출하여,
사용자가
작성한
OnPaint
함수에
이르게
된다

 

 

ATL
접근법 

 

나는
ATL
메시지
처리
구조를
조사하기위해
다음코드를
사용했다.
( http://www.rpi.edu/~pudeyo/articles/wndproc/atl.cpp )
디버거에서
실행하고
CWindowImpl::Create ( atlwin.h )
함수안으로
들어간다.
잠시후
AtlWinModuleAddCreateWndData (atlbase.h)
함수에
이르게
되는데,

함수는
_AtlCreateWndData
구조체
포인터를
리스트에
저장한다.

구조체
리스트에는
윈도우
객체
포인터들이
저장되는데,
WM_NCCREATE
메시지가
도달하기
전에
ATL

window procedure
들이
접근하여
사용할

있다.
모든
구조체는
thread identifier

거기에
속한
윈도우
객체
포인터를
가지고
있다.
MFC
에서는
실제로
윈도우를
생성중이건
아니건간에,
모든
thread

현재
생성중인
윈도우에
대한
참조를
가지고
있지만,
ATL

전체
application

대하여

하나의
리스트가
그것을
관리한다.
이런
방식은
하나의
UI thread

많은
worker thread

존재할

약간의
메모리를
절약


있다

 

이제
static
멤버함수
CWindowImplBaseT::StartWndProc (atlwin.h)

이른다.
이것은
현재
윈도우에
보내진

번째
메시지를
처리하는
메시지
헨들러
이다.
여기서
CWindow

HWND

쌍을
연결하는
작업을
하여,
현재
메시지와
앞으로

메시지들이
멤버
윈도우
프로시저로
route
되도록
한다.
지체
없이
create-window-data structure
리스트로부터
AtlWinModuleExtractCreateWndData (atlbase.h)
함수를
사용하여
this
포인터를
가져온다.

함수는
current thread identifier

리스트를
조회한다.
thread identifier

일치하면
거기에
대응하는
window object pointer

리턴된다.
다른
ATL
코드나
사용자
코드가
실행되기
전에
가져오기
때문에
특정
thread
에는
오직
하나의
window object pointer

연결된다.
이로써
CreateWindow
함수는

번째
받은
메시지로부터
호출되게
된다.
ATL

여러개의
thread

동시에
리스트에
접근하는
것을
방지하기
위해
critical section

사용한다.
window
생성
과정전체
동안이
아니라,
create window data

추가하고
가져오는
동안에만
lock

하기
때문에
다른
thread
들이
필요없이
기다리지
않는다

 

이제
CWindowImplBaseT::StartWindowProc
함수는
this
포인터를
가지게
되었다.
CWindowImplBaseT::GetWindowProc
함수를
사용하여
실제
윈도우
프로시저를
가져온다.
그다음
thunk

생성하는데,
이것의
목적은
HWND
헨들을
CWindow
포인터로
변환하는
것이다.
그것은
다음과
같이
이루어진다

 


StartWindowProc
함수는
window object

instance data

일부인
특정
메모리를
특정
코드로
초기화한다.

코드는
임시
윈도우
프로시저처럼
동작하는
코드로,
들어오는
HWND

CWindow
포인터로
멥핑한다.
그런
다음
윈도우
프로시저는
( SetWindowLong
함수를
통해서
)

메모리
블록의
시작지점을
가리키도록
설정된다.
 

 



특정
메모리는
다음과
같이
평범한
프로토타입의
윈도우
프로시저를
저장하는
어떤
것이다

LRESULT
CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); 

 


윈도우
메시지가

프로시저에
도달했을
,

코드는
hwnd parameter

CWindow object

포인터로
변환한다.
CWindow object

주소는
같은
메모리블럭에
저장된다


그다음
메시지
프로시저는
실제
static member procedure

jump
한다.
그것은
WindowProc
함수와
동일한
프로토타입을가지고
있지만
hwnd parameter

실제
윈도우
헨들이
아니라,
CWindow pointer
이다.
그함수는
포인터를
얻기위해
cast

수행한다

CWindowImplBaseT<
TBase, TWinTraits >* pThis = (CWindowImplBaseT< TBase, TWinTraits
>*)hWnd; 

 

다음은
atlbase.h

있는
_stdcallthunk

thunk code
이다

 

mov
dword ptr[esp+4], pThis 

jmp
WndProc 

 

pThis

Init

파라메터로
전달된다.
이것은
현재윈도우의
CWindow
객체에
대한
포인터이다.
WndProc
또한
파라메터로
전달된다.
이것은
실제
WndProc

주소이며,
대부분의
경우
이것은
CWindowImplBaseT::WindowProc (atlwin.h)

된다.
Init


코드를
실행시에
주어진
두가지
정보를
가지고
build
한다.

코드는
다음과
같은
이유로
CWindow
객체의
일부인
메모리블럭에
저장된다

 

1.
Code segment

읽기전용이며,
메모리에
있는
코드를
수정하는
것은
바이러스처럼
보인다

2.
윈도우가
다르면
윈도우
프로시저도
다르다.
그리고
윈도우
프로시저가
가진
CWindow
포인터는
모두
다르다.
그러므로
하나의
코드가
종류가
다른
윈도우를
처리하는데
사용될
수는
없고,
코드는
실행시에
생성되거나
복제되어야
한다

 

 


코드가
하는
일은
hwnd parameter

thunk

속한
객체의
포인터를
사용하여
window procedure

교체하는
일이다.
(CWindowImplRoot, atlwin.h)
그리고
static
멤버함수
WndProc
으로
jump
하는데,

함수의

번째
파레메터는
HWND

가장한
CWindow
포인터이다.
물론
컴파일러는
에러를
발생시키지
않는다.
ATL

구현은
멤버
윈도우
프로시저를
가져오는
부분에
있어서는
MFC

비해
매우
효율적이다.
MFC
코드는
윈도우
객체를
조회하는데
윈도우의
개수에
비례하는
선형시간이
소요되는
반면,
ATL

상수시간이
소요된다.
ATL
코드는
하나의
move,
하나의
jump

사용하여,
최대한
효율적이다

 

 

MFC

ATL
비교 

 

위에서
언급했듯이,
ATL

MFC

구현에는
다음과
같은
차이점이
있다

 


ATL

thread-local storage

사용하지
않는다.
그러므로
어떠한
초기화도
사용하지
않는다.
MFC

사용하면
AfxWinInit
으로
초기화를
반드시
해야한다.
ATL

사용자
코드를
직접적으로
수행한다.
게다가
TLS

접근하는
것은
동적
배열을
사용하므로,
상각된
상수시간(
amotized constant time )

소요되는데,
이것은
MFC

실행
속도를
감소시킬

있다

 


ATL

맵핑은
thread-specific
하지
않다.
프로그래머는
CWindow
객체를
만든
thread
에서
뿐만
아니라,
모든
thread
에서
사용할

있다.
MFC

CWnd

같은
객체를
그것을
생성한
thread

bind
한다.
MFC

포인터를
CWnd*
형으로
전달하는반면,
ATL

함수
파라메터로
HWND
헨들을
전달한다


MFC

WndProc

간접호출이
많아
오버헤드를
좀더
가진다.
ATL

조금더
빠르다


MFC

주어진
HWND

CWnd
포인터를
조회하는데
상수시간이
소요된다.
응용프로그램에
윈도우가
다수존재할
경우
이것은
그리
빠른성능을
보이지
않는다.
MFC

WndProc

윈도우의
개수가
많아
질수록
속도가
느려지는
반면,
ATL

윈도우의
개수에
상관없이
동일한
고성능을
보인다

Leave a Reply

Your email address will not be published. Required fields are marked *