Transcript 고급 MFC

고급 MFC
권영근
[email protected]
Introduction

교재



“MFC Internals”, George Shepherd, Addison-Wesley,
1996
MSDN Library, http://msdn.microsoft.com/library/
선수 강좌


C++
Win32 API
Why MFC Internals?

프로그램 debugging 시



종종 MFC 내에서 오류 발생.
MFC class 구조에 친숙해져야 함.
Class Derivation


많은 class가 derivation 목적으로 정의됨.
VC++에서 자동 코드 삽입 => 안전한가?
강의 계획
1. A Conceptual Overview of MFC
2. Basic Windows Support
3. Message Handling in MFC
4. The MFC Utility Classes
5. All Roads Lead to CObject
6. MFC Dialog and Control Classes
7. MFC’s Document/View Architecture
8. Advanced Document/View Internals
9. MFC’s Enhanced User-Interface Classes
10. MFC Dlls and Threads
Chap 1.
A Conceptual Overview of MFC
Contents





Application Frameworks 정의
MFC의 역사
MFC의 설계 목적
MFC의 구성
MFC code basics
Application Frameworks

Framework


Application Framework


특정 영역의 작업을 도와 주는 class 집합
특정 OS의 application 개발 작업을 도와 주는 class
집합
MFC (Microsoft Foundation Classes)

Windows용 application 개발 작업을 도와 주는
framework
MFC의 역사

1989년

AFX group 탄생


목적


Application framework technology development group의 약자
Windows application개발자를 위한 C++과 object-oriented
개념을 적용시킨 tool의 개발
First prototype  실패

Too complex and too different from Windows itself
MFC의 역사 (cont’d)

1992년

MFC version 1.0




C/C++ version 7.0과 함께 나옴
60개가 넘는 Windows application 개발을 위한class포함
일반 목적의 여러 class포함
완벽하지 못했음

개발자로부터 많은 불평을 받음
MFC의 역사 (cont’d)

1993년

MFC version 2.0



Visual C++ version 1.0과 Windows NT와 함께 나옴
100개가 넘는 class포함
새로 포함된 내용


새로운 application architecture
새로운 high-level abstractions
MFC의 역사 (cont’d)

1993년

MFC version 2.5



Visual C++ version 1.5와 함께 나옴
OLE2.0과 ODBC 지원이 추가됨
1994년

MFC version 3.0

Thread에 대한 기능이 보강됨
MFC의 역사 (cont’d)

1995년

MFC version 3.1

추가된 기능



Simple Messaging Application Programming
Interface(MAPI)
WinSock
MFC version 4.0


Visual C++ version 4.0과 함께 나옴
개발환경의 발전, 재사용성의 향상
MFC의 역사 (cont’d)

1997년

MFC version 4.2



1998년

MFC version 6.0



Visual C++ version 5.0과 함께 나옴
Internet 등을 위한 기능 추가
Visual C++ version 6.0과 함께 나옴
User interface향상 등의 기능이 포함
2002년

MFC version 7.0


Visual C++ .net(7.0) 과 함께 나옴
기존의 각 요소별 추가 및 확장


DHTML(editing, dialog box, …), Ole control, …
Static casting and MFC message maps
MFC의 역사(cont’d)

MFC version과 Visual C++ version
MFC version
Visual C++ version
6.0
Microsoft Visual C++ version 6.0
4.2
Microsoft Visual C++ versions 4.2 and 5.0
4.0
Microsoft Visual C++ versions 4.0 and 4.1
3.2
Microsoft Visual C++ version 2.2
3.1
Microsoft Visual C++ version 2.1
3.0
Microsoft Visual C++ version 2.0
2.5
Microsoft Visual C++ version 1.5
2.1
Microsoft Visual C++ version 1.0
2.0
Microsoft Visual C++ version 1.0
1.0
Microsoft C/C++ version 7.0
MFC 설계 목적

AFX 그룹의 설계 목적





Real-World Application
Simplifying the Windows API
Using the existing knowledge of Windows
Foundation for large-scale applications
Small and fast framework
MFC의 구성

MFC class 의 분류




General-purpose classes
Windows API classes
Application framework classes
High-level abstractions
MFC의 구성(cont’d)

General-purpose Class


프로그램의 일반적 용도를 위한 class
CObject class



MFC의 최상위 class
run-time type information, serialization, diagnostic function,
support for dynamic creation 기능 제공
CArchive, CDumpContext, CRuntimeClass 등과 연계되어 작용
MFC의 구성(cont’d)

Exception-handling class






memory, I/O error 발생 시 처리
CException : base class
CArchiveException, CFileException
CMemoryException, CResourceException
CNotSupportedException, CUserException
COleException, CDBException
MFC의 구성(cont’d)

Collection class



Array : CByteArray, CWordArray, CDWordArray, CPtrArray,
CObArray, CStringArray, CUnitArray
Linked list : CObList, CPtrList, CStringList
Map : CMapPtrToWord, CMapPtrToPtr, CMapStringToOb,
CMapStringToPtr, CMapStringToString, CMapWordToOb,
CMapWordToPtr
MFC의 구성(cont’d)

Dynamic string class



CString
concatenation, comparison, assignment 등의 기본 연산 제공
File class


CFile, CStdioFile, CMemFile
추상적으로 disk상의 파일 제어, 실제로는 memory상의 파일
제어
MFC의 구성(cont’d)

Time class


CTime, CTimeSan
기타

CPoint, CSize, CRect : Windows structure
MFC의 구성(cont’d)

Windows API class

Application 관련 class




CCmdTarget : message 처리
CCmdUI : user interface의 update
CWinThread : MFC program의 실행 thread를 의미, 즉
program의 main을 포함
CWinApp : CWinThread의 파생 class으로서 standard
windows application을 표현
MFC의 구성(cont’d)

Window 관련 class




CWnd : CCmdTarget의 파생 class이므로 message를 handle.
윈도우를 다루는 API 포함.
CFramWnd, CMDIFrameWnd : main frame window로서
message를 받는 첫 윈도우
CDialog, 공통다이어로그박스(CFileDialog, CColorDialog,
CFontDialog, CPrintDialog, CFindReplaceDialog)
CDataExchange : DDX/DDV
MFC의 구성(cont’d)




CPropertySheet, CPropertyPage
Controls : CButton, CEdit, …
CMenu
GDI 관련 class


CDC, CPaintDC, CWindowDC, CClientDC, CMetaFileDC
CPen, CBrush, CFont, ...
MFC의 구성(cont’d)

Application framework class

Document/View Architecture




CDocTemplate, CSingleDocTemplate, CMultiDocTemplate :
document와 view를 연결
CDocument : data를 관리
CView : data를 redering하여 보여 줌
Context-Sensitive Help
MFC의 구성(cont’d)

High-level abstraction

Enhanced Views



Splitter Window


CScrollView, CFormView
CEditView, CListView, CRichEditView, CTreeView
CSplitterWnd : dynamic, static
Control Views

CToolBar, CStatusBar, CDialogBar
MFC Code Basics

Class Declaration Subsections

MFC library 개발 팀의 코딩 규칙



Not based public/protected/private
Private 변수는 거의 사용하지 않는다.
Header / source file





//
//
//
//
//
Constructors
Attributes
Operations
Overridables
Implementation
Example of Comments
class CStdioFile : public CFile
{
DECLARE_DYNAMIC(CStdioFile)
public:
// Constructors
CStdioFile();
...
// Attributes
FILE* m_pStream; // stdio FILE
...
// Operations
virtual void WriteString(LPCTSTR lpsz);
...
virtual LPTSTR ReadString(LPTSTR lpsz, UINT nMax);
...
// Implementation
public:
...
};
MFC Comments

Class Declaration Subsections


MFC library 개발 팀의 코딩 규칙
Class header file의 각 항목의 의미

// Constructors




C++ constructors, any other initialization
예) CWnd::Create
대개는 public
// Attributes


대개는 documented public data members
Member functions(위의 data를 조작하는) : Get / Set 함수들
MFC Code Basics (cont’d)

// Operations



// Overridables



Documented member functions
대개는 public, non-const : side effects
상속 받은 class가 override한 functions
Pure virtual functions
// Implementation




Implementation detail
Undocumented
대개는 protected
주의 : may change in future versions of MFC
MFC Code Basics (cont’d)

Variable Naming (common)
Type
Prefix
Example
Comment
char
c
cDirSeparator
BOOL
b
blsSending
int
n
nVariableCnt
UINT
n
nMyUnsigned
WORD
w
wListID
LONG
l
lAxisRatio
DWORD
dw
dwPackedmessage
* (pointer)
p
pWnd
FAR *
lp
lpWnd
LPSTR
handle
lpsz
h
lpszFileName
hWnd
Z indicates NULL terminated.
callback
lpfn
lpfnHookProc
Pointer to a function
MFC Code Basics (cont’d)

Variable Naming (MFC extensions)
Class
Prefix
Example
CRect
rect
rectScroll
CPoint
pt
ptMouseClick
CSize
sz
szRectangle
CString
str
strFind
CWnd
Wnd
WndControl
CWnd*
pWnd
pWndDialog
MFC Code Basics (cont’d)

Symbol Naming
Type
Prefix
Example
Range
Shared by multiple resources
IDR_
IDR_MAINFRAME
1-0x6FFF
Dialog resource
IDD_
IDD_ABOUT
1-0x6FFF
Dialog resource help context ID
(for context-sensitive help)
HIDD_
HIDD_HELP_ABOU
T
0x2001-0x26FF
Bitmap resource
IDB_
IDB_SMILEY
1-0x6FFF
Cursor resource
Icon resource
IDC_
IDC_HAND
1-0x6FFF
MFC Code Basics (cont’d)

Symbol Naming
Type
Prefix
Example
Range
Menu or toolbar command
ID_
ID_CIRCLE_TOOL
0x8000-0xDFFF
Command help context
HID_
HID_CIRCLE_TOOL
0x1800-0x1DFF
Message box prompt
IDP_
IDP_FATALERROR
8-0xDFFF
Message box help context
HIDP_
HIDP_FATALERROR
0x3008-0x3DFF
Control in dialog template
IDC_
IDC_COMBO1
8-0xDFFF
String resource
IDS_
IDS_ERROR12
1-0x7FFF
Chap. 2
Basic Windows Support
Contents



Introduction
MFC versus C/SDK
Basic Application Components





Find WinMain()
Hidden Cool Stuff




CWinApp
CWnd
Window handles & Window objects
Registering Window Classes
MFC’s Windows Hooks
MFC’s Message Pump
MFC’s GDI Support
Introduction

MFC



200개 이상의 클래스들의 거대한 집합
But, MFC has also “Basic Windows Support”
A Windows program is still a Windows program


어떤 언어(C, C++, Delphi, …)나 framework(MFC,
OWL, …) 를 이용하든지 기본적인 요소들이 구현된다.
Basic windows application support

WinMain, window class 등록, 메시지 루프, …
Issue

MFC가 어떻게 Windows application을 만드는가




The application itself
Windows
Message handling
The Graphics Device Interface (GDI)
MFC vs. C/SDK

Motivation

모든 Windows application은 다음 2개의 component를
포함한다.



C/SDK 개발 환경



main application itself
message를 핸들하는 하나 이상의 window
copy & paste
Time-consuming & inefficient
C++/MFC 개발 환경


OOP 활용 : inheritance & encaptulation
필요한 부분만 변경
Boilerplate Code

왜 필요한가?

Windows is event-driven OS


Imposes a grate deal of overhead
Windows OS



H/W 와 응용 프로그램을 연결하는 위치
Application에게 발생하는 이벤트를 알림
이벤트를 다루기 위해 상당한 양의 코드가 항상 필요
메시지 처리를 위한 작업들

Set up a message handler and register it

RegiserClass()

Windows가 application의 instance들을 추적

application은 Windows에게 메시지를
요청(ask)하고, 처리(dispatch)한다.

application이 종료될 때까지 위 작업을 반복한다.
Application의 준비
1.
WinMain() 함수


프로그램의 시작점
Windows 로부터 프로그램을 실행하는데 필요한
정보를 얻어 오는 통로

2.
적어도 하나의 main window class를 등록

3.
4.
User interface를 제공
Message loop를 설정
Some initialization and setup

5.
현재 instance의 handle, 직전에 실행된 instance의 handle,
command line argument, window의 모습(최대화, 최소화, …)
Application specific, instance specific
Message handler를 제공

최소한 WM_DESTROY 처리 -> WM_QUIT 생성
Application의 기본 요소
Message
Handler
Main 함수
Application specific
Initialization
(Window class 등록)
Instance specific
Initialization
(Main window(UI)생성, 보여줌)
Message loop
Source 1 : C/SDK
#include <windows.h>
HANDLE hInst;
/* current instance */
LRESULT CALLBACK MainWndProc(HANDLE hWnd, UINT message, WPARAM
wParam, LPARAM lParam)
{
switch(message) {
case WM_LBUTTONDOWN:
MessageBox(hWnd,“Left mouse button clicked”, NULL, MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default: /* Passes it on if unprocessed */
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return 0;
}
BOOL InitApplication(HANDLE hInstance){
WNDCLASS wc;
wc.style = 0;
/* Class style(s) */
wc.lpfnWndProc = MainWndProc; /* Message handler */
wc.cbClsExtra = 0;
/* No per-class extra data */
wc.cbWndExtra = 0;
/* No per-window extra data */
wc.hInstance = hInstance; /* Application that owns the class*/
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
/* Name of menu */
wc.lpszClassName = “MinimalWClass”; /* Name of window class */
return (RegisterClass(&wc));
}
BOOL InitInstance(HANDLE hInstance, int nCmdShow) {
HWND hWnd;
/* Main window handle */
hInst = hInstance;
// needed for loading resources //
hWnd = CreateWindow(
“MinimalWClass”,
“Minimal”,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Window class */
Caption */
Window style */
Default horizontal pos. */
Default vertical pos. */
Default width */
Default height. */
No parent */
Use the window class menu */
This instance owns the window. */
if (!hWnd)
return (FALSE);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return (TRUE);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
MSG msg;
/* message */
if (!hPrevInstance) {
/* First instance? */
if (!InitApplication(hInstance)) /* Shared stuff */
return (FALSE);
/* cannot initialize */
}
if (!InitInstance(hInstance, nCmdShow))
return (FALSE);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);/*Returns the value from PostQuitMessage*/
}
Source 2 : MFC
#include <afxwin.h> // MFC 주요 클래스 선언
class CGenericApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
class CGenericWindow : public CFrameWnd {
public:
CGenericWindow() {
Create(NULL, “Generic”);
}
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CGenericWindow, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
void CGenericWindow::OnLButtonDown(UINT nFlags, CPoint point) {
MessageBox(“Left mouse button pressed…”, NULL, MB_OK);
}
BOOL CGenericApp::InitInstance() {
m_pMainWnd = new CGenericWindow();
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
CGenericApp GenericApp;
C/SDK와 MFC의 비교

WinMain() Function



Window class 등록



C/SDK : 존재
MFC : where? ( part of application framework )
C/SDK : 존재
MFC : where?
Message loop


C/SDK : 존재
MFC : where?

Additional functions : idle processing, PreTranslateMessage()
C/SDK와 MFC의 비교 (cont’d)

Instance initialization



C/SDK : 존재
MFC : 존재, 그러나 언제 실행?
Message handling


C/SDK : 존재
MFC : 존재, 그러나 어떻게 command-routing,
message-dispatch?


Member functions + Message map
AfxWndProc() : 공통 window procedure
Basic MFC Application Components

Windows application의 두 가지 components



Message pump
Window procedure
MFC에서의 구현 방법

CWinApp



Application을 나타내는 class
application-specific : 초기화, window 생성, 메시지 루프
CWnd


Window를 나타내는 class
window-specific : 메시지 핸들링
CWinApp (“AfxWin.h”)
CWinApp : public CWinThread
{
public:
// Constructor
CWinApp(LPCTSTR lpszAppName = NULL);
to EXE name
// app name defaults
// Attributes
// Startup args (do not change)
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance; // not used
LPTSTR m_lpCmdLine;
int m_nCmdShow;
// Running args (can be changed in InitInstance)
LPCTSTR m_pszAppName; // human readable name
// (from constructor or AFX_IDS_APP_TITLE)
public: // set in constructor to override default
LPCTSTR m_pszExeName;
// executable name (no spaces)
LPCTSTR m_pszHelpFilePath; // default based on module path
LPCTSTR m_pszProfileName; // default based on app name
// Overridables
// hooks for your initialization code
virtual BOOL InitApplication();
void SetCurrentHandles();
// overrides for implementation
virtual BOOL InitInstance();
virtual int ExitInstance(); // return app exit code
virtual int Run();
virtual BOOL OnIdle(LONG lCount);
// return TRUE if more idle processing
virtual LRESULT ProcessWndProcException
(CException* e, const MSG* pMsg);
public:
virtual ~CWinApp();
protected:
//{{AFX_MSG(CWinApp)
afx_msg void OnAppExit();
afx_msg void OnUpdateRecentFileMenu(CCmdUI* pCmdUI);
afx_msg BOOL OnOpenRecentFile(UINT nID);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CWinApp


AFXWIN.h
멤버 변수와 멤버 함수

WinMain()에 전달된 command line parameter 를
관리할 변수




m_hInstance : the current instance handle
m_hPrevInstance : the previous instance handle
m_lpCmdLine : the command line parameters
m_nCmdShow : the show window flag
CWinApp (cont’d)


m_pszAppName : application name
관련 pointer




m_pszExeName : executable file name
m_pszHelpFilePath : the path to help file
m_pszProfileName : application profiles name
프로그램 실행 시 주어진 command line parameter
유지

CCommandLineInfo class 이용 (AFXWIN.H)
CWinApp (cont’d)




InitInstance() : instance-specific 초기화
ExitInstance() : instance 종료 시 처리
Run() : message pump 수행
OnIdle() : Message queue가 비었을 때, Run 함수에
의해 호출
CWnd

AFXWIN.H

2가지 기능 수행

Wrapping the regular Windows API


예: Create(), ShowWindow(), …
Higher-level MFC-related functionality

예: default message handling
CWnd ( 기능 1 )

Wrapping the Windows API


m_hWnd : regular API-level window handle 을
나타내기 위한 멤버변수
window handle을 인자로 갖는 거의 모든 API를 멤버
함수로 가진다.

예)
API
HWND hWnd;
ShowWindow(hWnd,SW_SHOWNORMAL);
MFC
CWnd * pWnd;
pWnd->ShowWindow(SW_SHOWNORMAL);

AFXWIN2.INL : API 의 호출
_AFXWIN_INLINE BOOL CWnd::ShowWindow(int nCmdShow)
{ ASSERT(::IsWindow(m_hWnd));
return ::ShowWindow(m_hWnd, nCmdShow); }
CWnd ( 기능 2 )

Higher-level MFC-related functionality


CObject -> CCmdTarget -> CWnd
CObject derivation



CCmdTarget derivation


Dynamic run-time information
Serialization
MFC’s message-routing scheme
Default message handling 제공
예 : default message handling
_AFXWIN_INLINE void CWnd::OnActivate(UINT, CWnd*, BOOL)
{ Default(); }
LRESULT CWnd::Default()
{
// call DefWindowProc with the last message
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return DefWindowProc(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam,
pThreadState->m_lastSentMsg.lParam);
}
Turning Window Handles into Window
Objects
High
Level
Application
MFC object
CHandleMap class
Low
Level
Windows OS
Window handle
계층적 관계

High level : MFC object


Low level : Window handle



application 입장에서는 MFC object를 이용
Windows OS는 handle을 이용
MFC는 어떤 handle에 어떤 object가 연결되어
있는지를 알아야 함
CHandleMap class를 이용하여 구현
CHandleMap


AFXSTAT_.H
Mapping window handle to MFC object



Handle이 주어지면 해당 object를 찾음
Windows OS가 callback 함수를 호출할 때, window
handle을 parameter로 호출
그러나, MFC는 해당 CWnd- 파생 클래스 객체를
가지고 작업
CHandleMap (cont’d)

멤버변수

CMapPtrToPtr m_permanantMap





Permanent map
명시적으로 객체가 생성될 때 정보가 추가됨(CWnd’s::Create())
객체가 종료될 때 정보가 제거됨 (CWnd’s::OnNcDestroy())
cf) WM_NCCREAT -> WM_CREAT -> … -> WM_DESTROY ->
WM_NCDESTROY
CMapPtrToPtr m_temporaryMap




Temporary map
임시로 생성해야 할때(ex: CWnd::GetActiveWindow())
실제로는 CWnd::HandleMap 에서 필요할 때 생성
OnIdle()에서 삭제 => DeleteTempMap() 호출
CHandleMap (cont’d)

멤버함수

CWnd::FromHandle(HWND hWnd)




주어진 window handle에 mapping되는 object의 pointer를
얻을 때 사용
hWnd를 wrap하고 있는 CWnd pointer return
만약 없으면 hWnd를 wrap하는 temporary CWnd return
획득하면 역으로 object를 통해 hWnd의 접근이 용이 (
CWnd::m_hWnd 멤버 변수 )
Temporary map 예
“AFXWIN2.INL”
_AFXWIN_INLINE CWnd* PASCAL CWnd::GetActiveWindow()
{ return CWnd::FromHandle(::GetActiveWindow()); }
“Wincore.cpp”
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE);
CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
return pWnd;
}
Temporary map 예 (cont’d)
“Winhand.cpp”
CObject* CHandleMap::FromHandle(HANDLE h)
{
if (h == NULL)
return NULL;
CObject* pObject = LookupPermanent(h);
if (pObject != NULL)
return pObject; // return permanent one
else if ((pObject = LookupTemporary(h)) != NULL)
{
HANDLE* ph = (HANDLE*)((BYTE*)pObject + m_nOffset);
ph[0] = h;
if (m_nHandles == 2)
{
ph[1] = h;
}
return pObject; // return current temporary one
}
Temporary map 예 (cont’d)
CObject* pTemp = NULL;
TRY {
pTemp = m_pClass->CreateObject();
m_temporaryMap.SetAt((LPVOID)h, pTemp);
}
CATCH_ALL(e) {
AfxSetNewHandler(pnhOldHandler);
AfxEnableMemoryTracking(bEnable);
THROW_LAST();
}
END_CATCH_ALL
AfxSetNewHandler(pnhOldHandler);
AfxEnableMemoryTracking(bEnable);
HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset);
ph[0] = h;
if (m_nHandles == 2) ph[1] = h;
return pTemp;
}
Other Mapping

AFX_MODULE_THREAD_STATE

m_pmapHWND


m_pmapHMENU


device context handles CDC objects
m_pmapHGDIOBJ


menu handles  CMenu objects
m_pmapHDC


window handles  CWnd objects
GDI object handles  CGDIObjects
m_pmapHIMAGELIST

image list handles  CImageList objects
Attaching & Detaching

Window handles과 CWnd-derived objects를
연관시켜주는 함수

CWnd::Attach()



주어진 window handle을 CWnd::m_hWnd에 대입
MFC’s permanent map에 정보 추가
CWnd::Detach()


CWnd::m_hWnd를 NULL로 만듬
MFC’s permanent map에서 정보 제거
CWnd::Attach
BOOL CWnd::Attach(HWND hWndNew)
{
ASSERT(m_hWnd == NULL);
// only attach once, detach on destroy
ASSERT(FromHandlePermanent(hWndNew) == NULL);
// must not already be in permanent map
if (hWndNew == NULL)
return FALSE;
CHandleMap* pMap = afxMapHWND(TRUE);
ASSERT(pMap != NULL);
pMap->SetPermanent(m_hWnd = hWndNew, this);
AttachControlSite(pMap);
return TRUE;
}
CWnd::Detach
HWND CWnd::Detach()
{
HWND hWnd = m_hWnd;
if (hWnd != NULL)
{
CHandleMap* pMap = afxMapHWND();
// don't create if not exist
if (pMap != NULL)
pMap->RemoveHandle(m_hWnd);
m_hWnd = NULL;
}
m_pCtrlSite = NULL;
return hWnd;
}
주의

Multiple threads


Permanent , temporary map 모두 thread 단위로 저장
전달할 경우 object 대신 HANDLE 을 보내는 것이
바람직
Thread 1
Thread 2
Window create
Window create
접근 불가
Find WinMain()

APPCORE.CPP


CWinApp 생성자 호출
APPMODULE.CPP

_tWinMain()
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine,
nCmdShow);
}
Find WinMain() (cont’d)

WINMAIN.CPP

AfxWinMain()
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE
hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
Find WinMain() (cont’d)
// Perform specific initializations
if (!pThread->InitInstance())
{
if (pThread->m_pMainWnd != NULL)
{
pThread->m_pMainWnd->DestroyWindow();
}
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
nReturnCode = pThread->Run();
AfxWinTerm();
return nReturnCode;
}
Find WinMain() (cont’d)

APPINIT.CPP

AfxWinInit()
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// handle critical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
Find WinMain() (cont’d)
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
// initialize thread specific data (for main thread)
if (!afxContextIsDLL) AfxInitThread();
return TRUE;
}
Find WinMain() (cont’d)

InitApplication()


InitInstance()


application-specific, but do nothing
instance-specific, virtual
Run()
CWinApp 생성자
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
if (lpszAppName != NULL) m_pszAppName = _tcsdup(lpszAppName);
else m_pszAppName = NULL;
// initialize CWinThread state
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
AFX_MODULE_THREAD_STATE* pThreadState =
pModuleState->m_thread;
pThreadState->m_pCurrentWinThread = this;
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// initialize CWinApp state
ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
CWinApp 생성자 (cont’d)
// in non-running state until WinMain
m_hInstance = NULL;
m_pszHelpFilePath = NULL;
m_pszProfileName = NULL;
m_pszRegistryKey = NULL;
m_pszExeName = NULL;
m_pRecentFileList = NULL;
m_pDocManager = NULL;
m_atomApp = m_atomSystemTopic = NULL;
m_lpCmdLine = NULL;
m_pCmdInfo = NULL;
// other initialization
m_bHelpMode = FALSE;
m_nSafetyPoolSize = 512;
// default size
}
InitApplication()
BOOL CWinApp::InitApplication()
{
if (CDocManager::pStaticDocManager != NULL)
{
if (m_pDocManager == NULL)
m_pDocManager = CDocManager::pStaticDocManager;
CDocManager::pStaticDocManager = NULL;
}
if (m_pDocManager != NULL)
m_pDocManager->AddDocTemplate(NULL);
else
CDocManager::bStaticInit = FALSE;
return TRUE;
}
InitInstance()
BOOL CWinApp::InitInstance()
{
return TRUE;
}
Run()
int CWinThread::Run()
{
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message
for (;;) {
// phase1: check to see if we can do idle work
while (bIdle && !::PeekMessage(
&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
Run (cont’d)
// phase2: pump messages while available
do {
// pump message, but quit on WM_QUIT
if (!PumpMessage()) return ExitInstance();
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage
(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
MFC state information


프로그램 종료까지 유지되는 정보
AFX_MODULE_STATE class


AFXSTAT_.H
보유하는 정보




Main window handles
Resource, module handles
Memory allocation tracking
ODBC support, OLE support, exception handling
Hidden Cool Stuff

2 issues


window class의 등록
Windows hook와 MFC window의 연결
Registering Window Classes

Windows application


OS 에 적어도 하나 이상의 window class를 등록해야만
한다.
window class

window의 기본적인 성질 정의


appearance ( via some flag )
behavior ( via a callback function )
Registering Window Classes (cont’d)

MFC의 window class 등록

4개의 기본 window class 등록





regular child windows
a control bar window
an MDI frame window
a window for an SDI or MDI child window
기타

common controls
Registering Window Classes (cont’d)

Style
WNDCLASSes and MFC
Style of window
LpfnWndProc
window proc, must be AfxWndProc
CbClsExtra
not used ( 0 )
CbWndExtra
not used ( 0 )
HInstance
automatically filled with AfxGetInstanceHandle
HIcon
icon for frame window
HCursor
cursor for when mouse is over window
HbrBackgroundbackground color
LpszMenuName
not used ( NULL )
LpszClassName
class name
Registering Window Classes (cont’d)

AfxWnd






Used for all child windows (CWnd::Create)
Class style : CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW
No icon
Arrow cursor
No background color
AfxFrameOrView





Used for frame windows and views
Class style : CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW
Icon AFX_IDI_STD_MDIFRAME
Arrow cursor
COLOR_WINDOW background color
Registering Window Classes (cont’d)

AfxMDIFrame






Used for MDI frame window (CMDIFrameWnd::Create)
Class style : CS_DBLCLKS
Icon Icon AFX_IDI_STD_MDIFRAME
Arrow cursor
No background color
AfxControlBar





Used for standard control bar implementation
Class style : 0
No icon
Arrow cursor
Gray background color (COLOR_BTNFACE)
Registering Window Classes (cont’d)

AfxDeferRegisterClass()


AFXIMPL.H
AfxEndDeferRegisterClass()

WINCORE.CPP
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister);
Class 등록 예
“WINCORE.CPP”
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth,
int nHeight, HWND hWndParent, HMENU nIDorHMenu, LPVOID
lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
Class 등록 예 (cont’d)
if (!PreCreateWindow(cs)) {
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance,
cs.lpCreateParams);
if (hWnd == NULL) GetLastError());
if (!AfxUnhookWindowCreate()) PostNcDestroy();
if (hWnd == NULL) return FALSE;
ASSERT(hWnd == m_hWnd);
// should have been set in send msg hook
return TRUE;
}
Class 등록 예 (cont’d)
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
// make sure the default window class is registered
VERIFY(AfxDeferRegisterClass(AFX_WND_REG));
// no WNDCLASS provided - use child window default
ASSERT(cs.style & WS_CHILD);
cs.lpszClass = _afxWnd;
}
return TRUE;
}
MFC’s Message Pump

CWinApp::Run()



이 함수가 실행되면서 실제 메시지 펌프가 동작하게
됨
이는 CWinApp가 CWinThread로 부터 상속받기 때문에
가능한 것임
Thread 종류


Worker thread : 일반적으로 말하는 thread
Interface thread : Message pump
MFC’s GDI 지원

GDI (Graphical Device Interface)



Windows OS상의 풍부한 그리기 작업
장치 독립적 지원 : GDI + Device driver
DC (Device Context)




GDI의 핵심적인 개념
GDI 함수의 parameter
스크린, 프린터 등을 표시
구조체 : 객체 + 속성 + 모드
Device Contexts

CDC

CPaintDC



CWindowDC



전체 스크린을 표시 (client area+frame)
GetWindowDC() ~ ReleaseDC()
CClientDC



painting 작업이 발생할 때
BeginPaint() ~ EndPaint()
Client area를 표시
GetClientDC() ~ ReleaseDC()
CMetaFileDC


메타파일에 대한 그리기 작업
생성자 ~ Create() ~ DeleteMetaFile()
Graphic Objects

CGdiObject : Base class







CPen
CBrush
CFont
CBitmap
CPalette
CRgn
Win32 API의 함수들을 wrapping
예제
CMyWnd::OnPaint() {
CPaintDC
paintDC(this);
CPen*
pOldPen;
CPen
bluePen(PS_SOLID, 25, RGB(0, 0, 255));
pOldPen = paintDC.SelectObject(&bluePen);
paintDC.MoveTo(1, 1);
paintDC.LineTo(100, 100);
paintDC.SelectObject(pOldPen);
}
Chap. 3
Message Handling
Contents





Introduction
Window messages
Message mapping
MFC가 message map을 이용하는 방법
Message loop의 hooking
Introduction

Message handling

C/SDK


Switch/case 문을 통해 구현
MFC


Switch/case 문은 없다
Callback function은 어떻게 정의되는가
Basic Components

MFC의 message handling 구조



CCmdTarget class
Message maps
MFC message maps에 대한 의문




어떻게 switch/case문을 대체하는가
어떤 data structure로 정의되는가
어떻게 message map이 작동하는가
어떻게 message가 연결되는가
Window Messages

3개 부분으로 구성됨

Unsigned integer


WPARAM



실제 메시지를 나타냄
Word(32bits)크기의 parameter
메시지에 따른 특정 데이타
LPARAM


4byte parameter
메시지에 따른 특정 데이타
Window Messages (cont’d)

핸들링 해야 할 메시지의 분류

Windows message



Control notification




WM_ 로 시작하는 메시지들 (WM_COMMAND 제외)
일반적으로 window, view 등에서 핸들링
WM_COMMAND 중 notify, WM_NOTIFY
Control, 자식 window 가 부모 윈도우에게 전달하는 메시지들
예) EN_CHANGED 를 포함한 WM_COMMAND 메시지
Command message

메뉴, 툴바, 엑셀레이터 등으로 인한 메시지
Window Messages (cont’d)

핸들링 클래스

Windows message, control notification


CWnd 로부터 파생된 클래스들 : HWND 를 포함
Command message


다양한 종류 : CCmdTarget 로부터 파생된 클래스들
예) open 명령 핸들링 : application
UI Objects and Command IDs
Message Handling in C/SDK

Windows program의 정수
While (GetMessage(&msg, NULL, NULL, NULL))
{
// Translates virtual key codes
TranslateMessage(&msg);
// Dispatches message to window
DispatchMessage(&msg);
}
// Returns the value from PostQuitMessage
Return (msg.wParam);
Message Handling in C/SDK (cont’d)
WNDCLASS ws;
…
ws.lpfnWndProc = WndProc;
…
RegisterClass(ws);
LRSULT WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_CREATE:
…
break;
case WM_COMMAND:
switch (wParam) {
case IDM_ABOUT:
…
}
}
Message Handling in MFC

방법 1



Polymorphism : 가상 멤버 함수 이용
심각한 메모리 낭비
방법 2


Message maps
MFC 의 특징
Message Mapping Internals

두 부분으로 구성

CCmdTarget


Window message나 command를 받기 위해 반드시
상속받아야 하는 class
Message map

Window message와 이를 처리하는 class member function을
연관시켜주는 mechanism
CCmdTarget Class

메시지의 처리


CCmdTarget의 파생 클래스
예



CWnd class
CDocument
CWinApp
Message Map

Data structures

AFX_MSGMAP_ENTRY(AFXWIN.H)


Message map table의 한 element(entry)
Message에 대한 정보와 message handler에 대한
정보를 저장
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode;
// control code or WM_NOTIFY code
UINT nID;
// control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig;
// signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
Message Map(contd.)

AFX_MSGMAP(AFXWIN.H)

실제 message map
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
Message Map Macros

Macros (AFXWIN.H)

DECLARE_MESSAGE_MAP


Header file에서 정의
BEGIN_MESSAGE_MAP / END_MESSAGE_MAP

Implementation file에서 정의
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
Message Map Macros (cont’d)
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return &theClass::messageMap; } \
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
{ &baseClass::messageMap, &theClass::_messageEntries[0] }; \
AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
{\
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
Massage Map 예
class CTestView : public Cview
{
protected:
//{{AFX_MSG(CTestView)
afx_msg void OnLButtonDblClk(UINT nFlags, Cpoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
class CTestView : public Cview
{
protected:
afx_msg void OnLButtonDblClk(UINT nFlags, Cpoint point);
private:
static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
static const AFX_MSGMAP messageMap;
virtual const AFX_MSGMAP* GetMessageMap() const;
};
Massage Map 예 (cont’d)
BEGIN_MESSAGE_MAP(CTestVIew, Cview)
//{{AFX_MSG_MAP(CTestView)
ON_COMMAND(ID_STRING_CENTER, OnStringCenter)
ON_WM_LBUTTONDBLCLK()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
const AFX_MSGMAP* CTestView::GetMessageMap() const
{ return &CTestView::messageMap; }
AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CTestView::messageMap =
{ &CView::messageMap, &CTestView::_messageEntries[0] };
const AFX_MSGMAP_ENTRY CTestVIew::_messageEntries[] =
{
ON_COMMAND(ID_STRING_CENTER, OnStringCenter)
ON_WM_LBUTTONDBLCLK()
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
Message Map Entry Macro

AFXMSG_.H
#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, \
AfxSig_vv, (AFX_PMSG)&memberFxn },
#define ON_WM_LBUTTONDBLCLK() \
{ WM_LBUTTONDBLCLK, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW) \
(void (AFX_MSG_CALL CWnd::*) \
(UINT, CPoint))&OnLButtonDblClk },
Message Map Entry Macro (cont’d)
Message Type
Macro Form
Predefined Windows messages
ON_WM_XXXX
Commands
ON_COMMAND
Update commands
ON_UPDATE_COMMAND_UI
Control notifications
ON_XXXX
User-defined message
ON_MESSAGE
Registered Windows message
ON_REGISTERED_MESSAGE
Range of command IDs
ON_COMMAND_RANGE
Range of command Ids for updating
ON_UPDATE_COMMAND_UI_RANGE
Range of control IDs
ON_CONTROL_RANGE
User-define Message
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
For example:
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
Handlers for Message-Map Ranges

종류




ON_COMMAND_RANGE
ON_UPDATE_COMMAND
ON_CONTROL_RANGE
Message-Map Entry

ID 는 연속적이어야 한다.
BEGIN_MESSAGE_MAP(CMyApp, CWinApp)
ON_COMMAND_RANGE(ID_MYCMD_ONE, ID_MYCMD_TEN,
OnDoSomething)
ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON1,
IDC_BUTTON10, OnButtonClicked)
END_MESSAGE_MAP( )
Handlers for Message-Map Ranges

Handlers
...
void CMyDialog::OnButtonClicked( UINT nID )
{
int nButton = nID - IDC_BUTTON1;
ASSERT( nButton >= 0 && nButton < 10 );
// ...
}
MFC의 Message Map이용

MFC-based program

두 종류의 메시지를 처리

regular window messages


commands


WM_MOUSEMOVE, …
WM_COMMAND
Message-mapping architecture의 이해

두 종류 메시지를 각각 추적
How to be wired

16-bit version

AfxWndProc()


Message handling procedure로 등록된 함수
32-bit version



DefWindowProc() 가 message handler로 등록
Message hook방법을 써서 결국에는 AfxWndProc()가
message를 처리하게 함
Hooking function


_AfxCbtFilterHook(), _AfxStandardSubclass()
3D Control을 지원하기 위함
How to be wired (cont’d)
“WinCore.cpp”
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
// mask off all classes that are already registered
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
fToRegister &= ~pModuleState->m_fRegisteredClasses;
if (fToRegister == 0) return TRUE;
LONG fRegisteredClasses = 0;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
How to be wired (cont’d)
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
…
AfxHookWindowCreate(this);
}
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
if (pThreadState->m_hHookOldCbtFilter == NULL) {
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
}
pThreadState->m_pWndInit = pWnd;
}
How to be wired (cont’d)
LRESULT CALLBACK
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
…
// subclass the window if not already wired to AfxWndProc
if (!bAfxWndProc)
{
// subclass the window with standard AfxWndProc
oldWndProc = (WNDPROC)SetWindowLong(hWnd,
GWL_WNDPROC, (DWORD)afxWndProc);
ASSERT(oldWndProc != NULL);
*pOldWndProc = oldWndProc;
}
…
}
Message Handling

AfxWndProc()(WINCORE.CPP)

WM_QUERYAFXWNDPROC


MFC’s message map을 사용하는 MFC window인지를
확인할 수 있는 message
다른 message에 대해서는 AfxCallWndProc()
함수를 호출
LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
if (nMsg == WM_QUERYAFXWNDPROC)
return 1;
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
Message Handling (cont’d)

AfxCallWndProc()(WINCORE.CPP)


Thread state 구조체에 message의 정보를 저장
WM_INITDIALOG처리


Auto-center dialog위한 처리를 수행
Window object의 window procedure호출



CWnd::WindowProc()(WINCORE.CPP)
내부적으로 OnWndMsg()를 호출
OnWndMsg()가 처리하지 못하면 CWnd::DefWindowProc()를
호출
Message Handling (cont’d)
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
MSG oldState = pThreadState->m_lastSentMsg; // save for nesting
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
CRect rectOld;
DWORD dwStyle = 0;
if (nMsg == WM_INITDIALOG)
_AfxPreInitDialog(pWnd, &rectOld, &dwStyle);
// delegate to object's WindowProc
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
// more special case for WM_INITDIALOG
if (nMsg == WM_INITDIALOG)
_AfxPostInitDialog(pWnd, rectOld, dwStyle);
pThreadState->m_lastSentMsg = oldState;
return lResult;
}
Message Handling (cont’d)

CWnd::WndowProc()(WINCORE.CPP)


virtual 이므로 override 가능
성능 향상을 위해 message-mapping system을 거치지
않고 처리해야 하는 메시지에 대한 핸들링 가능
“WinCore.Cpp”
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM
lParam)
{
// OnWndMsg does most of the work, except for DefWindowProc call
LRESULT lResult = 0;
if (!OnWndMsg(message, wParam, lParam, &lResult))
lResult = DefWindowProc(message, wParam, lParam);
return lResult;
}
Message Handling (cont’d)

CWnd::OnWndMsg()(WINCORE.CPP)

특별히 처리하는 message



WM_COMMAND, WM_NOTIFY
WM_ACTIVATE, WM_SETCURSOR
기타 다른 message들은 message map을 이용하여
해당 message handler를 호출
Message Handling (cont’d)
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam,
LRESULT* pResult)
{
// special case for commands
if (message == WM_COMMAND) {
if (OnCommand(wParam, lParam)) {
lResult = 1;
goto LReturnTrue;
}
return FALSE;
}
// special case for notifies
if (message == WM_NOTIFY) { }
// special case for activation
if (message == WM_ACTIVATE) { }
// special case for set cursor HTERROR
if (message == WM_SETCURSOR ) { }
Message Handling (cont’d)
const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();
UINT iHash; iHash = (LOWORD((DWORD)pMessageMap) ^ message) &
(iHashMax-1);
AfxLockGlobals(CRIT_WINMSGCACHE);
AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
const AFX_MSGMAP_ENTRY* lpEntry;
if (message == pMsgCache->nMsg && pMessageMap == pMsgCache>pMessageMap)
{
…
} else {
// not in cache, look for it
pMsgCache->nMsg = message;
pMsgCache->pMessageMap = pMessageMap;
for (/* pMessageMap already init'ed */; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap)
{
AfxFindMessageEntry();
}
Message Handling Trace

두가지 종류의 message

Regular window message


Command message



WM_MOUSEMOVE
메뉴나 control이 보내는 message
WM_COMMAND
서로 다른 방식으로 처리

CWnd::OnWndMsg()참고
Handling WM_COMMAND

가정


CWnd::OnWndMsg()함수에서


Main frame의 메뉴 명령 수행
CWnd::OnCommand()함수 호출
CWnd::OnCommand()


Virtual function(Framework이 적절한 version의
함수를 호출함)
CFrameWnd::OnCommand()


On-line help에 관한 것이면 해당 기능 수행
아니면 CWnd::OnCommand()함수 호출
Handling WM_COMMAND (cont’d)
“WinFrm.cpp”
BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
HWND hWndCtrl = (HWND)lParam;
UINT nID = LOWORD(wParam);
CFrameWnd* pFrameWnd = GetTopLevelFrame();
ASSERT_VALID(pFrameWnd);
if (pFrameWnd->m_bHelpMode&&hWndCtrl==NULL&&nID!=ID_HELP&&
nID!= ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP) {
// route as help
if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))
SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
return TRUE;
}
// route as normal command
return CWnd::OnCommand(wParam, lParam);
}
Handling WM_COMMAND (cont’d)
“WinCore.Cpp”
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
if (hWndCtrl == NULL)
else {
if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
return TRUE;
// locked out - ignore control notification
// reflect notification to child window control
if (ReflectLastMsg(hWndCtrl))
return TRUE; // eaten by child
// zero IDs for normal commands are not allowed
if (nID == 0)
return FALSE;
}
return OnCmdMsg(nID, nCode, NULL, NULL);
}
Handling WM_COMMAND (cont’d)

CWnd::OnCommand()(WINCORE.CPP)



LPARAM을 조사
만약 control이 보낸 message이면 control에게 다시
message를 reflect한 후 return
아니면 CWnd::OnCmdMsg()(virtual function)함수
호출
Handling WM_COMMAND (cont’d)
“WinFrm.cpp”
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
CPushRoutingFrame push(this);
// pump through current view FIRST
CView* pView = GetActiveView();
if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
Handling WM_COMMAND (cont’d)
“ViewCore.cpp”
BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// first pump through pane
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// then pump through document
if (m_pDocument != NULL)
{
// special state for saving view before routing to document
CPushRoutingView push(this);
return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
return FALSE;
}
Handling WM_COMMAND (cont’d)
“DocCore.cpp”
BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
// otherwise check template
if (m_pDocTemplate != NULL &&
m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
return FALSE;
}
Handling WM_COMMAND (cont’d)

CFrameWnd::OnCmdMsg()(WINFRM.CPP)

다음의 순서로 해당 OnCmdMsg()함수를 호출




Active view
Active view’s document
Main frame window
Application
Handling WM_COMMAND (cont’d)

만약 active view에 해당 handler가 있다면


CView::OnCmdMsg()가 호출됨
CView::OnCmdMsg()(VIEWCORE.CPP)



View에서 처리를 시도한 후 안되면 document에서
처리를 시도
View에서 처리 시도는 CWnd::OnCmdMsg()함수의
호출로 이루어짐
이 때 CWnd는 OnCmdMsg를 override하지 않기
때문에 CCmdTarget::OnCmdMsg를 호출하는 결과가
됨
Handling WM_COMMAND (cont’d)
“CmdTarg.cpp”
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
for (pMessageMap = GetMessageMap(); pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap) {
lpEntry = AfxFindMessageEntry
(pMessageMap->lpEntries, nMsg, nCode, nID);
if (lpEntry != NULL) {
return _AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
}
}
return FALSE; // not handled
}
Handling WM_COMMAND (cont’d)

CCmdTarget::OnCmdMsg()(CMDTARG.CPP)



Message map을 보고 message handler를 찾음
찾게 되면 _AfxDispatchCmdMsg()함수를 호출하여
해당 handler를 실행시킴
_AfxDispatchCmdMsg()(CMDTARG.CPP)

Function signature에 따라 다른 동작을 수행




Regular command
Extended command
Visual Basic control
등등
Handling WM_COMMAND (cont’d)

Frame window일 때의 처리과정 정리










AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CCmdTarget::OnCmdMsg()
_AfxDispatchCmdMsg()
CMainFram::On~()
Handling WM_COMMAND (cont’d)

Document일 때의 처리과정 정리












AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CView::OnCmdMsg()
Cdocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
_AfxDispatchCmdMsg()
CMyDoc::On~()
Handling WM_COMMAND (cont’d)

View일 때의 처리과정 정리











AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
_AfxDispatchCmdMsg()
CMyView::On~()
Handling WM_COMMAND (cont’d)

App일 때의 처리과정 정리








AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CFrameWnd::OnCommand()
CCmdTarget::OnCmdMsg()
_AfxDispatchCmdMsg()
CMyApp::On~()
Handling WM_COMMAND (cont’d)

Dialog Box일 때의 처리과정 정리








AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CDialog::OnCmdMsg()
CCmdTarget::OnCmdMsg()
_AfxDispatchCmdMsg()
CAboutDlg::On~()
예 – Clear All

Handling 과정 (MDI application)





Main frame window가 message를 받는다
현재 활성화된 MDI child window가 처리 할 기회를
얻는다
Child window가 먼저 view에게 처리 기회를 준다.
실패하여 view가 연결된 document가 처리를 시도한다
Document가 메시지 핸들러를 찾는 데 성공한다.
Handling Regular Window Message

Command message일 때와 처음의 과정은 비슷




AfxWndProc()
AfxCallWndProc()
WindowProc()
OnWndMsg()

이 함수 안에서 message handler를 찾기
위해AfxFindMessageEntry()를 호출
Handling Regular Window Message
(cont’d)

AfxFindMessageEntry()

두 버전이 있음.




Assembly language : intel-based machine
C language : otherwise
Message map에서 해당 핸들러가 있는지 검색
END_MESSAGE_MAP에 의해 생성된 table의 끝까지
검색
Handling Regular Window Message
(cont’d)

Command message와의 차이점


Command message는 handler를 찾기 위해서 여러
장소를 옮겨다님
Regular message는 OnWndMsg()에서 handler를
찾으면 바로 해당 handler를 호출하고, 아니면
DefWindowProc()를 이용
Handling Regular Window Message
(cont’d)

예) View에서 WM_SIZE message처리 과정





AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnWndMsg()
CSdiappView::OnSize()
Other Kinds of Messages

지금까지 살펴 본 메시지 종류



WM_COMMAND
Window messages(WM_SIZE, WM_MOVE, …)
그 이외의 메시지 종류



WM_NOTIFY
WM_ACTIVATE
WM_SETCURSOR
Other Kinds of Messages (cont’d)

WM_NOTIFY



Control이 보내는 message
항상 notify message
반면, WM_COMMAND

command이거나 notify message
Other Kinds of Messages (cont’d)
CWnd::OnWndMsg()에서
// special case for notifies
if (message == WM_NOTIFY)
{
NMHDR* pNMHDR = (NMHDR*)lParam;
if (pNMHDR->hwndFrom != NULL
&& OnNotify(wParam, lParam, &lResult))
goto LReturnTrue;
return FALSE;
…
}
struct NMHDR {
HWND
hwndFrom;
UINT
idFrom;
UINT
code;
// control that sent notification
// ID of control
// notification code
Other Kinds of Messages (cont’d)
“Wincore.cpp”
BOOL CWnd::OnNotify(WPARAM, LPARAM lParam, LRESULT* pResult)
{
NMHDR* pNMHDR = (NMHDR*)lParam;
HWND hWndCtrl = pNMHDR->hwndFrom;
// get the child ID from the window itself
UINT nID = _AfxGetDlgCtrlID(hWndCtrl);
int nCode = pNMHDR->code;
if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
return TRUE;
// locked out - ignore control notification
// reflect notification to child window control
if (ReflectLastMsg(hWndCtrl, pResult)) return TRUE;
// eaten by child
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), &notify, NULL);
}
Other Kinds of Messages (cont’d)
BOOL PASCAL CWnd::ReflectLastMsg(HWND hWndChild, LRESULT* pResult)
{
CHandleMap* pMap = afxMapHWND();
CWnd* pWnd = (CWnd*)pMap->LookupPermanent(hWndChild);
if (pWnd == NULL) {
// check if the window is an OLE control
…
return FALSE;
}
return pWnd->SendChildNotifyLastMsg(pResult);
}
Other Kinds of Messages (cont’d)
BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult)
{
_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
return OnChildNotify(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam,
pThreadState->m_lastSentMsg.lParam, pResult);
}
Other Kinds of Messages (cont’d)

Message reflection



OnWndMsg()에서 OnNotify()를 호출
OnNofity()는 다시 OnChildNotify()를 호출하여
control로 message를 다시 보냄
결국에는 control이 parent에 의존하지 않고 자신의
일을 처리하게 함
Other Kinds of Messages (cont’d)

WM_ACTIVATE


OnWndMsg()함수에서 _AfxHandleActivate()를 호출
WM_SETCURSOR

OnWndMsg()함수에서 _AfxHandleSetCursor()함수 호출
Speed Message-map Matching

방법



최근에 처리 된 메시지를 캐쉬에 저장
같은 메시지를 다시 처리할 가능성이 높음
장점

Unhandled message 빠르고 효과적으로 처리
Message Loop Hooking

Message loop hooking


Message가 해당 handler에 의해 처리되기 전에
어떠한 작업을 하고 싶을때
PreTranslateMessage()

CWinApp::PreTranslateMessage()



CWinApp::Run()함수는 message가 message pump에 의해
처리되기 전에 위 함수를 호출
TRUE를 return하면 message pump는 해당 message에
관해서는 처리를 하지 않음
CWnd::PreTranslateMessage()
Message Loop Hooking (cont’d)
“THRDCORE.cpp”
int CWinThread::Run()
{
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume "no idle" state
}
Message Loop Hooking (cont’d)
// phase2: pump messages while available
do {
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset "no idle" state after pumping "normal" message
if (IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL,
PM_NOREMOVE));
}
ASSERT(FALSE); // not reachable
}
Message Loop Hooking (cont’d)
BOOL CWinThread::PumpMessage()
{
ASSERT_VALID(this);
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
}
// process this message
if (m_msgCur.message != WM_KICKIDLE
&& !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
Message Loop Hooking (cont’d)
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
// if this is a thread-message, short-circuit this function
if (pMsg->hwnd == NULL && DispatchThreadMessageEx(pMsg))
return TRUE;
// walk from target to main window
CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
return TRUE;
// in case of modeless dialogs, last chance route through main
// window's accelerator table
if (pMainWnd != NULL) {
CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd);
if (pWnd->GetTopLevelParent() != pMainWnd)
return pMainWnd->PreTranslateMessage(pMsg);
}
return FALSE; // no special processing
}
Message Loop Hooking (cont’d)
“WinCore.Cpp”
BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
{
// walk from the target window up to the hWndStop window checking
// if any window wants to translate this message
for (HWND hWnd=pMsg->hwnd; hWnd != NULL;hWnd=::GetParent(hWnd))
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
if (pWnd != NULL) {
// target window is a C++ window
if (pWnd->PreTranslateMessage(pMsg))
return TRUE;
}
// got to hWndStop window without interest
if (hWnd == hWndStop)
break;
}
return FALSE;
// no special processing
}