Download presentation
Presentation is loading. Please wait.
1
MFC Internals
2
Contents Chapter 01 MFC 개관 Chapter 02 MFC와 C/SDK
Chapter 03 MFC에서의 Message 처리 Chapter 04 MFC Utility Class Chapter 05 모든 길은 Cobject로 통한다 Chapter 06 MFC Dialog와 Control Dialog Chapter 07 MFC의 Document/View Architecture
3
Chapter 01 MFC 개관
4
Contents Application Frameworks 정의 MFC의 역사 MFC의 설계 목적 MFC의 구성
MFC code basics
5
Application Frameworks
특정 영역의 작업을 도와 주는 class 집합 Application Framework 특정 OS의 application 개발 작업을 도와 주는 class 집합 MFC (Microsoft Foundation Classes) Windows용 application 개발 작업을 도와 주는 framework MFC란 Microsoft의 Windows 운영체제(Operating System)에서 사용할 수 있도록 응용 프로그램을 만들기 위해 잘 다듬어진 application framework이다. MFC는 OOP의 원리를 사용하여 windows API를 논리적으로 잘 묶어주고 있다.
6
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 - AFX group Windows 프로그램 개발자를 위해서 C++로 이루어진 객체 지향 툴을 만들 목적으로 설립 개발자들이 보다 향상된 GUI 프로그램을 작성할 수 있도록 가장 최근의 OOP 기술만을 사용하여 개발 툴과 라이브러리를 제공하는 것을 원칙으로 함 범용적이지 못하고, 자신들의 기준에 맞게 framework를 구성하게 되었고 이를 통해 프로그램을 만든다는 것은 너무 어렵고 복잡하다고 판단됨
7
MFC의 역사 (cont’d) 1992년 MFC version 1.0 C/C++ version 7.0과 함께 나옴
60개가 넘는 Windows application 개발을 위한class포함 일반 목적의 여러 class포함 완벽하지 못했음 개발자로부터 많은 불평을 받음 일반 목적의 여러 class - time, string, collection, file, persistent storage, memory management, exception, diagnostic과 관련된 클래스
8
MFC의 역사 (cont’d) 1993년 MFC version 2.0
Visual C++ version 1.0과 Windows NT와 함께 나옴 100개가 넘는 class포함 새로 포함된 내용 새로운 application architecture 새로운 high-level abstractions MFC 2.0은 대부분의 Windows API와 OLE 1.0을으로 이루어졌다. 여기에는 100여개의 class와 60,000줄의 표준 C++ 코드가 있다.
9
MFC의 역사 (cont’d) 1993년 말 1994년 MFC version 2.5
Visual C++ version 1.5와 함께 나옴 OLE2.0과 ODBC 지원이 추가됨 1994년 MFC version 3.0 Thread에 대한 기능이 보강됨
10
MFC의 역사 (cont’d) 1995년 MFC version 3.1 추가된 기능
Simple Messaging Application Programming Interface(MAPI) WinSock MFC version 4.0 Visual C++ version 4.0과 함께 나옴 개발환경의 발전, 재사용성의 향상
11
MFC의 역사 (cont’d) 1997년 1998년 2002년 MFC version 4.2
Visual C++ version 5.0과 함께 나옴 Internet 등을 위한 기능 추가 1998년 MFC version 6.0 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
12
MFC의 역사(cont’d) MFC version과 Visual C++ version
Microsoft Visual C++ version 6.0 6.0 Microsoft C/C++ version 7.0 1.0 Microsoft Visual C++ version 1.0 2.0 2.1 Microsoft Visual C++ version 1.5 2.5 Microsoft Visual C++ version 2.0 3.0 Microsoft Visual C++ version 2.1 3.1 Microsoft Visual C++ version 2.2 3.2 Microsoft Visual C++ versions 4.0 and 4.1 4.0 Microsoft Visual C++ versions 4.2 and 5.0 4.2 Visual C++ version MFC version
13
MFC 설계 목적 AFX 그룹의 설계 목적 Real-World Application
Simplifying the Windows API Using the existing knowledge of Windows Foundation for large-scale applications Small and fast framework AFX 그룹은 새로운 Framework을 설계하기 위한 5가지 목적을 두었다. 객체지향기술과 C++을 사용하여 실세계에서 사용 가능한 응용프로그램을 발표 프로그램 초보자 및 전문가를 위해, Windows 프로그램 개발을 간소화 Windows에 대한 기존의 지식을 계속해서 사용할 수 있도록 유지 가장 최근의 Windows 기술(OLE, ODBC…) 을 이용하여 거대한 규모의 응용 프로그램을 작성할 수 있도록 개발자를 위한 토대 마련 5) 가능한 한 application framework를 빠르고 작게 설계
14
MFC의 구성 MFC class 의 분류 General-purpose classes Windows API classes
Application framework classes High-level abstractions Operating System Extension MFC Class의 외관상 분류 일반적인 목적의 Class - windows class, dialog box class, device context class… 2) Windows API를 포장하는 class 3) Application framework class - message pump, printing, on-line help, document/view architecture… 4) High-level abstractions - toolbar, splitter windows, status lines… 5) Operating System Extension - OLE, ODBC, MAPI, WinSock…
15
MFC의 구성(cont’d) General-purpose Class 프로그램의 일반적 용도를 위한 class
(1) CObject class MFC의 최상위 class run-time type information, serialization, diagnostic function, support for dynamic creation 4가지 중요한 기능 제공 CArchive, CDumpContext, CRuntimeClass 등과 연계되어 작용 CObject MFC의 최상위 클래스로써 아래의 기능을 제공한다. 1) CAchive는 CObject와 함께 개체의 영속성(지속성)을 처리한다. 2) CDumpContext는 진단 함수(diagnostic function)에서 사용된다. - 객체에 대한 정보를 사용자에게 보여준다. 주로 dubuging할 때 사용된다. 3) CRuntimeClass 는 CObject에서 파생된 모든 class와 관계되면, 그 class 에 대한 정보를 가지고 있다. - runtime-identification, serialization, diagnostic function, dynamic object creation…
16
MFC의 구성(cont’d) (2) Exception-handling class memory, I/O error 발생 시 처리
CException : base class CArchiveException, CFileException CMemoryException, CResourceException CNotSupportedException, CUserException COleException, CDBException exception(예외)은 응용프로그램이 제어권을(ex memory 부족, 입/출력error) 잃었을 때 발생한다. MFC는 예외를 처리할 수 있는 class를 제공한다. MFC 함수는 응용프로그램을 보호하기 위해서 예외를 exception handler에게 던진다.(throw) MFC는 아래와 같이 exception handler class를 정의해 놓았다. CException : 예외 상황 처리의 기본 Class CArchiveException : serializatoin시 발생되는 예외를 처리 CFileException : file 연산이 수행되는 동안 발생하는 예외를 처리 CMemoryException : memory 예외를 처리 CNotSupportedException : 프로그램이 지원하지 않는 기능을 수행하려 할 때 발생한 예외를 처리 COleException : OLE client나 OLE server 에 대한 예외를 처리 CDBException : Database를 처리하는 동안 던져진 예외 처리 CUserException : 응용프로그램에서 정의한 예외를 처리 CResourceException : Windows resource를 읽는 것을 실패했을 때 처리
17
MFC의 구성(cont’d) (3) Collection class
Array : CByteArray, CWordArray, CDWordArray, CPtrArray, CObArray, CStringArray, CUnitArray Linked list : CObList, CPtrList, CStringList Map : CMapPtrToWord, CMapPtrToPtr, CMapStringToOb, CMapStringToPtr, CMapStringToString, CMapWordToOb, CMapWordToPtr Array : MFC 안에서는 size를 마음대로 조정가능하다. (가변적이다.) CByteArray : byte 배열 CWordArray : word 값의 배열 CDWordArray : double word값의 배열 CPtrArray : pointer의 배열 CObArray : Cobject로부터 파생된 객체의 배열 CStringArray : CString 객체의 배열 CUnitArray : unsigned integer의 배열 Linked list : MFC 는 양방향 검색 가능 CObList : Cobject로부터 파생된 객체의 Linked list CPtrList : pointer의 linked list CStringList : Cstring 객체의 linked list Map : = 사전이라 함. 어떤 스트링이 주어지면 여기에 맞는 index 값을 리턴시킨다. Hello 는 2번, Korea 는 3번에 임의로 넣어준다. Hash Function을 통해서…. 검색을 할때 유용하다. CMapPtrToWord : pointer와 word의 맵 CMapPtrToPtr : 두 pointer의 맵 CMapStringToOb : string과 Cobject로부터 파생된 객체의 맵 CMapStringToPtr : string과 pointer의 맵 CMapStringToString : 두 string의 맵 CMapWordToOb : word와 Cobject로부터 파생된 객체의 맵 CMapWordToPtr : word와 pointer의 맵
18
MFC의 구성(cont’d) (4) Dynamic string class CString
concatenation, comparison, assignment 등의 기본 연산 제공 (5) File class CFile, CStdioFile, CMemFile 추상적으로 disk상의 파일 제어, 실제로는 memory상의 파일 제어 동적 스트링 MFC에서 제공되는 동적 string인 CString을 사용하면 사용상의 편리성과 기본적인 string 연산 기능들을 제공한다. - strlen(), strcat(), strstr() … 파일 클래스 MFC의 file 클래스는 file 입/출력을 캡슐화 한다. CFie : 표준 이진파일 I/O를 다룬다. CStdioFile : buffered file I/O를 다룬다. CMemFile : memory file을 다룬다. 마치 memory file은 disk에 존재하는 것처럼 행동하는 binary file이지만, 사실 그것들은 memory 에 존재하고 있다.
19
MFC의 구성(cont’d) (6) Time class CTime, CTimeSpan (7) 기타
CPoint, CSize, CRect : Windows structure CTime 과 CTimeSpan은 응용프로그램 내에서 시간을 쉽게 다루도록 해준다. CTime 시간을 표시 현재의 시간을 알아 낼 수 있는 member function을 제공 시간을 문자열로 바꿀 수 있다. 시간의 특정 요소를 추출할 수 있다. 시간에 대한 연산 및 비교, 할당 연산자 제공 CTimeSpen : 시간을 초단위로 표시 시간의 경과를 보여주는 데 편리 시간에 따른 뺄셈, 비교, 할당 연산자 제공
20
MFC의 구성(cont’d) Windows API class (1) Application 관련 class
CCmdTarget : message 처리 CCmdUI : user interface의 update CWinThread : MFC program의 실행 thread를 의미, 즉 program의 main을 포함 CWinApp : CWinThread의 파생 class으로서 standard windows application을 표현 Windows API를 Class로 포장(wrap) CObject | CCmdTarget 메시지 핸들링, message mapping 개념을 이용한 메시지 처리 => 어떤 이벤트가 발생했을 때, 그 이벤트를 처리해 주는 어떤 class내의 member fuction과 연계 따라서, 여기서 파생된 클래스는 모두 message를 처리 할 수는 message mapping을 가지고 있다. CCmdUI user_interface(menu, checkbox…) 의 상태를 변경시켜주는 여러 가지 방법들을 제공 ex) 메뉴의 상태들을 API함수대신 객체를 통해 처리 CWinThread MFC 프로그램내에서 안정된 스레드 제공
21
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
22
MFC의 구성(cont’d) CPropertySheet, CPropertyPage
Controls : CButton, CEdit, … Cmenu GDI 관련 class CDC, CPaintDC, CWindowDC, CClientDC, CMetaFileDC CPen, CBrush, CFont, ...
23
MFC의 구성(cont’d) Application framework class Document/View Architecture
CDocTemplate, CSingleDocTemplate, CMultiDocTemplate : document와 view를 연결 CDocument : data를 관리 CView : data를 redering하여 보여 줌 Context-Sensitive Help
24
MFC의 구성(cont’d) High-level abstraction Enhanced Views
CScrollView, CFormView CEditView, CListView, CRichEditView, CTreeView Splitter Window CSplitterWnd : dynamic, static Control Views CToolBar, CStatusBar, CDialogBar
25
MFC Code Basics Class Declaration Subsections MFC library 개발 팀의 코딩 규칙
Not based public/protected/private Private 변수는 거의 사용하지 않는다. Header / source file // Constructors // Attributes // Operations // Overridables // Implementation
26
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 };
27
MFC Comments Class Declaration Subsections
MFC library 개발 팀의 코딩 규칙 Class header file의 각 항목의 의미 // Constructors(생성자 함수들, 초기화 함수들) C++ constructors, any other initialization 예) CWnd::Create 대개는 public // Attributes(위의 내용을 setting하거나 얻어옴) 대개는 documented public data members Member functions(위의 data를 조작하는) : Get / Set 함수들
28
MFC Code Basics (cont’d)
// Operations(값이 변한다>??) Documented member functions 대개는 public, non-const : side effects // Overridables 상속 받은 class가 override한 functions Pure virtual functions // Implementation(현재 쓰고 있는 개념임) Implementation detail Undocumented 대개는 protected 주의 : may change in future versions of MFC
29
MFC Code Basics (cont’d)
Variable Naming (common) Pointer to a function lpfnHookProc lpfn callback hWnd h handle Z indicates NULL terminated. lpszFileName lpsz LPSTR lpWnd lp FAR * pWnd p * (pointer) dwPackedmessage dw DWORD lAxisRatio l LONG wListID w WORD nMyUnsigned n UINT nVariableCnt int blsSending b BOOL cDirSeparator c char Comment Example Prefix Type
30
MFC Code Basics (cont’d)
Variable Naming (MFC extensions) pWndDialog pWnd CWnd* WndControl Wnd CWnd strFind str CString szRectangle sz CSize ptMouseClick pt CPoint rectScroll rect CRect Example Prefix Class
31
MFC Code Basics (cont’d)
Symbol Naming 1-0x6FFF IDC_HAND IDC_ Cursor resource Icon resource IDB_SMILEY IDB_ Bitmap resource 0x2001-0x26FF HIDD_HELP_ABOUT HIDD_ Dialog resource help context ID (for context-sensitive help) IDD_ABOUT IDD_ Dialog resource IDR_MAINFRAME IDR_ Shared by multiple resources Range Example Prefix Type
32
MFC Code Basics (cont’d)
Symbol Naming 8-0xDFFF IDC_COMBO1 IDC_ Control in dialog template 1-0x7FFF IDS_ERROR12 IDS_ String resource 0x3008-0x3DFF HIDP_FATALERROR HIDP_ Message box help context IDP_FATALERROR IDP_ Message box prompt 0x1800-0x1DFF HID_CIRCLE_TOOL HID_ Command help context 0x8000-0xDFFF ID_CIRCLE_TOOL ID_ Menu or toolbar command Range Example Prefix Type
33
Chapter 02 MFC와 C/CDK
34
Contents Introduction MFC versus C/SDK Basic Application Components
CWinApp CWnd Window handles & Window objects Find WinMain() Hidden Cool Stuff Registering Window Classes MFC’s Windows Hooks MFC’s Message Pump MFC’s GDI Support
35
Introduction MFC A Windows program is still a Windows program
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 등록, 메시지 루프, …
36
Issue MFC가 어떻게 Windows application을 만드는가
The application itself(응용프로그램 그 자체) - main thread Windows Message handling(위의 두가지를 연결해줌) The Graphics Device Interface (GDI) Application 은 mainThread 와 Windows 두개로 이루어져있고, Message handling을 통해 연결해 주고 있다.
37
MFC vs. C/SDK Motivation
모든 Windows application은 다음 2개의 component를 포함한다. main application itself message를 핸들하는 하나 이상의 window C/SDK 개발 환경 copy & paste Time-consuming & inefficient C++/MFC 개발 환경 OOP 활용 : inheritance & encaptulation 필요한 부분만 변경
38
Boilerplate Code 왜 필요한가? Windows is event-driven OS
Imposes a grate deal of overhead Windows OS H/W 와 응용 프로그램을 연결하는 위치 Application에게 발생하는 이벤트를 알림 이벤트를 다루기 위해 상당한 양의 코드가 항상 필요 Bolierplate : 반복적으로 판을 찍어내는 것을 말한다. 왜 필요한가 : 윈도우는 이벤트바탕의 os이기 때문이다.-> 이벤트를 다루기 위해서는 상당한 양의 코드가 필요하기 때문이다.
39
메시지 처리를 위한 작업들 Set up a message handler and register it
RegiserClass() : 윈도우 프로시져 등록 Windows가 application의 instance들을 추적 application은 Windows에게 메시지를 요청(ask)하고, 처리(dispatch)한다. application이 종료될 때까지 위 작업을 반복한다. -> 윈도우
40
Application의 준비 WinMain() 함수 적어도 하나의 main window class를 등록
프로그램의 시작점 Windows 로부터 프로그램을 실행하는데 필요한 정보를 얻어 오는 통로 현재 instance의 handle, 직전에 실행된 instance의 handle, command line argument, window의 모습(최대화, 최소화, …) 적어도 하나의 main window class를 등록 User interface를 제공 Message loop를 설정 Some initialization and setup(초기화 작업) Application specific, instance specific Message handler를 제공 최소한 WM_DESTROY 처리 -> WM_QUIT 생성 4. 번의 경우 : 두개의 일반적인 초기화 작업이 필요하다. -> 하나의 어플리케이션이 실행되면 여러 개의 인스턴스를 가지고 있다. Application specific : 2번에서 실행되는 초기화 instance specific : 각 인스턴스가 실행될때마다 초기화가 필요하다.(ex CreateWindows)
41
(Main window(UI)생성, 보여줌)
Application의 기본 요소 Main 함수 Application specific Initialization (Window class 등록) Instance specific (Main window(UI)생성, 보여줌) Message loop Message Handler 두개의 틀로 구성된다. Main 틀 - 메시지 루프에서 발생된 이벤트를 메시지 핸들러를 통해 구현한다. 2. 메시지를 핸들링하는 독립된 틀
42
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); default: /* Passes it on if unprocessed */ return (DefWindowProc(hWnd, message, wParam, lParam)); } return 0; MainWndProc : 앞장의 메시지 핸들링을 위한 함수임. switch case 문이다. Command message에서도 다시 case문이 필요하다. 아주 복잡하지? Destroy : 부분은 반드시 있어야 종료가 되겠지?
43
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)); } 어플리케이션의 초기화 작업 Application specific : 2번에서 실행되는 초기화작업을 말한다. 중요한 부분 : 윈도우 클래스를 등록하는 작업
44
BOOL InitInstance(HANDLE hInstance, int nCmdShow) {
HWND hWnd; /* Main window handle */ hInst = hInstance; // needed for loading resources // hWnd = CreateWindow( “MinimalWClass”, /* Window class */ “Minimal”, /* Caption */ WS_OVERLAPPEDWINDOW, /* Window style */ CW_USEDEFAULT, /* Default horizontal pos. */ CW_USEDEFAULT, /* Default vertical pos. */ CW_USEDEFAULT, /* Default width */ CW_USEDEFAULT, /* Default height. */ NULL, /* No parent */ NULL, /* Use the window class menu */ hInstance, /* This instance owns the window. */ NULL); 인스턴스의 초기화 작업 instance specific : 각 인스턴스가 실행될때마다 초기화가 필요하다.(ex CreateWindows) 각 옵션들이 보이지? 윈도우 생성, 보여줌.
45
if (!hWnd) return (FALSE); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return (TRUE); }
46
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*/ 프로그램의 시작점( 4개의 값을 가져온다.) InitApplication : 최초로 실행되는 인스턴스라면 윈도우 클레스를 등록하겠다. InitInstance ; 모든 인스턴스들의 초기화 작업, 보여준다. Getmessage : 메시지 루프 부분으로 3개의 함수로 구분된다. 메시지를 가져오고, 메시지를 재번역하고, 등록되어 있는 함수로 메시지를 던져준다. 메시지 핸들러는 이전 차트에서 보였지 이런 4가지 부분이 중요한 포인트겠쥐??
47
Source 2 : MFC #include <afxwin.h> // MFC 주요 클래스 선언
class CGenericApp : public CWinApp { public: virtual BOOL InitInstance(); }; class CGenericWindow : public CFrameWnd { CGenericWindow() { Create(NULL, “Generic”); } afx_msg void OnLButtonDown(UINT nFlags, CPoint point); DECLARE_MESSAGE_MAP() 일반적인 MFC Program 임당…………………… .H 부분 #include 하고 두개의 클레스를 선언 2. CWinApp 파생 클레스 - Initlinstace 를 오버라이딩한다. 3. CFrameWnd 파생 클레스 선언한다. - 생성자를 만든다. - 왼쪽버튼클릭관련
48
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; .cpp 파일부분 InitInstance 부분 파생클레스의 글로벌 변수 선언 황당하다>?? 소스 자체로 보았을때 : 메모리에 로드되는 부분이 없다,, CGenericApp Genner icApp : 이 한라인만 메모리에 로딩된다. 이 외의 부분은 MFC 라이브러리 안에 숨겨져 있다. WinMain이 없다. 윈도우 클레스 등록이 없다. 윈도우 크레이트 , 생성, 보여주는 부분은 있다. : InitInstance() 함수 -> 언제 어떻게 호출되는지는 없다. 메시지 루프가 없다. MFC 안에서 할것이다. 핸들러 함수 구현했다. OnLButtonDown : 하지만 이벤트가 발생했을때 직접 윈도우클레스를 등록하지도 않았는데 어케 실행될것인가?? 단지 어플리케이션마다 달라지는 부분만 코딩해주면 되고 나머지 부분은 Boilerplate Code 부분으로 라이브러리 안에 숨겼다.
49
C/SDK와 MFC의 비교 WinMain() Function Window class 등록 Message loop
MFC : where? ( part of application framework ) Window class 등록 MFC : where? Message loop Additional functions : idle processing, PreTranslateMessage()
50
C/SDK와 MFC의 비교 (cont’d)
Instance initialization C/SDK : 존재 MFC : 존재, 그러나 언제 실행? Message handling MFC : 존재, 그러나 어떻게 command-routing, message-dispatch? Member functions + Message map AfxWndProc() : 공통 window procedure 항상 시작점이다.
51
Basic MFC Application Components
Windows application의 두 가지 components Message pump Window procedure MFC에서의 구현 방법 CWinApp Application을 나타내는 class application-specific : 초기화, window 생성, 메시지 루프 CWnd Window를 나타내는 class window-specific : 메시지 핸들링 CWinApp(MainThread를 담당, CWnd(Window를 담당) 클레스가 있는데 이 것이 두가지의 틀이다.(MainThread, Window)
52
CWinApp (“AfxWin.h”) CWinApp : public CWinThread { public:
// Constructor CWinApp(LPCTSTR lpszAppName = NULL); // app name defaults to EXE name // 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) 가장 최신버젼 피티다 : 책은 틀린 부분이 있다. CWinApp 클레스 : Cwin Thread의 클레스의 파생클레스 Startup args : 처음에 생성되면 변하지 않는 아규먼트 -> 4개의 아규먼트가 WinMain에서 보았던 아규먼트와 같다.(API에서) Running args : 실행시 변경되는 아규먼트
53
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); 가상함수가 2개가 나오지 : MainThread의 내용이었지?(밑에 두개) InitApplication : API에서는 클레스 등록이었는데 여기서는 아니다. IniInstance : 아무내용이 없다. 어플리케이션마다 달라지기 때문에 반드시 오버라이딩이 필요하다. ExinInstacne : 인스턴스 종료될때 호출 Run : 메시지 루프를 만들어 주는 가상함수 OnIdle :
54
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() };
55
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
56
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) CCommandLineInfo : 세번째 들어온 내용을 조각내서 담고 있다.] (LPTSTR m_lpCmdLine 여기에 들어온 내용) 피티54페이지 참조…..
57
CWinApp (cont’d) InitInstance() : instance-specific 초기화
ExitInstance() : instance 종료 시 처리 Run() : message pump 수행 OnIdle() : Message queue가 비었을 때, Run 함수에 의해 호출 OnId() ; 응용프로그램이 쉬는 순간이 되겠지?
58
CWnd AFXWIN.H 2가지 기능 수행 Wrapping the regular Windows API
예: Create(), ShowWindow(), … Higher-level MFC-related functionality 예: default message handling CWnd : 2가지 기능(메시지를 핸들링하는 부분임) Windows API 포장 - 가장 기본적인 내용으로 예의 내용들을 래핑한다. 그 의 특징 : MFC와 관계되는 높은 수준의 기능들(API와 다른 메시지 핸들링 기법) 상속성 이용
59
CWnd ( 기능 1 ) Wrapping the Windows API
m_hWnd : regular API-level window handle 을 나타내기 위한 멤버변수 window handle을 인자로 갖는 거의 모든 API를 멤버 함수로 가진다. 예) AFXWIN2.INL : API 의 호출 API HWND hWnd; ShowWindow(hWnd,SW_SHOWNORMAL); MFC CWnd * pWnd; pWnd->ShowWindow(SW_SHOWNORMAL); 첫번째 래핑기능 예) API 와 MFC 의 다른 구현 방법(MFC는 인스턴스를 만들어(클레스 오브젝트) 그 맴버함수인 ShowWindow를 호출한다. NCmdShow 값을 가져와서 API 함수를 리턴한다. :: 윈도우 핸들과 MFC 오브젝트와의 관계를 어떻게 정리할 것인가?? 아주 중요한 문제임……….. ASSERT : 이 조건이 맞나 틀리나를 항상 확인한다 : 습관을 들이자… _AFXWIN_INLINE BOOL CWnd::ShowWindow(int nCmdShow) { ASSERT(::IsWindow(m_hWnd)); return ::ShowWindow(m_hWnd, nCmdShow); }
60
CWnd ( 기능 2 ) Higher-level MFC-related functionality
CObject -> CCmdTarget -> CWnd CObject derivation Dynamic run-time information Serialization CCmdTarget derivation MFC’s message-routing scheme Default message handling 제공 두번째 기능 상속 : 윗단계의 특징들을 활용할 수 있다. 메시지 라우팅이 가능하다. 디폴트 핸들링 가능하다.
61
예 : 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); } 전 차트 3번의 예 CObject -> CCmdTarget -> CWnd-> CMyWnd 만약 CMyWnd에서 작성하지 않았을 경우 부모 클레스의 함수를 호출하게 된다. _afxThreadState.GetData(); 메시지 프록에서 꺼내온 마지막 메시지가 저장되어 있다.
62
Turning Window Handles into Window Objects
Application MFC object High Level CHandleMap class Windows OS Window handle Low Level MFC : 반드시 오브젝트단위로 형성된다.(윗그림) -- 만약 HWND가 있고, 대응되는 오브젝트(CWnd *)가 있는데 문제는 MFC어플리케이션은 오브젝트 단위로 되어 있고 -- OS 가 어떠한 정보를 넘길때는 handle단위로 되어 있다. -- 따라서 MFC에서는 핸들을 보고 이것을 담당하는 오브젝트를 호출해 줘야 한다. 따라서 초기에 이러한 윈도우핸들이 있을때 이것을 담당하는 오브젝트를 만들고 정보를 어딘가에 담고 있어야 한다. 그게 뭐냐 하면 MAP으로 만들어 놓았다.(array 나 linkedlist가 아니다.) CHandleMap class를 만들고 여기에 insert한다.(어딘가에 글로벌하게 만들어 놓았다.) API :Window handle로 Windows 호출하였다.(아랫그림) 뒷 차트에 정리되어있다.
63
계층적 관계 High level : MFC object Low level : Window handle
application 입장에서는 MFC object를 이용 Low level : Window handle Windows OS는 handle을 이용 MFC는 어떤 handle에 어떤 object가 연결되어 있는지를 알아야 함 CHandleMap class를 이용하여 구현
64
CHandleMap AFXSTAT_.H Mapping window handle to MFC object
Handle이 주어지면 해당 object를 찾음 Windows OS가 callback 함수를 호출할 때, window handle을 parameter로 호출 그러나, MFC는 해당 CWnd- 파생 클래스 객체를 가지고 작업 CHandleMap을 알아보자… 목적은 : 핸들을 -> MFC(오브젝트로)…… Key(핸들) 가 무었이면 Value(오브젝트)가 무었이다.
65
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() 호출 내용을 보자 두개의 중요한 맴버변수가 있다. 두곳으로 나누어서 맵핑정보를 관리한다. M_permanantmat : 영구적인 맵 객체가 생성되서 종료될때 까지 정보를 보관 M_temporaryMap : 임시 맵 순간적으로 가지고 있다가 적절할때 버린다. 만약 두개의 쓰레드가 있고 비활성화쓰레드에서 활성화쓰레드의 GetActiveWindow할때 임시적으로 활성화되어있는 쓰레드의 핸들과 오브젝트가 저장된다. 처리할 메시지가 없을때 삭제된다. 예가 뒤에뒤에장에 있다.
66
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 멤버 변수 )
67
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; } 리턴되는 값은 오브젝트 : GetActiveWindow라는 API함수를 호출해서 Handle을 return한다. 이 핸들에 대응되는 object를 리턴하려고FromHandle을 호출한다. FromHandel에서는 현재의 돌아가는 시점의 맵핑정보(두가지가 있지? 영구적/임시적인맵) 를 가지고 있고 이를 가져온다. 결국은 Chandle맵의 영구맵의 정보가 온다.
68
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 처음으로 lookup 한다. 1순위로 permanent 맵에서 찾고, 2번째 임시맵에서 찾는다. 찾아 지면 리턴한다. 탐색의 기법 : 어디 부터 찾을 것인가?? 원칙은 확률이 높은 곳에서 부터 찾는다. 따라서 영구부터 찾게 된다.
69
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 HANDLE* ph = (HANDLE*)((BYTE*)pTemp + m_nOffset); ph[0] = h; if (m_nHandles == 2) ph[1] = h; return pTemp; 만약 없다면 임시맵에 오브젝트를 하나 만들고 정보를 임시맵에 삽입하고 리턴한다. (CreateObject() : 를 통해서 만드네 Dynamic creation하게 만든다.)
70
Other Mapping AFX_MODULE_THREAD_STATE m_pmapHWND
window handles CWnd objects m_pmapHMENU menu handles CMenu objects m_pmapHDC device context handles CDC objects m_pmapHGDIOBJ GDI object handles CGDIObjects m_pmapHIMAGELIST image list handles CImageList objects 여러가지 핸들이 있다. 이러한 정보가 MODULE_THREAD 에 있다. (현재 모듈에 대한 정보를 담아두는 곳)
71
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에서 정보 제거 삭제 되고 호출될때 호출되는 함수 핸들로 오브젝트는 찾는데 그 역은 어케 하나?? 오브젝트안에 맴버변수로 되어 있기 때문에 더 쉽다.
72
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; }
73
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;
74
주의 접근 불가 Multiple threads Thread 1 Thread 2 Window create
Permanent(영구) , temporary map(임시) 모두 thread 단위로 저장 전달할 경우 object 대신 HANDLE 을 보내는 것이 바람직 Thread 1 Thread 2 Window create Window create 흔하게 실수하는게… 디버깅할때 쓰레딩이 다르면 NULL 값을 리턴한다. 왜냐 하면 쓰레드 단위로 저장되기 때문에 따라서 바로 핸들을 넘기거나 맴버변수로 저장한다. 접근 불가
75
Find WinMain() APPCORE.CPP APPMODULE.CPP (여기에 정의) CWinApp 생성자 호출
_tWinMain() extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } WinMain을 찾자…
76
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()) 이 전에 Cwin_app 생성자가 호출된다. 1, WinMain 에서 AfxWinmain호출한다. 1.1 AfxWinMain 여기서 틀이 돌아갈 수 있도록 준비한다. 메인스레드가 동작할 수 있는 순서를 정하고 실행된다. - 글로별 변수값이 리턴된다.(2가지지) 글로벌 변수에 대한 할당을 먼저 한다. 생성자 함수 호출되고 생성되고 WinMain으로 들어온다는 말. 1.1.1 AfxWininit : 초기화 작업이 이루어진다. 1.1.2 CWinApp::InitApplication : 1.1.3 CWinApp::InitInstance() : 여기서 초기화하고 생성하는게 확인되고 1.1.4 CWinApp::Run() : 여기서 매시지 루프를 확인한다. 담부터 하나씩 확인해 보자…
77
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;
78
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; setErrorMode 현재의 인스턴스 핸들 저장 : AfxGetModuleState(); 어따가 ModuleState에다가(영구적)
79
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; 그리고 글로벌 오브젝트를 가져와서 : AfxGetApp() 맴버변수로 Setting한다. 이것을 가지고 작업을 할 것이다.
80
Find WinMain() (cont’d)
InitApplication() application-specific, but do nothing InitInstance() instance-specific, virtual Run()
81
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); 모듈스테이트와 쓰레드의 초기화 작업
82
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 } CWinApp에 들어있는 맴버변수의 초기화 작업이 생성자 함수에서 하게 된다.
83
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; InitApplication 에 클래스 등록과 관련된 작업은 없다. 메시지 핸들러에 대한 것이 남아있다.
84
InitInstance() BOOL CWinApp::InitInstance() { return TRUE; }
85
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 } 이 함수는 메시지루프를 만드는 함수 무한 루프를 돌고 두 단계로 구성되어 있다. 첫 단계 : 매시지가 없을때 이 것을 돈다.-> 메시지가 있으면 빠져나온다. idle procesing : idleCount값을 통해 계속도는건 아니고 messagepump로도 잠깐 같다오곤한다.)
86
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 두번째 단계: PeekMessage 해서 메시지가 무한정 돈다. 펌프메시지 : 라우팅의 시작점…… 이게 종료 : WM_QUIT일때 종료된다. ASSERT(FALSE); // not reachable : 반드시 이 전 while문에서 return이 이루어져야 한다는 뜻
87
MFC state information 프로그램 종료까지 유지되는 정보 AFX_MODULE_STATE class
AFXSTAT_.H 보유하는 정보 Main window handles Resource, module handles Memory allocation tracking ODBC support, OLE support, exception handling 모듈 정보와 쓰레드상태 정보가 중요하고 필요하다. 현재 쓰고 있는 모듈에 대한 핸들값과, 메모리얼로케이션 정보값을 가지고 있다.
88
Hidden Cool Stuff 2 issues window class의 등록
Windows hook와 MFC window의 연결 클래스 등록과 관련된 작업은 없다. 메시지 핸들러에 대한 것이 남아있다.
89
Registering Window Classes
Windows application OS 에 적어도 하나 이상의 window class를 등록해야만 한다. window class window의 기본적인 성질 정의 appearance ( via some flag ) behavior ( via a callback function ) 윈도우 클래스 두가지 역할을 한다. : 윈도우의 모습을 정의하고, 메시지가 발생하면 어떻게 처리할 것인가를 정의한다.
90
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 MFC 가 내부적으로 등록한다는 것을 예상할수 있다. 4가지의 class 가 등록이 되는데 언제 등록이 되는걸까?
91
Registering Window Classes (cont’d)
WNDCLASSes and MFC Style 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 HbrBackground background color LpszMenuName not used ( NULL ) LpszClassName class name
92
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 Icon AFX_IDI_STD_MDIFRAME COLOR_WINDOW background color
93
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 Gray background color (COLOR_BTNFACE)
94
Registering Window Classes (cont’d)
AfxDeferRegisterClass() AFXIMPL.H AfxEndDeferRegisterClass() WINCORE.CPP #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass) BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister); 언제 레지스터클레스 함수를 호출하느냐?? AfxDeferRegisterClass() AFXIMPL.H AfxEndDeferRegisterClass() WINCORE.CPP 두 함수 모두 동일한 형태로 정의되어 있다.
95
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; Doc/View 구조에서 View를 연결할때 Create를 한다. 이때 CreateEx가 호출된다. 윈도우가 CreateEX가 호출되면 클레스 이름을 받아 온다.NULL이겠지? 그리고 preCreateWindow 를 호출한다. 윈도우를 생성하기 이전에 호출되지? 클레스 이름이 NULL이라면은(최초의 CreateWind)이기 때문에 AfxDeferRegisterClass함수를 호출하게된다.) 그 다음에 CreateWindowEx 를 통해서 여기서 윈도우가 생성된다. 만약 다음 또 윈도우를 생성하게 되면 위의 두단계가 빠지고 CreateWindowEx로 들어가겠지? 왜냐하면 이럴때는 NULL값이 아니기 때문이지…… ASSERT : 디버깅모드에서만 작동한다. VERIFY : 어떠한 모드에서도 작동된다.
96
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;
97
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;
98
MFC’s Message Pump CWinApp::Run() 이 함수가 실행되면서 실제 메시지 펌프가 동작하게 됨
이는 CWinApp가 CWinThread로 부터 상속받기 때문에 가능한 것임 Thread 종류 Worker thread : 일반적으로 말하는 thread Interface thread : Message pump 이 함수가 호출되면 메시지 펌프가 동작한다. CWinApp 는 Thread이다.(Interface Thread의 일종이다.) 메시지 핸들링에 대해서만 남아있지?? 이것은 3장에서 다루어진다.
99
MFC’s GDI 지원 GDI (Graphical Device Interface) DC (Device Context)
Windows OS상의 풍부한 그리기 작업 장치 독립적 지원 : GDI + Device driver DC (Device Context) GDI의 핵심적인 개념 GDI 함수의 parameter 스크린, 프린터 등을 표시 구조체 : 객체 + 속성 + 모드
100
Device Contexts CDC CPaintDC painting 작업이 발생할 때
BeginPaint() ~ EndPaint() CWindowDC 전체 스크린을 표시 (client area+frame) GetWindowDC() ~ ReleaseDC() CClientDC Client area를 표시 GetClientDC() ~ ReleaseDC() CMetaFileDC 메타파일에 대한 그리기 작업 생성자 ~ Create() ~ DeleteMetaFile()
101
Graphic Objects CGdiObject : Base class Win32 API의 함수들을 wrapping CPen
CBrush CFont CBitmap CPalette CRgn Win32 API의 함수들을 wrapping
102
예제 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); } 왜 MFC가 Device context를 이용해 독립적으로 할려 하느냐? WM_PAINT(모니터에서 발생) Cview::OnPaint()에서 안만들고 OnDraw에서 처리한다. WM_PRINT OnView::OnPaint OnDraw를 호출한다. WM_PAINTPREVIEW OnView::OnPreview OnDraw 다 OnDraw로 가네??? OnDraw에서 중요한 것은 DC를 얻어오는것이쥐.. 이게 paint에서 왔느냐? Printf에서 왔느냐 Preview에서 왔느냐는 모른다.(중요치 않다.) 단지 얻어온 DC만 안다. 이를통해 프린트나 화면출력인가가 동일한 곳에서 적용될 수 있다.
103
Chapter 03 MFC에서의 Message 처리
104
Contents Introduction Window messages Message mapping
MFC가 message map을 이용하는 방법 Message loop의 hooking
105
Introduction Message handling C/SDK Switch/case 문을 통해 구현 MFC
Callback function은 어떻게 정의되는가
106
Basic Components MFC의 message handling 구조 MFC message maps에 대한 의문
CCmdTarget class Message maps MFC message maps에 대한 의문 어떻게 switch/case문을 대체하는가 어떤 data structure로 정의되는가 어떻게 message map이 작동하는가 어떻게 message가 연결되는가 메시지 처리 구조는 두가지로 요약 : CCmdTarget class, Message maps 이게 어케 switch/case 문을 대치하는가??
107
Window Messages 3개 부분으로 구성됨 Unsigned integer 실제 메시지를 나타냄 WPARAM
Word(32bits)크기의 parameter 메시지에 따른 특정 데이타 LPARAM 4byte parameter MSG HWND Message “winuser.h”에 정의되어 있어서 이걸루 어떤 메시지가 발생했는지 확인 가능 wParam lParam 이 세가지를 통해 메시지를 어케 할 것인가를 판단 Time pt
108
Window Messages (cont’d)
핸들링 해야 할 메시지의 분류 Windows message WM_ 로 시작하는 메시지들 (WM_COMMAND 제외) 일반적으로 window, view 등에서 핸들링 Control notification WM_COMMAND 중 notify, WM_NOTIFY Control, 자식 window 가 부모 윈도우에게 전달하는 메시지들 예) EN_CHANGED 를 포함한 WM_COMMAND 메시지 Command message 메뉴, 툴바, 엑셀레이터 등으로 인한 메시지
109
Window Messages (cont’d)
핸들링 클래스 Windows message, control notification CWnd 로부터 파생된 클래스들 : HWND 를 포함 Command message 다양한 종류 : CCmdTarget 로부터 파생된 클래스들 예) open 명령 핸들링 : application
110
UI Objects and Command IDs
111
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); Run( ) 을 통해 메시지 루프를 구현한다.
112
Message Handling in C/SDK (cont’d)
WNDCLASS ws; … ws.lpfnWndProc = WndProc; RegisterClass(ws); LRESULT WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: break; case WM_COMMAND: switch (wParam) { case IDM_ABOUT: } MFC 에는 출발점이 있고 switch/case 문이 없지? 이 걸 어케 대치할 것인가??
113
Message Handling in MFC
방법 1 Polymorphism : 가상 멤버 함수 이용 심각한 메모리 낭비 방법 2 Message maps MFC 의 특징 메모리 낭비 : 가상함수를 선언하면 하위 클레스에 어떤 영향을 미치냐 하면.. 각 클레스별로 가상함수 테이블 (상위 클레스에서 선언한 가상함수의 주소값을 저장 만약 하위 크레스에서 구현이 안되어 있으면 주소는 상위클레스를 저장하고, 구현이 되어 있으면 자신의 클레스 주소를 저장한다.) 이 만들어진다. 결론은 구현한것만 가지고 있고, 나머지는 버린다.(메모리의 최소화를 위해서)… 하지만 가상함수를 완전히 대치할 순 없다. 왜냐하면 하위 클레스에서 구현을 안하면 가상함수테이블을 만들지 않는다. 그러면 하위클레스에서 메시지가 발생했을때 상위클레스로 전해줘야 하는데 이때 라우팅이 필요하다. 근데 너무 자주일어나면 시간적으로 손해가 난다. 이를 없애기 위해 노력을 한다. 메시지맵의 장점은….. 장점은 정보유지를 최소화한다. 단점은 쓸데없는 라우팅과정이 필요하다.
114
Message Mapping Internals
두 부분으로 구성 CCmdTarget Window message나 command를 받기 위해 반드시 상속받아야 하는 class Message map Window message와 이를 처리하는 class member function을 연관시켜주는 mechanism Message map 최소한의 정보를 유지하고 있으면서 제대로 길을 찾아갈수 있게(라우팅) 한다.
115
CCmdTarget Class 메시지의 처리 CCmdTarget의 파생 클래스 예 CWnd class CDocument
CWinApp
116
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) }; 메시지 맵은 위와 같은 구조체로 만들어진다. AFX_MSGMAP_ENTRY 어떤 메시지인가? 3)4)5) 부가적인 정보 6) function이 작성되어 있다. 메시지를 처리하는 함수 1)번의 메시지는 6)번의 function에서 처리해라
117
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; }; 자기자신을 가리키는 포인트(연결리스트의 값) 상위클레스를 가리키는 포인트(배열에 대한 주소값) 제일 먼저 ipEntries 포인터를 통해 자신의 AFX_MSGMAP_ENTRY를 찾아 보고 없으면 pBaseMap을 통해서 상위클레스로 이동하고, 다시 상위클레스의 ipEntries 포인터를 통해 AFX_MSGMAP_ENTRY를 찾아본다.
118
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; \ 앞의 코드들이 이번장에서 처럼 MACRO로 구현되어 있다. 상당히 사용하기가 편하다. 배열 정의 메시지에 대한 배열이겠쥐…. messageMap 구조체를 통해 포인터 두개 저장 GetMessageMap : 포인터 리턴 뭔 포인터냐 하면 위의 구조첼르 통해 저장된 포인터를 리턴하겠쥐…
119
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 } \ }; \ BEGIN_MESSAGE_MAP : 인자로 두개의 인자가 들어간다. 자기자신의 클레스 이름, 베이스클레스 이름 GetMessageMap : 구현 현재 주소값을 리턴 messageMap 변수 선언 : 두개의 포인터를이용해서 부모 클레스의 메시지맵의 시작주소를 연결하고, 두번째는 자기자신의 배열의 시작주소를 연결한다. 그 다음 ON_COMMAND(ID_STRING_CENTER, OnStringCenter) ON_WM_LBUTTONDBLCLK( ) 을 통해서 messagemap에 넣어주고… 마지막으로 빈 메크로를 넣어준다.(END_MESSAGE_MAP()
120
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[]; static const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const; }; afx_msg void OnLButtonDblClk(UINT nFlags, Cpoint point); 이러한 핸들러 함수가 구현이된다. 아래와 같은 일들이 일어난다.
121
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[] = { (BEGIN_MESSAGE_MAP) ON_COMMAND(ID_STRING_CENTER, OnStringCenter) ON_WM_LBUTTONDBLCLK() {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } (END_MESSAGE_MAP) }; 위의 것이 번역이 되면 아래와 같이 구현된다.
122
Message Map Entry Macro
AFXMSG_.H #define ON_COMMAND(id, memberFxn) \ { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, \ AfxSig_vv, (AFX_PMSG)&memberFxn }, userdefine(사용자가 지정한 이름 사용) #define ON_WM_LBUTTONDBLCLK() \ { WM_LBUTTONDBLCLK, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW) \ (void (AFX_MSG_CALL CWnd::*) \ (UINT, CPoint))&OnLButtonDblClk }, 만들어져 있는 이름을 사용 #define 문 :
123
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
124
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);
125
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( )
126
Handlers for Message-Map Ranges
... void CMyDialog::OnButtonClicked( UINT nID ) { int nButton = nID - IDC_BUTTON1; ASSERT( nButton >= 0 && nButton < 10 ); // ... }
127
MFC의 Message Map이용 MFC-based program 두 종류의 메시지를 처리
regular window messages WM_MOUSEMOVE, … commands WM_COMMAND Message-mapping architecture의 이해 두 종류 메시지를 각각 추적
128
How to be wired 16-bit version 32-bit version AfxWndProc()
Message handling procedure로 등록된 함수 32-bit version DefWindowProc() 가 message handler로 등록 Message hook방법을 써서 결국에는 AfxWndProc()가 message를 처리하게 함 Hooking function _AfxCbtFilterHook(), _AfxStandardSubclass() 3D Control을 지원하기 위함 AfxWndProc() : MFC 의 시작점을 나타내는 함수 .
129
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; AfxEndDeferRegisterClass : 래지스터클레스로 등록하는 함수 ….
130
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; 실제 윈도우가 Create 되고 등록이 되면… AfxHookWindowCreate 호출하고,
131
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; } 서브클래싱하여서 afxWndProc 으로 바꿔어준다.
132
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); } AfxWndProc : 핸들링 시작 첫번째 특별한 메시지처리 OS로부터 핸들을 넘겨받으면 이걸 object로 바꾸어준다.(FromHandlePermanent) 1.1 AfxCallWndProc 함수 호출 1.2 CWnd::WndProc 함수 호출 1.2.1 CWnd::OnWndMsg
133
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()를 호출
134
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; } AfxCallWndProc : 1. 쓰레드state 리턴받고,,,,메시지를 가져온다. 2. WindowProc을 호출한다.
135
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; } WindowProc
136
Message Handling (cont’d)
CWnd::OnWndMsg()(WINCORE.CPP) 특별히 처리하는 message WM_COMMAND, WM_NOTIFY WM_ACTIVATE, WM_SETCURSOR 기타 다른 message들은 message map을 이용하여 해당 message handler를 호출
137
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 가 분기가 된다. Command message 일 경우에는 OnCommand를 호출한다. WM_COMMAND : 는 다른함수를 호출한다. 만약 이러한 메시지를 다 통과하면.. 다음 라인으로 간다.
138
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(); } Regular window message 예)WM_MOUSEMOVE 레귤러윈도우메시지가 들어왔다.(예를 들어 mouse_move) 그러면 pMessageMap 을 통해서 메시지맵을 가져오고(여기에는 자신의 메시지맵과 상위클레스의 메시지맵 포인터를 가지고 있쥐) Cash를 통해 최근의 메시지는 모아놓는다. 왜냐하면 자주 발생하는 메시지를 바로 처리하기 위해서… 그래서 여기를 뒤져보고… 그리고 여기에 저장되어 있는 메시지이면 처리를 하고, Else 아니면 cash를 업데이트하고 for문을 돌려서 AfxFindMessageEntry를 통해 Message가 있는지 확인한다. 여기서 pMessagemap 은 가져온 메시지 맵이다.
139
Message Handling Trace
Regular window message WM_MOUSEMOVE Command message 메뉴나 control이 보내는 message WM_COMMAND 서로 다른 방식으로 처리 CWnd::OnWndMsg()참고
140
Handling WM_COMMAND 가정 CWnd::OnWndMsg()함수에서 CWnd::OnCommand()
Main frame의 메뉴 명령 수행 CWnd::OnWndMsg()함수에서 CWnd::OnCommand()함수 호출 CWnd::OnCommand() Virtual function(Framework이 적절한 version의 함수를 호출함) CFrameWnd::OnCommand() On-line help에 관한 것이면 해당 기능 수행 아니면 CWnd::OnCommand()함수 호출
141
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); CFRAMEWND::OnCommand() WM_COMMAND 메시지가 들어오면 호출되는 함수 궁극적으로는 CWnd::OnCommand() 를 호출한다.
142
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); NULL 이 아니라는 이야기는 Notification이라는 야기… 일반적인 command message일경우에는 OnCmdMsg를 호출한다. 뒷장으로…
143
Handling WM_COMMAND (cont’d)
CWnd::OnCommand()(WINCORE.CPP) LPARAM을 조사 만약 control이 보낸 message이면 control에게 다시 message를 reflect한 후 return 아니면 CWnd::OnCmdMsg()(virtual function)함수 호출
144
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)) // last but not least, pump through app CWinApp* pApp = AfxGetApp(); if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return FALSE; } 여기서 처리가 이루어진다. GetActiveView : 현제 활성화되어 있는 View 의 포인터를 가져온다. OnCmdMsg에서 처리가 되면 종료가 되고 만약 처리가 안되면 1) CWnd::OnCmdMsg를 호출한다. Frame을 통해서 호출한다. 만약 또 처리가 안되면(그럴 경우는 잘 없지만) AfsGetApp() 를 호출한다.
145
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; 1)CWnd::OnCmdMsg 를 호출한다. : 이때 현재 this 포인터는 현제 Active되있는 View CCmdTarget::OnCmdMsg를 호출한다. 성공하면 끝나고 아니면….. 2) Cdocument를 가지고….. 도큐먼트포인터를 가지고 함수를 참조한다. CCmdTarget::OnCmdMsg를 호출한다.
146
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 FALSE; }
147
Handling WM_COMMAND (cont’d)
CFrameWnd::OnCmdMsg()(WINFRM.CPP) 다음의 순서로 해당 OnCmdMsg()함수를 호출 Active view Active view’s document Main frame window Application
148
Handling WM_COMMAND (cont’d)
만약 active view에 해당 handler가 있다면 CView::OnCmdMsg()가 호출됨 CView::OnCmdMsg()(VIEWCORE.CPP) View에서 처리를 시도한 후 안되면 document에서 처리를 시도 View에서 처리 시도는 CWnd::OnCmdMsg()함수의 호출로 이루어짐 이 때 CWnd는 OnCmdMsg를 override하지 않기 때문에 CCmdTarget::OnCmdMsg를 호출하는 결과가 됨
149
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
150
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 등등
151
Handling WM_COMMAND (cont’d)
Frame window일 때의 처리과정 정리 1. AfxWndProc() 1.1 AfxCallWndProc() 1.2CWnd::WindowProc() 1.2. 1CWnd::OnWndMsg() CFrameWnd::OnCommand() CWnd::OnCommand() CFrameWnd::OnCmdMsg() CCmdTarget::OnCmdMsg() _AfxDispatchCmdMsg() CMainFram::On~() 1. AfxWndProc() 1.1 AfxCallWndProc() 1.2CWnd::WindowProc() 1.2. 1CWnd::OnWndMsg() 1.2.1 CFrameWnd::OnCommand() 1.2.2 CWnd::OnCommand() 1.2.3 CFrameWnd::OnCmdMsg() 1) CVIew::OnCmdMsg CCmdTarget::OnCmdmsg 2) Cdocument::OnCmdMsg CCmdTarget::OnCmdmsg 3) Cframe::OnCmdMsg CCmdTarget::OnCmdmsg 4) CWinAPp::OnCmdMsg CCmdTarget::OnCmdmsg 결국은 CCmdMsg 에서 처리가 되는 데 누가 주체냐가 문제가 되는 거쥐.???
152
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~()
153
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~()
154
Handling WM_COMMAND (cont’d)
App일 때의 처리과정 정리 AfxWndProc() AfxCallWndProc() CWnd::WindowProc() CWnd::OnWndMsg() CFrameWnd::OnCommand() CCmdTarget::OnCmdMsg() _AfxDispatchCmdMsg() CMyApp::On~()
155
Handling WM_COMMAND (cont’d)
Dialog Box일 때의 처리과정 정리 AfxWndProc() AfxCallWndProc() CWnd::WindowProc() CWnd::OnWndMsg() CDialog::OnCmdMsg() CCmdTarget::OnCmdMsg() _AfxDispatchCmdMsg() CAboutDlg::On~()
156
예 – Clear All Handling 과정 (MDI application)
Main frame window가 message를 받는다 현재 활성화된 MDI child window가 처리 할 기회를 얻는다 Child window가 먼저 view에게 처리 기회를 준다. 실패하여 view가 연결된 document가 처리를 시도한다 Document가 메시지 핸들러를 찾는 데 성공한다.
157
Handling Regular Window Message
Command message일 때와 처음의 과정은 비슷 AfxWndProc() AfxCallWndProc() WindowProc() OnWndMsg() 이 함수 안에서 message handler를 찾기 위해AfxFindMessageEntry()를 호출
158
Handling Regular Window Message (cont’d)
AfxFindMessageEntry() 두 버전이 있음. Assembly language : intel-based machine C language : otherwise Message map에서 해당 핸들러가 있는지 검색 END_MESSAGE_MAP에 의해 생성된 table의 끝까지 검색
159
Handling Regular Window Message (cont’d)
Command message와의 차이점 Command message는 handler를 찾기 위해서 여러 장소를 옮겨다님 Regular message는 OnWndMsg()에서 handler를 찾으면 바로 해당 handler를 호출하고, 아니면 DefWindowProc()를 이용
160
Handling Regular Window Message (cont’d)
예) View에서 WM_SIZE message처리 과정 AfxWndProc() AfxCallWndProc() CWnd::WindowProc() CWnd::OnWndMsg() CSdiappView::OnSize()
161
Other Kinds of Messages
지금까지 살펴 본 메시지 종류 WM_COMMAND Window messages(WM_SIZE, WM_MOVE, …) 그 이외의 메시지 종류 WM_NOTIFY WM_ACTIVATE WM_SETCURSOR
162
Other Kinds of Messages (cont’d)
WM_NOTIFY Control이 보내는 message 항상 notify message 반면, WM_COMMAND command이거나 notify message
163
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; // control that sent notification UINT idFrom; // ID of control UINT code; // notification code
164
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), ¬ify, NULL); } 컨트롤이 부모에게 보내는 메시지… 컨트롤 자기 자신이 컨트롤 할 수 있도록 해줌…. 메시지가 자식에게 가면 OnNotify를 통해서 부모윈도우의 Qeue에 들어간다. 그러면 메시지는 부모에게 있는데 자식에게 보내서 스스로 처리를 할 수 있게 하고,(이 부분이 ReflectLastMsg 이다.) 이게 안되면 부모에 의해서 메시지를 처리한다.
165
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);
166
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); }
167
Other Kinds of Messages (cont’d)
Message reflection OnWndMsg()에서 OnNotify()를 호출 OnNofity()는 다시 OnChildNotify()를 호출하여 control로 message를 다시 보냄 결국에는 control이 parent에 의존하지 않고 자신의 일을 처리하게 함
168
Other Kinds of Messages (cont’d)
WM_ACTIVATE OnWndMsg()함수에서 _AfxHandleActivate()를 호출 WM_SETCURSOR OnWndMsg()함수에서 _AfxHandleSetCursor()함수 호출
169
Speed Message-map Matching
방법 최근에 처리 된 메시지를 캐쉬에 저장 같은 메시지를 다시 처리할 가능성이 높음 장점 Unhandled message 빠르고 효과적으로 처리
170
Message Loop Hooking Message loop hooking
Message가 해당 handler에 의해 처리되기 전에 어떠한 작업을 하고 싶을때 PreTranslateMessage() CWinApp::PreTranslateMessage() CWinApp::Run()함수는 message가 message pump에 의해 처리되기 전에 위 함수를 호출 TRUE를 return하면 message pump는 해당 message에 관해서는 처리를 하지 않음 CWnd::PreTranslateMessage()
171
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 }
172
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
173
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; if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) 리턴 결과가 참이면 전체적으로 거짓이 되서 나간다. 이렇기 땜시 m_msgCur 를 전처리할수 있다.
174
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)) // 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 TRUE 가 되면 라우팅처리가 안된다. FALSE면 정상적인 라우팅이 진행된다.
175
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 For에서 하는 일은 처리하고자 하는 윈도우 핸들을 가지고 시작하고 거기에 대응되는 오브젝트를 찾고 그 오브젝트의 PreTranslateMessage를 찾고 되면 TRUE를 리턴해 라우팅이 안된다.
Similar presentations