티스토리 뷰

준비물 : Visual Studio 6.0 , WinSpy++ v1.6

일단 아래와 같은 단순한 API 프로그램을 살펴보자.

그림 1

Visual C++ 6.0에서 New -> Project -> Win32 Application에서 프로젝트 이름은 BULK라고 하고 첫 번째 스텝에서 A typical "Hello World!" application을 선택하여 자동으로 생성된 프로그램을 그대로 빌드하여 실행시킨 것이다. 실제로 해보면 알겠지만 대부분의 Win32 App의 기본 골격인 메인 클래스 등록, 메인 윈도우 생성, 메시지 루프, 윈도우 프로시저까지 생성해 준다.


그림 2

위와 같이 메뉴에서 Help -> About 을 선택하여 모달 다이얼로그를 띄운다. 아래 코드가 실행되는 것이다.


그림 3

DialogBox()함수가 실행되면서 About 대화상자가 뜨는 것이다. 이 함수는 대화상자가 닫힐 때까지 리턴되지 않는다. [OK]버튼이나 [X]버튼을 눌러서 닫지 않는 이상 DialogBox()함수 아래 있는 break; 문은 실행되지 않는다는 것이다.

About 대화상자를 메인 윈도우의 클라이언트 영역에서 이리저리 움직여보자. “Hello World!"라는 글자를 가렸다가 보였다가 해보자. 어떤가? ...... 잘된다. -_-; 그렇다고 하면 DialogBox()함수에서 블록(?)이 걸려있는 WndProc()이 WM_PAINT 메시지를 훌륭하게 처리했다는 말이다. 여기서 의문점이 생긴다. 어떻게 블록되어 있는 상태에서 WndProc()에 재진입했단 말인가? 쓰레드가 여러개라도 되나?? 확인해보자.


그림 4

쓰레드는 하나다! 어떻게 된 것인가... 곰곰이 생각을 해보자. 생각할 수 있는 방법은 이것 뿐이다.


그림 5

DialogBox()함수는 API 함수이다. 이 함수에 의해 생성된 대화상자는 버튼이나 에디트 박스와 같이 Windows에서 제공하는 공통 클래스로 생성되고 따라서 프로시저도 Windows에서 제공한다. 프로시저 코드는 바로 USER32.DLL에 들어있다. 그 프로시저에서 어떤 메시지(그 메시지는 나중에 찾을 것이다)가 들어왔을 때 자신의 소유(Owner) 윈도우(이 대화상자는 CHILD 속성이 아니므로 부모(Parent) 윈도우가 아니다. Owner와 Parent는 다른 것이다) 핸들을 얻고 그 곳으로 WM_PAINT 메시지를 보내는 것이라고 생각할 수 있다.

그러나 위 방법을 짐작일 뿐이다. 그것을 확인해 보자.


그림 6

이 곳에 BP를 걸고 About 대화상자를 움직여 보자. 그러면 BP가 딱 걸린다. 콜 스택을 확인해보자.


그림 7

위와 같다. 살펴보자............... 모르겠다. -_-;;;;;;;; 좀 더 살펴보자..... ... ... 그래도 모르겠다. 가장 위에 우리의 WinProc() 호출되었다는 것은 알겠지만 아래 함수는 주소로만 표기된다. 운영체제 코드이므로 당연한 것이다. 심볼이 없기 때문에 함수이름으로 볼 수가 없는 것이다. 그러면 About 대화상자의 윈도우 프로시저 주소를 알아내자. 간단한 툴을 사용하면 쉽게 알아낼 수 있다.


그림 8

윈도우 프로시저의 주소는 77D1E54F이다. 이제 콜 스택에서 77D1E54F보다 큰 가장 가까운 주소를 찾아보자.


그림 9

77d1e571이 있다. 어셈블리 코드를 살펴보자.


그림 10

바로 이것이 Windows가 제공하는 표준 대화상자의 윈도우 프로시저 본체이다. 보다시피 상당히 짧다. 결과적으로
77D1E56C call 77D03F5A
에 의해 우리(?) 윈도우의 WinProc으로 재진입이 일어난 것이다. 더 깊이는 들어가지 않고(사실 잘 몰라서 ^^;;) 77D1E571에 BP를 찍고 RUN(F5)을 하여 어떤 메시지가 들어왔는지 살펴보자.


그림 11

위와 같이 BP가 걸린다. 이 상태에서 스택을 살펴보면 윈도우 프로시저에 넘어온 인자값을 확인할 수 있다. 다른 유용한(?) 디버거와는 달리 VC++ 내장 디버거는 스택창이 따로 없다. 따라서 메모리창을 띄워 직접 확인해야 한다.


그림 12
주소란에 ebp+8을 치면 첫 번째 인자부터 차례로 나온다. 함수 호출 후 스택 프레임 구성에 대해서는 다음을 참고하세요.
   .
   .
   .
ebp - 3n  =  지역 변수 3
ebp - 2n  =  지역 변수 2
ebp - 1n  =  지역 변수 1
ebp         =  이전 ebp
ebp + 1n  =  리턴 주소
ebp + 2n  =  인자 1
ebp + 3n  =  인자 2
ebp + 4n  =  인자 3
   .
   .
   .

윈도우 프로시저 정의는
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
위와 같으므로 첫 번째 값부터 차례대로
hWnd => B090C
message => 112
wParam => F012
lParam => 11E01CE
메시지가 16진수로 112이다. 112는 WM_SYSCOMMAND를 의미하는데, 이 메시지에서 wParam은 다음 갚을 가진다.


그림 13

F012가 없다! ㅜㅜ SC_MOVE 인것 같은데 왜 2가 있지. 이럴 때 MSDN을 본다.


그림 14

해석하면, “WM_SYSCOMMAND 메시지에서 wParam 파라미터의 하위 4비트는 시스템에 의해 내부적으로 사용된다. 따라서 wParam의 올바른 값을 얻기 위해서 어플리케이션에서는 wParam을 0xFFF0와 AND 연산하여 사용해야 한다.”라고 되어 있다.
여기서 하위 4비트는 시스템에 의해 내부적으로 사용된다는 말이 중요하다. 여기서 알아본 경우가 내부적으로 사용되는 경우인 것 같다. “창이 이동이 되긴 했는데 뒤에 창에 그림이 그려져야 하니까 그쪽으로 WM_PAINT를 보내라” 뭐 이런 말을 시스템이 하는 것이 아닐까 한다. 참고로 lParam은 커서의 위치이다.

'OS > Windows' 카테고리의 다른 글

[WINDOWS] Perfmon (성능 모니터)  (0) 2011.02.09
findstr - 파일에서 문자열 찾기  (0) 2010.11.26
Non-Paged Pool  (2) 2010.08.12
MS-DOS 명령어  (0) 2008.07.16
ASCII 테이블  (0) 2008.01.12
댓글