MFC airensoar snu ac kr Introduction v v
- Slides: 177
고급 MFC 권영근 airen@soar. snu. ac. kr
Introduction v 교재 Ø Ø v “MFC Internals”, George Shepherd, Addison-Wesley, 1996 MSDN Library, http: //msdn. microsoft. com/library/ 선수 강좌 Ø Ø C++ Win 32 API
강의 계획 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 v v v Application Frameworks 정의 MFC의 역사 MFC의 설계 목적 MFC의 구성 MFC code basics
Application Frameworks v Framework Ø v Application Framework Ø v 특정 영역의 작업을 도와 주는 class 집합 특정 OS의 application 개발 작업을 도와 주는 class 집 합 MFC (Microsoft Foundation Classes) Ø Windows용 application 개발 작업을 도와 주는 framework
MFC의 역사 v 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) v 1993년 Ø MFC version 2. 0 ü ü ü Visual C++ version 1. 0과 Windows NT와 함께 나옴 100개가 넘는 class포함 새로 포함된 내용 Ø Ø 새로운 application architecture 새로운 high-level abstractions
MFC의 역사 (cont’d) v 1993년 Ø MFC version 2. 5 ü ü v Visual C++ version 1. 5와 함께 나옴 OLE 2. 0과 ODBC 지원이 추가됨 1994년 Ø MFC version 3. 0 ü Thread에 대한 기능이 보강됨
MFC의 역사 (cont’d) v 1995년 Ø MFC version 3. 1 ü 추가된 기능 Ø Ø Ø Simple Messaging Application Programming Interface(MAPI) Win. Sock MFC version 4. 0 ü ü Visual C++ version 4. 0과 함께 나옴 개발환경의 발전, 재사용성의 향상
MFC의 역사 (cont’d) v 1997년 Ø MFC version 4. 2 ü ü v 1998년 Ø MFC version 6. 0 ü ü v 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) v 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 Microsoft C/C++ version 7. 0
MFC 설계 목적 v AFX 그룹의 설계 목적 Ø Ø Ø Real-World Application Simplifying the Windows API Using the existing knowledge of Windows Foundation for large-scale applications Small and fast framework
MFC의 구성 v MFC class 의 분류 Ø Ø General-purpose classes Windows API classes Application framework classes High-level abstractions
MFC의 구성(cont’d) v General-purpose Class Ø Ø 프로그램의 일반적 용도를 위한 class CObject class ü ü ü MFC의 최상위 class run-time type information, serialization, diagnostic function, support for dynamic creation 기능 제공 CArchive, CDump. Context, CRuntime. Class 등과 연계되어 작용
MFC의 구성(cont’d) Ø Exception-handling class ü ü ü memory, I/O error 발생 시 처리 CException : base class CArchive. Exception, CFile. Exception CMemory. Exception, CResource. Exception CNot. Supported. Exception, CUser. Exception COle. Exception, CDBException
MFC의 구성(cont’d) Ø Collection class ü ü ü Array : CByte. Array, CWord. Array, CDWord. Array, CPtr. Array, COb. Array, CString. Array, CUnit. Array Linked list : COb. List, CPtr. List, CString. List Map : CMap. Ptr. To. Word, CMap. Ptr. To. Ptr, CMap. String. To. Ob, CMap. String. To. Ptr, CMap. String. To. String, CMap. Word. To. Ob, CMap. Word. To. Ptr
MFC의 구성(cont’d) Ø Dynamic string class ü ü Ø CString concatenation, comparison, assignment 등의 기본 연산 제공 File class ü ü CFile, CStdio. File, CMem. File 추상적으로 disk상의 파일 제어, 실제로는 memory상의 파일 제어
MFC의 구성(cont’d) Ø Time class ü Ø CTime, CTime. San 기타 ü CPoint, CSize, CRect : Windows structure
MFC의 구성(cont’d) v Windows API class Ø Application 관련 class ü ü CCmd. Target : message 처리 CCmd. UI : user interface의 update CWin. Thread : MFC program의 실행 thread를 의미, 즉 program의 main을 포함 CWin. App : CWin. Thread의 파생 class으로서 standard windows application을 표현
MFC의 구성(cont’d) Ø Window 관련 class ü ü CWnd : CCmd. Target의 파생 class이므로 message를 handle. 윈도우를 다루는 API 포함. CFram. Wnd, CMDIFrame. Wnd : main frame window로서 message를 받는 첫 윈도우 CDialog, 공통다이어로그박스(CFile. Dialog, CColor. Dialog, CFont. Dialog, CPrint. Dialog, CFind. Replace. Dialog) CData. Exchange : DDX/DDV
MFC의 구성(cont’d) ü ü ü Ø CProperty. Sheet, CProperty. Page Controls : CButton, CEdit, … CMenu GDI 관련 class ü ü CDC, CPaint. DC, CWindow. DC, CClient. DC, CMeta. File. DC CPen, CBrush, CFont, . . .
MFC의 구성(cont’d) v Application framework class Ø Document/View Architecture ü ü ü Ø CDoc. Template, CSingle. Doc. Template, CMulti. Doc. Template : document와 view를 연결 CDocument : data를 관리 CView : data를 redering하여 보여 줌 Context-Sensitive Help
MFC의 구성(cont’d) v High-level abstraction Ø Enhanced Views ü ü Ø Splitter Window ü Ø CScroll. View, CForm. View CEdit. View, CList. View, CRich. Edit. View, CTree. View CSplitter. Wnd : dynamic, static Control Views ü CTool. Bar, CStatus. Bar, CDialog. Bar
MFC Code Basics v Class Declaration Subsections Ø MFC library 개발 팀의 코딩 규칙 ü ü Ø Not based public/protected/private Private 변수는 거의 사용하지 않는다. Header / source file ü ü ü // Constructors // Attributes // Operations // Overridables // Implementation
Example of Comments class CStdio. File : public CFile { DECLARE_DYNAMIC(CStdio. File) public: // Constructors CStdio. File(); . . . // Attributes FILE* m_p. Stream; // stdio FILE. . . // Operations virtual void Write. String(LPCTSTR lpsz); . . . virtual LPTSTR Read. String(LPTSTR lpsz, UINT n. Max); . . . // Implementation public: . . . };
MFC Comments v 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) v Variable Naming (common) Type Prefix Example Comment char c c. Dir. Separator BOOL b bls. Sending int n n. Variable. Cnt UINT n n. My. Unsigned WORD w w. List. ID LONG l l. Axis. Ratio DWORD dw dw. Packedmessage * (pointer) p p. Wnd FAR * lp lp. Wnd LPSTR handle lpsz h lpsz. File. Name h. Wnd Z indicates NULL terminated. callback lpfn. Hook. Proc Pointer to a function
MFC Code Basics (cont’d) v Variable Naming (MFC extensions) Class Prefix Example CRect rect. Scroll CPoint pt pt. Mouse. Click CSize sz sz. Rectangle CString str. Find CWnd Wnd. Control CWnd* p. Wnd. Dialog
MFC Code Basics (cont’d) v Symbol Naming Type Prefix Example Range Shared by multiple resources IDR_MAINFRAME 1 -0 x 6 FFF Dialog resource IDD_ABOUT 1 -0 x 6 FFF Dialog resource help context ID (for context-sensitive help) HIDD_HELP_ABOUT 0 x 2001 -0 x 26 FF Bitmap resource IDB_SMILEY 1 -0 x 6 FFF Cursor resource Icon resource IDC_HAND 1 -0 x 6 FFF
MFC Code Basics (cont’d) v Symbol Naming Type Prefix Example Range Menu or toolbar command ID_CIRCLE_TOOL 0 x 8000 -0 x. DFFF Command help context HID_CIRCLE_TOOL 0 x 1800 -0 x 1 DFF Message box prompt IDP_FATALERROR 8 -0 x. DFFF Message box help context HIDP_FATALERROR 0 x 3008 -0 x 3 DFF Control in dialog template IDC_COMBO 1 8 -0 x. DFFF String resource IDS_ERROR 12 1 -0 x 7 FFF
Chap. 2 Basic Windows Support
Contents v v v Introduction MFC versus C/SDK Basic Application Components Ø Ø Ø v v Find Win. Main() Hidden Cool Stuff Ø Ø Ø v CWin. App CWnd Window handles & Window objects Registering Window Classes MFC’s Windows Hooks MFC’s Message Pump MFC’s GDI Support
Introduction v MFC Ø Ø v 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 ü Win. Main, window class 등록, 메시지 루프, …
Issue v MFC가 어떻게 Windows application을 만드는가 Ø Ø The application itself Windows Message handling The Graphics Device Interface (GDI)
MFC vs. C/SDK v Motivation Ø 모든 Windows application은 다음 2개의 component를 포함한다. ü ü Ø C/SDK 개발 환경 ü ü Ø main application itself message를 핸들하는 하나 이상의 window copy & paste Time-consuming & inefficient C++/MFC 개발 환경 ü ü OOP 활용 : inheritance & encaptulation 필요한 부분만 변경
메시지 처리를 위한 작업들 v Set up a message handler and register it Ø Regiser. Class() v Windows가 application의 instance들을 추적 v application은 Windows에게 메시지를 요청(ask) 하고, 처리(dispatch)한다. v application이 종료될 때까지 위 작업을 반복한다.
Application의 준비 1. Win. Main() 함수 Ø Ø 프로그램의 시작점 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를 제공 v 최소한 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 h. Inst; /* current instance */ LRESULT CALLBACK Main. Wnd. Proc(HANDLE h. Wnd, UINT message, WPARAM w. Param, LPARAM l. Param) { switch(message) { case WM_LBUTTONDOWN: Message. Box(h. Wnd, “Left mouse button clicked”, NULL, MB_OK); break; case WM_DESTROY: Post. Quit. Message(0); break; default: /* Passes it on if unprocessed */ return (Def. Window. Proc(h. Wnd, message, w. Param, l. Param)); } return 0; }
BOOL Init. Application(HANDLE h. Instance){ WNDCLASS wc; wc. style = 0; /* Class style(s) */ wc. lpfn. Wnd. Proc = Main. Wnd. Proc; /* Message handler */ wc. cb. Cls. Extra = 0; /* No per-class extra data */ wc. cb. Wnd. Extra = 0; /* No per-window extra data */ wc. h. Instance = h. Instance; /* Application that owns the class*/ wc. h. Icon = Load. Icon(NULL, IDI_APPLICATION); wc. h. Cursor = Load. Cursor(NULL, IDC_ARROW); wc. hbr. Background = Get. Stock. Object(WHITE_BRUSH); wc. lpsz. Menu. Name = NULL; /* Name of menu */ wc. lpsz. Class. Name = “Minimal. WClass”; /* Name of window class */ return (Register. Class(&wc)); }
BOOL Init. Instance(HANDLE h. Instance, int n. Cmd. Show) { HWND h. Wnd; /* Main window handle */ h. Inst = h. Instance; // needed for loading resources // h. Wnd = Create. Window( “Minimal. WClass”, “Minimal”, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, NULL, h. Instance, 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 (!h. Wnd) return (FALSE); Show. Window(h. Wnd, n. Cmd. Show); Update. Window(h. Wnd); return (TRUE); }
int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPSTR lp. Cmd. Line, int n. Cmd. Show) { MSG msg; /* message */ if (!h. Prev. Instance) { if (!Init. Application(h. Instance)) return (FALSE); } /* First instance? */ /* Shared stuff */ /* cannot initialize */ if (!Init. Instance(h. Instance, n. Cmd. Show)) return (FALSE); while (Get. Message(&msg, NULL, 0, 0)) { Translate. Message(&msg); Dispatch. Message(&msg); } return(msg. w. Param); /*Returns the value from Post. Quit. Message*/ }
Source 2 : MFC #include <afxwin. h> // MFC 주요 클래스 선언 class CGeneric. App : public CWin. App { public: virtual BOOL Init. Instance(); }; class CGeneric. Window : public CFrame. Wnd { public: CGeneric. Window() { Create(NULL, “Generic”); } afx_msg void On. LButton. Down(UINT n. Flags, CPoint point); DECLARE_MESSAGE_MAP() };
BEGIN_MESSAGE_MAP(CGeneric. Window, CFrame. Wnd) ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() void CGeneric. Window: : On. LButton. Down(UINT n. Flags, CPoint point) { Message. Box(“Left mouse button pressed…”, NULL, MB_OK); } BOOL CGeneric. App: : Init. Instance() { m_p. Main. Wnd = new CGeneric. Window(); m_p. Main. Wnd->Show. Window(m_n. Cmd. Show); m_p. Main. Wnd->Update. Window(); return TRUE; } CGeneric. App;
C/SDK와 MFC의 비교 v Win. Main() Function Ø Ø v Window class 등록 Ø Ø v C/SDK : 존재 MFC : where? ( part of application framework ) C/SDK : 존재 MFC : where? Message loop Ø Ø C/SDK : 존재 MFC : where? ü Additional functions : idle processing, Pre. Translate. Message()
C/SDK와 MFC의 비교 (cont’d) v Instance initialization Ø Ø v C/SDK : 존재 MFC : 존재, 그러나 언제 실행? Message handling Ø Ø C/SDK : 존재 MFC : 존재, 그러나 어떻게 command-routing, message-dispatch? ü ü Member functions + Message map Afx. Wnd. Proc() : 공통 window procedure
Basic MFC Application Components v Windows application의 두 가지 components Ø Ø v Message pump Window procedure MFC에서의 구현 방법 Ø CWin. App ü ü Ø Application을 나타내는 class application-specific : 초기화, window 생성, 메시지 루프 CWnd ü ü Window를 나타내는 class window-specific : 메시지 핸들링
CWin. App (“Afx. Win. h”) CWin. App : public CWin. Thread { public: // Constructor CWin. App(LPCTSTR lpsz. App. Name = NULL); to EXE name // app name defaults // Attributes // Startup args (do not change) HINSTANCE m_h. Instance; HINSTANCE m_h. Prev. Instance; // not used LPTSTR m_lp. Cmd. Line; int m_n. Cmd. Show; // Running args (can be changed in Init. Instance) LPCTSTR m_psz. App. Name; // human readable name // (from constructor or AFX_IDS_APP_TITLE)
public: // set in constructor to override default LPCTSTR m_psz. Exe. Name; // executable name (no spaces) LPCTSTR m_psz. Help. File. Path; // default based on module path LPCTSTR m_psz. Profile. Name; // default based on app name // Overridables // hooks for your initialization code virtual BOOL Init. Application(); void Set. Current. Handles(); // overrides for implementation virtual BOOL Init. Instance(); virtual int Exit. Instance(); // return app exit code virtual int Run(); virtual BOOL On. Idle(LONG l. Count); // return TRUE if more idle processing virtual LRESULT Process. Wnd. Proc. Exception (CException* e, const MSG* p. Msg);
public: virtual ~CWin. App(); protected: //{{AFX_MSG(CWin. App) afx_msg void On. App. Exit(); afx_msg void On. Update. Recent. File. Menu(CCmd. UI* p. Cmd. UI); afx_msg BOOL On. Open. Recent. File(UINT n. ID); //}}AFX_MSG DECLARE_MESSAGE_MAP() };
CWin. App v v AFXWIN. h 멤버 변수와 멤버 함수 Ø Win. Main()에 전달된 command line parameter 를 관리 할 변수 ü ü m_h. Instance : the current instance handle m_h. Prev. Instance : the previous instance handle m_lp. Cmd. Line : the command line parameters m_n. Cmd. Show : the show window flag
CWin. App (cont’d) Ø Ø m_psz. App. Name : application name 관련 pointer ü ü ü Ø m_psz. Exe. Name : executable file name m_psz. Help. File. Path : the path to help file m_psz. Profile. Name : application profiles name 프로그램 실행 시 주어진 command line parameter 유 지 ü CCommand. Line. Info class 이용 (AFXWIN. H)
CWin. App (cont’d) Ø Ø Init. Instance() : instance-specific 초기화 Exit. Instance() : instance 종료 시 처리 Run() : message pump 수행 On. Idle() : Message queue가 비었을 때, Run 함수에 의해 호출
CWnd v AFXWIN. H v 2가지 기능 수행 Ø Wrapping the regular Windows API ü Ø 예: Create(), Show. Window(), … Higher-level MFC-related functionality ü 예: default message handling
CWnd ( 기능 1 ) v Wrapping the Windows API Ø Ø m_h. Wnd : regular API-level window handle 을 나타내 기 위한 멤버변수 window handle을 인자로 갖는 거의 모든 API를 멤버 함수로 가진다. ü 예) API HWND h. Wnd; Show. Window(h. Wnd, SW_SHOWNORMAL); MFC CWnd * p. Wnd; p. Wnd->Show. Window(SW_SHOWNORMAL); Ø AFXWIN 2. INL : API 의 호출 _AFXWIN_INLINE BOOL CWnd: : Show. Window(int n. Cmd. Show) { ASSERT(: : Is. Window(m_h. Wnd)); return : : Show. Window(m_h. Wnd, n. Cmd. Show); }
CWnd ( 기능 2 ) v Higher-level MFC-related functionality Ø Ø CObject -> CCmd. Target -> CWnd CObject derivation ü ü Ø CCmd. Target derivation ü Ø Dynamic run-time information Serialization MFC’s message-routing scheme Default message handling 제공
예 : default message handling _AFXWIN_INLINE void CWnd: : On. Activate(UINT, CWnd*, BOOL) { Default(); } LRESULT CWnd: : Default() { // call Def. Window. Proc with the last message _AFX_THREAD_STATE* p. Thread. State = _afx. Thread. State. Get. Data(); return Def. Window. Proc(p. Thread. State->m_last. Sent. Msg. message, p. Thread. State->m_last. Sent. Msg. w. Param, p. Thread. State->m_last. Sent. Msg. l. Param); }
Turning Window Handles into Window Objects High Level Application MFC object CHandle. Map class Low Level Windows OS Window handle
계층적 관계 v High level : MFC object Ø v Low level : Window handle Ø v v application 입장에서는 MFC object를 이용 Windows OS는 handle을 이용 MFC는 어떤 handle에 어떤 object가 연결되어 있 는지를 알아야 함 CHandle. Map class를 이용하여 구현
CHandle. Map v v AFXSTAT_. H Mapping window handle to MFC object Ø Ø Ø Handle이 주어지면 해당 object를 찾음 Windows OS가 callback 함수를 호출할 때, window handle을 parameter로 호출 그러나, MFC는 해당 CWnd- 파생 클래스 객체를 가지 고 작업
CHandle. Map (cont’d) v 멤버변수 Ø CMap. Ptr. To. Ptr m_permanant. Map ü ü Ø Permanent map 명시적으로 객체가 생성될 때 정보가 추가됨(CWnd’s: : Create()) 객체가 종료될 때 정보가 제거됨 (CWnd’s: : On. Nc. Destroy()) cf) WM_NCCREAT -> WM_CREAT -> … -> WM_DESTROY -> WM_NCDESTROY CMap. Ptr. To. Ptr m_temporary. Map ü ü Temporary map 임시로 생성해야 할때(ex: CWnd: : Get. Active. Window()) 실제로는 CWnd: : Handle. Map 에서 필요할 때 생성 On. Idle()에서 삭제 => Delete. Temp. Map() 호출
CHandle. Map (cont’d) v 멤버함수 Ø CWnd: : From. Handle(HWND h. Wnd) ü ü 주어진 window handle에 mapping되는 object의 pointer를 얻 을 때 사용 h. Wnd를 wrap하고 있는 CWnd pointer return 만약 없으면 h. Wnd를 wrap하는 temporary CWnd return 획득하면 역으로 object를 통해 h. Wnd의 접근이 용이 ( CWnd: : m_h. Wnd 멤버 변수 )
Temporary map 예 “AFXWIN 2. INL” _AFXWIN_INLINE CWnd* PASCAL CWnd: : Get. Active. Window() { return CWnd: : From. Handle(: : Get. Active. Window()); } “Wincore. cpp” CWnd* PASCAL CWnd: : From. Handle(HWND h. Wnd) { CHandle. Map* p. Map = afx. Map. HWND(TRUE); CWnd* p. Wnd = (CWnd*)p. Map->From. Handle(h. Wnd); return p. Wnd; }
Temporary map 예 (cont’d) “Winhand. cpp” CObject* CHandle. Map: : From. Handle(HANDLE h) { if (h == NULL) return NULL; CObject* p. Object = Lookup. Permanent(h); if (p. Object != NULL) return p. Object; // return permanent one else if ((p. Object = Lookup. Temporary(h)) != NULL) { HANDLE* ph = (HANDLE*)((BYTE*)p. Object + m_n. Offset); ph[0] = h; if (m_n. Handles == 2) { ph[1] = h; } return p. Object; // return current temporary one }
Temporary map 예 (cont’d) CObject* p. Temp = NULL; TRY { p. Temp = m_p. Class->Create. Object(); m_temporary. Map. Set. At((LPVOID)h, p. Temp); } CATCH_ALL(e) { Afx. Set. New. Handler(pnh. Old. Handler); Afx. Enable. Memory. Tracking(b. Enable); THROW_LAST(); } END_CATCH_ALL Afx. Set. New. Handler(pnh. Old. Handler); Afx. Enable. Memory. Tracking(b. Enable); HANDLE* ph = (HANDLE*)((BYTE*)p. Temp + m_n. Offset); ph[0] = h; if (m_n. Handles == 2) ph[1] = h; return p. Temp; }
Other Mapping v AFX_MODULE_THREAD_STATE Ø m_pmap. HWND ü Ø m_pmap. HMENU ü Ø device context handles CDC objects m_pmap. HGDIOBJ ü Ø menu handles CMenu objects m_pmap. HDC ü Ø window handles CWnd objects GDI object handles CGDIObjects m_pmap. HIMAGELIST ü image list handles CImage. List objects
Attaching & Detaching v Window handles과 CWnd-derived objects를 연관 시켜주는 함수 Ø CWnd: : Attach() ü ü Ø 주어진 window handle을 CWnd: : m_h. Wnd에 대입 MFC’s permanent map에 정보 추가 CWnd: : Detach() ü ü CWnd: : m_h. Wnd를 NULL로 만듬 MFC’s permanent map에서 정보 제거
CWnd: : Attach BOOL CWnd: : Attach(HWND h. Wnd. New) { ASSERT(m_h. Wnd == NULL); // only attach once, detach on destroy ASSERT(From. Handle. Permanent(h. Wnd. New) == NULL); // must not already be in permanent map if (h. Wnd. New == NULL) return FALSE; CHandle. Map* p. Map = afx. Map. HWND(TRUE); ASSERT(p. Map != NULL); p. Map->Set. Permanent(m_h. Wnd = h. Wnd. New, this); Attach. Control. Site(p. Map); return TRUE; }
CWnd: : Detach HWND CWnd: : Detach() { HWND h. Wnd = m_h. Wnd; if (h. Wnd != NULL) { CHandle. Map* p. Map = afx. Map. HWND(); // don't create if not exist if (p. Map != NULL) p. Map->Remove. Handle(m_h. Wnd); m_h. Wnd = NULL; } m_p. Ctrl. Site = NULL; return h. Wnd; }
주의 v Multiple threads Ø Ø Permanent , temporary map 모두 thread 단위로 저장 전달할 경우 object 대신 HANDLE 을 보내는 것이 바람 직 Thread 1 Thread 2 Window create 접근 불가
Find Win. Main() v APPCORE. CPP Ø v CWin. App 생성자 호출 APPMODULE. CPP Ø _t. Win. Main() extern "C" int WINAPI _t. Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { // call shared/exported Win. Main return Afx. Win. Main(h. Instance, h. Prev. Instance, lp. Cmd. Line, n. Cmd. Show); }
Find Win. Main() (cont’d) v WINMAIN. CPP Ø Afx. Win. Main() int AFXAPI Afx. Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { int n. Return. Code = -1; CWin. Thread* p. Thread = Afx. Get. Thread(); CWin. App* p. App = Afx. Get. App(); // AFX internal initialization if (!Afx. Win. Init(h. Instance, h. Prev. Instance, lp. Cmd. Line, n. Cmd. Show)) goto Init. Failure; // App global initializations (rare) if (p. App != NULL && !p. App->Init. Application()) goto Init. Failure;
Find Win. Main() (cont’d) // Perform specific initializations if (!p. Thread->Init. Instance()) { if (p. Thread->m_p. Main. Wnd != NULL) { p. Thread->m_p. Main. Wnd->Destroy. Window(); } n. Return. Code = p. Thread->Exit. Instance(); goto Init. Failure; } n. Return. Code = p. Thread->Run(); Afx. Win. Term(); return n. Return. Code; }
Find Win. Main() (cont’d) v APPINIT. CPP Ø Afx. Win. Init() BOOL AFXAPI Afx. Win. Init(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { // handle critical errors and avoid Windows message boxes Set. Error. Mode(0) | SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX); // set resource handles AFX_MODULE_STATE* p. Module. State = Afx. Get. Module. State(); p. Module. State->m_h. Current. Instance. Handle = h. Instance; p. Module. State->m_h. Current. Resource. Handle = h. Instance;
Find Win. Main() (cont’d) // fill in the initial state for the application CWin. App* p. App = Afx. Get. App(); if (p. App != NULL) { // Windows specific initialization (not done if no CWin. App) p. App->m_h. Instance = h. Instance; p. App->m_h. Prev. Instance = h. Prev. Instance; p. App->m_lp. Cmd. Line = lp. Cmd. Line; p. App->m_n. Cmd. Show = n. Cmd. Show; p. App->Set. Current. Handles(); } // initialize thread specific data (for main thread) if (!afx. Context. Is. DLL) Afx. Init. Thread(); return TRUE; }
Find Win. Main() (cont’d) v Init. Application() Ø v Init. Instance() Ø v application-specific, but do nothing instance-specific, virtual Run()
CWin. App 생성자 CWin. App: : CWin. App(LPCTSTR lpsz. App. Name) { if (lpsz. App. Name != NULL) m_psz. App. Name = _tcsdup(lpsz. App. Name); else m_psz. App. Name = NULL; // initialize CWin. Thread state AFX_MODULE_STATE* p. Module. State = _AFX_CMDTARGET_GETSTATE(); AFX_MODULE_THREAD_STATE* p. Thread. State = p. Module. State->m_thread; p. Thread. State->m_p. Current. Win. Thread = this; m_h. Thread = : : Get. Current. Thread(); m_n. Thread. ID = : : Get. Current. Thread. Id(); // initialize CWin. App state ASSERT(afx. Current. Win. App == NULL); // only one CWin. App object please p. Module. State->m_p. Current. Win. App = this; ASSERT(Afx. Get. App() == this);
CWin. App 생성자 (cont’d) // in non-running state until Win. Main m_h. Instance = NULL; m_psz. Help. File. Path = NULL; m_psz. Profile. Name = NULL; m_psz. Registry. Key = NULL; m_psz. Exe. Name = NULL; m_p. Recent. File. List = NULL; m_p. Doc. Manager = NULL; m_atom. App = m_atom. System. Topic = NULL; m_lp. Cmd. Line = NULL; m_p. Cmd. Info = NULL; // other initialization m_b. Help. Mode = FALSE; m_n. Safety. Pool. Size = 512; // default size }
Init. Application() BOOL CWin. App: : Init. Application() { if (CDoc. Manager: : p. Static. Doc. Manager != NULL) { if (m_p. Doc. Manager == NULL) m_p. Doc. Manager = CDoc. Manager: : p. Static. Doc. Manager; CDoc. Manager: : p. Static. Doc. Manager = NULL; } if (m_p. Doc. Manager != NULL) m_p. Doc. Manager->Add. Doc. Template(NULL); else CDoc. Manager: : b. Static. Init = FALSE; return TRUE; }
Init. Instance() BOOL CWin. App: : Init. Instance() { return TRUE; }
Run() int CWin. Thread: : Run() { // for tracking the idle time state BOOL b. Idle = TRUE; LONG l. Idle. Count = 0; // acquire and dispatch messages until a WM_QUIT message for (; ; ) { // phase 1: check to see if we can do idle work while (b. Idle && !: : Peek. Message( &m_msg. Cur, NULL, PM_NOREMOVE)) { // call On. Idle while in b. Idle state if (!On. Idle(l. Idle. Count++)) b. Idle = FALSE; // assume "no idle" state }
Run (cont’d) // phase 2: pump messages while available do { // pump message, but quit on WM_QUIT if (!Pump. Message()) return Exit. Instance(); // reset "no idle" state after pumping "normal" message if (Is. Idle. Message(&m_msg. Cur)) { b. Idle = TRUE; l. Idle. Count = 0; } } while (: : Peek. Message (&m_msg. Cur, NULL, PM_NOREMOVE)); } ASSERT(FALSE); // not reachable }
MFC state information v v 프로그램 종료까지 유지되는 정보 AFX_MODULE_STATE class Ø Ø AFXSTAT_. H 보유하는 정보 ü ü Main window handles Resource, module handles Memory allocation tracking ODBC support, OLE support, exception handling
Hidden Cool Stuff v 2 issues Ø Ø window class의 등록 Windows hook와 MFC window의 연결
Registering Window Classes v Windows application Ø v OS 에 적어도 하나 이상의 window class를 등록해야만 한다. window class Ø window의 기본적인 성질 정의 ü ü appearance ( via some flag ) behavior ( via a callback function )
Registering Window Classes (cont’d) v 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) v WNDCLASSes and MFC Style of window Lpfn. Wnd. Proc Cb. Cls. Extra Cb. Wnd. Extra HInstance HIcon HCursor Hbr. Background Lpsz. Menu. Name Lpsz. Class. Name window proc, must be Afx. Wnd. Proc not used ( 0 ) automatically filled with Afx. Get. Instance. Handle icon for frame window cursor for when mouse is over window background color not used ( NULL ) class name
Registering Window Classes (cont’d) v Afx. Wnd Ø Ø Ø v Used for all child windows (CWnd: : Create) Class style : CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW No icon Arrow cursor No background color Afx. Frame. Or. View Ø Ø Ø 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) v Afx. MDIFrame Ø Ø Ø v Used for MDI frame window (CMDIFrame. Wnd: : Create) Class style : CS_DBLCLKS Icon AFX_IDI_STD_MDIFRAME Arrow cursor No background color Afx. Control. Bar Ø Ø Ø Used for standard control bar implementation Class style : 0 No icon Arrow cursor Gray background color (COLOR_BTNFACE)
Registering Window Classes (cont’d) v Afx. Defer. Register. Class() Ø v AFXIMPL. H Afx. End. Defer. Register. Class() Ø WINCORE. CPP #define Afx. Defer. Register. Class(f. Class) Afx. End. Defer. Register. Class(f. Class) BOOL AFXAPI Afx. End. Defer. Register. Class(LONG f. To. Register);
Class 등록 예 “WINCORE. CPP” BOOL CWnd: : Create. Ex(DWORD dw. Ex. Style, LPCTSTR lpsz. Class. Name, LPCTSTR lpsz. Window. Name, DWORD dw. Style, int x, int y, int n. Width, int n. Height, HWND h. Wnd. Parent, HMENU n. IDor. HMenu, LPVOID lp. Param) { // allow modification of several common create parameters CREATESTRUCT cs; cs. dw. Ex. Style = dw. Ex. Style; cs. lpsz. Class = lpsz. Class. Name; cs. lpsz. Name = lpsz. Window. Name; cs. style = dw. Style; cs. x = x; cs. y = y; cs. cx = n. Width; cs. cy = n. Height; cs. hwnd. Parent = h. Wnd. Parent; cs. h. Menu = n. IDor. HMenu; cs. h. Instance = Afx. Get. Instance. Handle(); cs. lp. Create. Params = lp. Param;
Class 등록 예 (cont’d) if (!Pre. Create. Window(cs)) { Post. Nc. Destroy(); return FALSE; } Afx. Hook. Window. Create(this); HWND h. Wnd = : : Create. Window. Ex(cs. dw. Ex. Style, cs. lpsz. Class, cs. lpsz. Name, cs. style, cs. x, cs. y, cs. cx, cs. cy, cs. hwnd. Parent, cs. h. Menu, cs. h. Instance, cs. lp. Create. Params); if (h. Wnd == NULL) Get. Last. Error()); if (!Afx. Unhook. Window. Create()) Post. Nc. Destroy(); if (h. Wnd == NULL) return FALSE; ASSERT(h. Wnd == m_h. Wnd); // should have been set in send msg hook return TRUE; }
Class 등록 예 (cont’d) BOOL CWnd: : Pre. Create. Window(CREATESTRUCT& cs) { if (cs. lpsz. Class == NULL) { // make sure the default window class is registered VERIFY(Afx. Defer. Register. Class(AFX_WND_REG)); // no WNDCLASS provided - use child window default ASSERT(cs. style & WS_CHILD); cs. lpsz. Class = _afx. Wnd; } return TRUE; }
Device Contexts v CDC Ø CPaint. DC ü ü Ø CWindow. DC ü ü Ø 전체 스크린을 표시 (client area+frame) Get. Window. DC() ~ Release. DC() CClient. DC ü ü Ø painting 작업이 발생할 때 Begin. Paint() ~ End. Paint() Client area를 표시 Get. Client. DC() ~ Release. DC() CMeta. File. DC ü ü 메타파일에 대한 그리기 작업 생성자 ~ Create() ~ Delete. Meta. File()
Graphic Objects v CGdi. Object : Base class Ø Ø Ø v CPen CBrush CFont CBitmap CPalette CRgn Win 32 API의 함수들을 wrapping
예제 CMy. Wnd: : On. Paint() { CPaint. DC CPen* CPen paint. DC(this); p. Old. Pen; blue. Pen(PS_SOLID, 25, RGB(0, 0, 255)); p. Old. Pen = paint. DC. Select. Object(&blue. Pen); paint. DC. Move. To(1, 1); paint. DC. Line. To(100, 100); paint. DC. Select. Object(p. Old. Pen); }
Chap. 3 Message Handling
Contents v v v Introduction Window messages Message mapping MFC가 message map을 이용하는 방법 Message loop의 hooking
Introduction v Message handling Ø C/SDK ü Ø Switch/case 문을 통해 구현 MFC ü ü Switch/case 문은 없다 Callback function은 어떻게 정의되는가
Basic Components v MFC의 message handling 구조 Ø Ø v CCmd. Target class Message maps MFC message maps에 대한 의문 Ø Ø 어떻게 switch/case문을 대체하는가 어떤 data structure로 정의되는가 어떻게 message map이 작동하는가 어떻게 message가 연결되는가
Window Messages (cont’d) v 핸들링 클래스 Ø Windows message, control notification ü Ø CWnd 로부터 파생된 클래스들 : HWND 를 포함 Command message ü ü 다양한 종류 : CCmd. Target 로부터 파생된 클래스들 예) open 명령 핸들링 : application
UI Objects and Command IDs
Message Handling in C/SDK v Windows program의 정수 While (Get. Message(&msg, NULL, NULL)) { // Translates virtual key codes Translate. Message(&msg); // Dispatches message to window Dispatch. Message(&msg); } // Returns the value from Post. Quit. Message Return (msg. w. Param);
Message Handling in C/SDK (cont’d) WNDCLASS ws; … ws. lpfn. Wnd. Proc = Wnd. Proc; … Register. Class(ws); LRSULT Wnd. Proc(HWND hwnd, UINT message, WPARAM w. Param, LPARAM l. Param) { switch (message) { case WM_CREATE: … break; case WM_COMMAND: switch (w. Param) { case IDM_ABOUT: … } }
Message Handling in MFC v 방법 1 Ø Ø v Polymorphism : 가상 멤버 함수 이용 심각한 메모리 낭비 방법 2 Ø Ø Message maps MFC 의 특징
Message Mapping Internals v 두 부분으로 구성 Ø CCmd. Target ü Ø Window message나 command를 받기 위해 반드시 상속받아 야 하는 class Message map ü Window message와 이를 처리하는 class member function을 연관시켜주는 mechanism
CCmd. Target Class v 메시지의 처리 Ø Ø CCmd. Target의 파생 클래스 예 ü ü ü CWnd class CDocument CWin. App
Message Map v Data structures Ø AFX_MSGMAP_ENTRY(AFXWIN. H) ü ü Message map table의 한 element(entry) Message에 대한 정보와 message handler에 대한 정보 를 저장 struct AFX_MSGMAP_ENTRY { UINT n. Message; // windows message UINT n. Code; // control code or WM_NOTIFY code UINT n. ID; // control ID (or 0 for windows messages) UINT n. Last. ID; // used for entries specifying a range of control id's UINT n. Sig; // 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* pfn. Get. Base. Map)(); #else const AFX_MSGMAP* p. Base. Map; #endif const AFX_MSGMAP_ENTRY* lp. Entries; };
Message Map Macros v 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 _message. Entries[]; protected: static AFX_DATA const AFX_MSGMAP message. Map; virtual const AFX_MSGMAP* Get. Message. Map() const;
Message Map Macros (cont’d) #define BEGIN_MESSAGE_MAP(the. Class, base. Class) const AFX_MSGMAP* the. Class: : Get. Message. Map() const { return &the. Class: : message. Map; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP the. Class: : message. Map = { &base. Class: : message. Map, &the. Class: : _message. Entries[0] }; AFX_COMDAT const AFX_MSGMAP_ENTRY the. Class: : _message. Entries[] = { #define END_MESSAGE_MAP() {0, 0, Afx. Sig_end, (AFX_PMSG)0 } };
Massage Map 예 class CTest. View : public Cview { protected: //{{AFX_MSG(CTest. View) afx_msg void On. LButton. Dbl. Clk(UINT n. Flags, Cpoint); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; class CTest. View : public Cview { protected: afx_msg void On. LButton. Dbl. Clk(UINT n. Flags, Cpoint); private: static const AFX_MSGMAP_ENTRY _message. Entries[]; protected: static const AFX_MSGMAP message. Map; virtual const AFX_MSGMAP* Get. Message. Map() const; };
Massage Map 예 (cont’d) BEGIN_MESSAGE_MAP(CTest. VIew, Cview) //{{AFX_MSG_MAP(CTest. View) ON_COMMAND(ID_STRING_CENTER, On. String. Center) ON_WM_LBUTTONDBLCLK() //}}AFX_MSG_MAP END_MESSAGE_MAP() const AFX_MSGMAP* CTest. View: : Get. Message. Map() const { return &CTest. View: : message. Map; } AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CTest. View: : message. Map = { &CView: : message. Map, &CTest. View: : _message. Entries[0] }; const AFX_MSGMAP_ENTRY CTest. VIew: : _message. Entries[] = { ON_COMMAND(ID_STRING_CENTER, On. String. Center) ON_WM_LBUTTONDBLCLK() {0, 0, Afx. Sig_end, (AFX_PMSG)0 } };
Message Map Entry Macro v AFXMSG_. H #define ON_COMMAND(id, member. Fxn) { WM_COMMAND, CN_COMMAND, (WORD)id, Afx. Sig_vv, (AFX_PMSG)&member. Fxn }, #define ON_WM_LBUTTONDBLCLK() { WM_LBUTTONDBLCLK, 0, 0, 0, Afx. Sig_vwp, (AFX_PMSG)(AFX_PMSGW) (void (AFX_MSG_CALL CWnd: : *) (UINT, CPoint))&On. LButton. Dbl. Clk },
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 On. My. Message(WPARAM w. Param, LPARAM l. Param); For example: #define WM_MYMESSAGE (WM_USER + 100) BEGIN_MESSAGE_MAP(CMy. Wnd, CMy. Parent. Wnd. Class) ON_MESSAGE(WM_MYMESSAGE, On. My. Message) END_MESSAGE_MAP() CWnd* p. Wnd =. . . ; p. Wnd->Send. Message(WM_MYMESSAGE);
Handlers for Message-Map Ranges v 종류 Ø Ø Ø v ON_COMMAND_RANGE ON_UPDATE_COMMAND ON_CONTROL_RANGE Message-Map Entry Ø ID 는 연속적이어야 한다. BEGIN_MESSAGE_MAP(CMy. App, CWin. App) ON_COMMAND_RANGE(ID_MYCMD_ONE, ID_MYCMD_TEN, On. Do. Something) ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON 10, On. Button. Clicked) END_MESSAGE_MAP( )
Handlers for Message-Map Ranges v Handlers . . . void CMy. Dialog: : On. Button. Clicked( UINT n. ID ) { int n. Button = n. ID - IDC_BUTTON 1; ASSERT( n. Button >= 0 && n. Button < 10 ); //. . . }
MFC의 Message Map이용 v MFC-based program Ø 두 종류의 메시지를 처리 ü regular window messages Ø ü commands Ø Ø WM_MOUSEMOVE, … WM_COMMAND Message-mapping architecture의 이해 ü 두 종류 메시지를 각각 추적
How to be wired v 16 -bit version Ø Afx. Wnd. Proc() ü v Message handling procedure로 등록된 함수 32 -bit version Ø Ø Ø Def. Window. Proc() 가 message handler로 등록 Message hook방법을 써서 결국에는 Afx. Wnd. Proc()가 message를 처리하게 함 Hooking function ü Ø _Afx. Cbt. Filter. Hook(), _Afx. Standard. Subclass() 3 D Control을 지원하기 위함
How to be wired (cont’d) “Win. Core. cpp” BOOL AFXAPI Afx. End. Defer. Register. Class(LONG f. To. Register) { // mask off all classes that are already registered AFX_MODULE_STATE* p. Module. State = Afx. Get. Module. State(); f. To. Register &= ~p. Module. State->m_f. Registered. Classes; if (f. To. Register == 0) return TRUE; LONG f. Registered. Classes = 0; // common initialization WNDCLASS wndcls; memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults wndcls. lpfn. Wnd. Proc = Def. Window. Proc; wndcls. h. Instance = Afx. Get. Instance. Handle(); wndcls. h. Cursor = afx. Data. hcur. Arrow;
How to be wired (cont’d) BOOL CWnd: : Create. Ex(DWORD dw. Ex. Style, LPCTSTR lpsz. Class. Name, LPCTSTR lpsz. Window. Name, DWORD dw. Style, int x, int y, int n. Width, int n. Height, HWND h. Wnd. Parent, HMENU n. IDor. HMenu, LPVOID lp. Param) { … Afx. Hook. Window. Create(this); } void AFXAPI Afx. Hook. Window. Create(CWnd* p. Wnd) { if (p. Thread. State->m_h. Hook. Old. Cbt. Filter == NULL) { p. Thread. State->m_h. Hook. Old. Cbt. Filter = : : Set. Windows. Hook. Ex(WH_CBT, _Afx. Cbt. Filter. Hook, NULL, : : Get. Current. Thread. Id()); if (p. Thread. State->m_h. Hook. Old. Cbt. Filter == NULL) Afx. Throw. Memory. Exception(); } p. Thread. State->m_p. Wnd. Init = p. Wnd; }
How to be wired (cont’d) LRESULT CALLBACK _Afx. Cbt. Filter. Hook(int code, WPARAM w. Param, LPARAM l. Param) { … // subclass the window if not already wired to Afx. Wnd. Proc if (!b. Afx. Wnd. Proc) { // subclass the window with standard Afx. Wnd. Proc old. Wnd. Proc = (WNDPROC)Set. Window. Long(h. Wnd, GWL_WNDPROC, (DWORD)afx. Wnd. Proc); ASSERT(old. Wnd. Proc != NULL); *p. Old. Wnd. Proc = old. Wnd. Proc; } … }
Message Handling v Afx. Wnd. Proc()(WINCORE. CPP) Ø WM_QUERYAFXWNDPROC ü Ø MFC’s message map을 사용하는 MFC window인지를 확 인할 수 있는 message 다른 message에 대해서는 Afx. Call. Wnd. Proc() 함수 를 호출 LRESULT CALLBACK Afx. Wnd. Proc(HWND h. Wnd, UINT n. Msg, WPARAM w. Param, LPARAM l. Param) { if (n. Msg == WM_QUERYAFXWNDPROC) return 1; CWnd* p. Wnd = CWnd: : From. Handle. Permanent(h. Wnd); return Afx. Call. Wnd. Proc(p. Wnd, h. Wnd, n. Msg, w. Param, l. Param); }
Message Handling (cont’d) v Afx. Call. Wnd. Proc()(WINCORE. CPP) Ø Ø Thread state 구조체에 message의 정보를 저장 WM_INITDIALOG처리 ü Ø Auto-center dialog위한 처리를 수행 Window object의 window procedure호출 ü ü ü CWnd: : Window. Proc()(WINCORE. CPP) 내부적으로 On. Wnd. Msg()를 호출 On. Wnd. Msg()가 처리하지 못하면 CWnd: : Def. Window. Proc()를 호출
Message Handling (cont’d) LRESULT AFXAPI Afx. Call. Wnd. Proc(CWnd* p. Wnd, HWND h. Wnd, UINT n. Msg, WPARAM w. Param = 0, LPARAM l. Param = 0) { _AFX_THREAD_STATE* p. Thread. State = _afx. Thread. State. Get. Data(); MSG old. State = p. Thread. State->m_last. Sent. Msg; // save for nesting p. Thread. State->m_last. Sent. Msg. hwnd = h. Wnd; p. Thread. State->m_last. Sent. Msg. message = n. Msg; p. Thread. State->m_last. Sent. Msg. w. Param = w. Param; p. Thread. State->m_last. Sent. Msg. l. Param = l. Param; CRect rect. Old; DWORD dw. Style = 0; if (n. Msg == WM_INITDIALOG) _Afx. Pre. Init. Dialog(p. Wnd, &rect. Old, &dw. Style); // delegate to object's Window. Proc l. Result = p. Wnd->Window. Proc(n. Msg, w. Param, l. Param); // more special case for WM_INITDIALOG if (n. Msg == WM_INITDIALOG) _Afx. Post. Init. Dialog(p. Wnd, rect. Old, dw. Style); p. Thread. State->m_last. Sent. Msg = old. State; return l. Result; }
Message Handling (cont’d) v CWnd: : Wndow. Proc()(WINCORE. CPP) Ø Ø virtual 이므로 override 가능 성능 향상을 위해 message-mapping system을 거치지 않고 처리해야 하는 메시지에 대한 핸들링 가능 “Win. Core. Cpp” LRESULT CWnd: : Window. Proc(UINT message, WPARAM w. Param, LPARAM l. Param) { // On. Wnd. Msg does most of the work, except for Def. Window. Proc call LRESULT l. Result = 0; if (!On. Wnd. Msg(message, w. Param, l. Param, &l. Result)) l. Result = Def. Window. Proc(message, w. Param, l. Param); return l. Result; }
Message Handling (cont’d) v CWnd: : On. Wnd. Msg()(WINCORE. CPP) Ø 특별히 처리하는 message ü ü Ø WM_COMMAND, WM_NOTIFY WM_ACTIVATE, WM_SETCURSOR 기타 다른 message들은 message map을 이용하여 해 당 message handler를 호출
Message Handling (cont’d) BOOL CWnd: : On. Wnd. Msg(UINT message, WPARAM w. Param, LPARAM l. Param, LRESULT* p. Result) { // special case for commands if (message == WM_COMMAND) { if (On. Command(w. Param, l. Param)) { l. Result = 1; goto LReturn. True; } 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* p. Message. Map; p. Message. Map = Get. Message. Map(); UINT i. Hash; i. Hash = (LOWORD((DWORD)p. Message. Map) ^ message) & (i. Hash. Max-1); Afx. Lock. Globals(CRIT_WINMSGCACHE); AFX_MSG_CACHE* p. Msg. Cache; p. Msg. Cache = &_afx. Msg. Cache[i. Hash]; const AFX_MSGMAP_ENTRY* lp. Entry; if (message == p. Msg. Cache->n. Msg && p. Message. Map == p. Msg. Cache>p. Message. Map) { … } else { // not in cache, look for it p. Msg. Cache->n. Msg = message; p. Msg. Cache->p. Message. Map = p. Message. Map; for (/* p. Message. Map already init'ed */; p. Message. Map != NULL; p. Message. Map = p. Message. Map->p. Base. Map) { Afx. Find. Message. Entry(); }
Message Handling Trace v 두가지 종류의 message Ø Regular window message ü Ø Command message ü ü v WM_MOUSEMOVE 메뉴나 control이 보내는 message WM_COMMAND 서로 다른 방식으로 처리 Ø CWnd: : On. Wnd. Msg()참고
Handling WM_COMMAND v 가정 Ø v CWnd: : On. Wnd. Msg()함수에서 Ø v Main frame의 메뉴 명령 수행 CWnd: : On. Command()함수 호출 CWnd: : On. Command() Ø Ø Virtual function(Framework이 적절한 version의 함수 를 호출함) CFrame. Wnd: : On. Command() ü ü On-line help에 관한 것이면 해당 기능 수행 아니면 CWnd: : On. Command()함수 호출
Handling WM_COMMAND (cont’d) “Win. Frm. cpp” BOOL CFrame. Wnd: : On. Command(WPARAM w. Param, LPARAM l. Param) { HWND h. Wnd. Ctrl = (HWND)l. Param; UINT n. ID = LOWORD(w. Param); CFrame. Wnd* p. Frame. Wnd = Get. Top. Level. Frame(); ASSERT_VALID(p. Frame. Wnd); if (p. Frame. Wnd->m_b. Help. Mode&&h. Wnd. Ctrl==NULL&&n. ID!=ID_HELP&& n. ID!= ID_DEFAULT_HELP && n. ID != ID_CONTEXT_HELP) { // route as help if (!Send. Message(WM_COMMANDHELP, 0, HID_BASE_COMMAND+n. ID)) Send. Message(WM_COMMAND, ID_DEFAULT_HELP); return TRUE; } // route as normal command return CWnd: : On. Command(w. Param, l. Param); }
Handling WM_COMMAND (cont’d) “Win. Core. Cpp” BOOL CWnd: : On. Command(WPARAM w. Param, LPARAM l. Param) { if (h. Wnd. Ctrl == NULL) else { if (_afx. Thread. State->m_h. Lockout. Notify. Window == m_h. Wnd) return TRUE; // locked out - ignore control notification // reflect notification to child window control if (Reflect. Last. Msg(h. Wnd. Ctrl)) return TRUE; // eaten by child // zero IDs for normal commands are not allowed if (n. ID == 0) return FALSE; } return On. Cmd. Msg(n. ID, n. Code, NULL); }
Handling WM_COMMAND (cont’d) v CWnd: : On. Command()(WINCORE. CPP) Ø Ø Ø LPARAM을 조사 만약 control이 보낸 message이면 control에게 다시 message를 reflect한 후 return 아니면 CWnd: : On. Cmd. Msg()(virtual function)함수 호 출
Handling WM_COMMAND (cont’d) “Win. Frm. cpp” BOOL CFrame. Wnd: : On. Cmd. Msg(UINT n. ID, int n. Code, void* p. Extra, AFX_CMDHANDLERINFO* p. Handler. Info) { CPush. Routing. Frame push(this); // pump through current view FIRST CView* p. View = Get. Active. View(); if (p. View != NULL && p. View->On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; // then pump through frame if (CWnd: : On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; // last but not least, pump through app CWin. App* p. App = Afx. Get. App(); if (p. App != NULL && p. App->On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; return FALSE; }
Handling WM_COMMAND (cont’d) “View. Core. cpp” BOOL CView: : On. Cmd. Msg(UINT n. ID, int n. Code, void* p. Extra, AFX_CMDHANDLERINFO* p. Handler. Info) { // first pump through pane if (CWnd: : On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; // then pump through document if (m_p. Document != NULL) { // special state for saving view before routing to document CPush. Routing. View push(this); return m_p. Document->On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info); } return FALSE; }
Handling WM_COMMAND (cont’d) “Doc. Core. cpp” BOOL CDocument: : On. Cmd. Msg(UINT n. ID, int n. Code, void* p. Extra, AFX_CMDHANDLERINFO* p. Handler. Info) { if (CCmd. Target: : On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; // otherwise check template if (m_p. Doc. Template != NULL && m_p. Doc. Template->On. Cmd. Msg(n. ID, n. Code, p. Extra, p. Handler. Info)) return TRUE; return FALSE; }
Handling WM_COMMAND (cont’d) v CFrame. Wnd: : On. Cmd. Msg()(WINFRM. CPP) Ø 다음의 순서로 해당 On. Cmd. Msg()함수를 호출 ü ü Active view’s document Main frame window Application
Handling WM_COMMAND (cont’d) v 만약 active view에 해당 handler가 있다면 Ø v CView: : On. Cmd. Msg()가 호출됨 CView: : On. Cmd. Msg()(VIEWCORE. CPP) Ø Ø Ø View에서 처리를 시도한 후 안되면 document에서 처 리를 시도 View에서 처리 시도는 CWnd: : On. Cmd. Msg()함수의 호 출로 이루어짐 이 때 CWnd는 On. Cmd. Msg를 override하지 않기 때문 에 CCmd. Target: : On. Cmd. Msg를 호출하는 결과가 됨
Handling WM_COMMAND (cont’d) “Cmd. Targ. cpp” BOOL CCmd. Target: : On. Cmd. Msg(UINT n. ID, int n. Code, void* p. Extra, AFX_CMDHANDLERINFO* p. Handler. Info) { for (p. Message. Map = Get. Message. Map(); p. Message. Map != NULL; p. Message. Map = p. Message. Map->p. Base. Map) { lp. Entry = Afx. Find. Message. Entry (p. Message. Map->lp. Entries, n. Msg, n. Code, n. ID); if (lp. Entry != NULL) { return _Afx. Dispatch. Cmd. Msg(this, n. ID, n. Code, lp. Entry->pfn, p. Extra, lp. Entry->n. Sig, p. Handler. Info); } } return FALSE; // not handled }
Handling WM_COMMAND (cont’d) v CCmd. Target: : On. Cmd. Msg()(CMDTARG. CPP) Ø Ø v Message map을 보고 message handler를 찾음 찾게 되면 _Afx. Dispatch. Cmd. Msg()함수를 호출하여 해 당 handler를 실행시킴 _Afx. Dispatch. Cmd. Msg()(CMDTARG. CPP) Ø Function signature에 따라 다른 동작을 수행 ü ü Regular command Extended command Visual Basic control 등등
Handling WM_COMMAND (cont’d) v Frame window일 때의 처리과정 정리 Ø Ø Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CFrame. Wnd: : On. Command() CFrame. Wnd: : On. Cmd. Msg() CCmd. Target: : On. Cmd. Msg() _Afx. Dispatch. Cmd. Msg() CMain. Fram: : On~()
Handling WM_COMMAND (cont’d) v Document일 때의 처리과정 정리 Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CFrame. Wnd: : On. Command() CFrame. Wnd: : On. Cmd. Msg() CView: : On. Cmd. Msg() Cdocument: : On. Cmd. Msg() CCmd. Target: : On. Cmd. Msg() _Afx. Dispatch. Cmd. Msg() CMy. Doc: : On~()
Handling WM_COMMAND (cont’d) v View일 때의 처리과정 정리 Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CFrame. Wnd: : On. Command() CFrame. Wnd: : On. Cmd. Msg() CView: : On. Cmd. Msg() CCmd. Target: : On. Cmd. Msg() _Afx. Dispatch. Cmd. Msg() CMy. View: : On~()
Handling WM_COMMAND (cont’d) v App일 때의 처리과정 정리 Ø Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CFrame. Wnd: : On. Command() CCmd. Target: : On. Cmd. Msg() _Afx. Dispatch. Cmd. Msg() CMy. App: : On~()
Handling WM_COMMAND (cont’d) v Dialog Box일 때의 처리과정 정리 Ø Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CDialog: : On. Cmd. Msg() CCmd. Target: : On. Cmd. Msg() _Afx. Dispatch. Cmd. Msg() CAbout. Dlg: : On~()
Handling Regular Window Message v Command message일 때와 처음의 과정은 비슷 Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() Window. Proc() On. Wnd. Msg() ü 이 함수 안에서 message handler를 찾기 위해 Afx. Find. Message. Entry()를 호출
Handling Regular Window Message (cont’d) v Afx. Find. Message. Entry() Ø 두 버전이 있음. ü ü Ø Ø Assembly language : intel-based machine C language : otherwise Message map에서 해당 핸들러가 있는지 검색 END_MESSAGE_MAP에 의해 생성된 table의 끝까지 검 색
Handling Regular Window Message (cont’d) v Command message와의 차이점 Ø Ø Command message는 handler를 찾기 위해서 여러 장 소를 옮겨다님 Regular message는 On. Wnd. Msg()에서 handler를 찾으 면 바로 해당 handler를 호출하고, 아니면 Def. Window. Proc()를 이용
Handling Regular Window Message (cont’d) v 예) View에서 WM_SIZE message처리 과정 Ø Ø Ø Afx. Wnd. Proc() Afx. Call. Wnd. Proc() CWnd: : Window. Proc() CWnd: : On. Wnd. Msg() CSdiapp. View: : On. Size()
Other Kinds of Messages v 지금까지 살펴 본 메시지 종류 Ø Ø v WM_COMMAND Window messages(WM_SIZE, WM_MOVE, …) 그 이외의 메시지 종류 Ø Ø Ø WM_NOTIFY WM_ACTIVATE WM_SETCURSOR
Other Kinds of Messages (cont’d) v WM_NOTIFY Ø Ø Ø Control이 보내는 message 항상 notify message 반면, WM_COMMAND ü command이거나 notify message
Other Kinds of Messages (cont’d) CWnd: : On. Wnd. Msg()에서 // special case for notifies if (message == WM_NOTIFY) { NMHDR* p. NMHDR = (NMHDR*)l. Param; if (p. NMHDR->hwnd. From != NULL && On. Notify(w. Param, l. Param, &l. Result)) goto LReturn. True; return FALSE; … } struct NMHDR { HWND hwnd. From; UINT id. From; UINT code; // control that sent notification // ID of control // notification code
Other Kinds of Messages (cont’d) “Wincore. cpp” BOOL CWnd: : On. Notify(WPARAM, LPARAM l. Param, LRESULT* p. Result) { NMHDR* p. NMHDR = (NMHDR*)l. Param; HWND h. Wnd. Ctrl = p. NMHDR->hwnd. From; // get the child ID from the window itself UINT n. ID = _Afx. Get. Dlg. Ctrl. ID(h. Wnd. Ctrl); int n. Code = p. NMHDR->code; if (_afx. Thread. State->m_h. Lockout. Notify. Window == m_h. Wnd) return TRUE; // locked out - ignore control notification // reflect notification to child window control if (Reflect. Last. Msg(h. Wnd. Ctrl, p. Result)) return TRUE; // eaten by child AFX_NOTIFY notify; notify. p. Result = p. Result; notify. p. NMHDR = p. NMHDR; return On. Cmd. Msg(n. ID, MAKELONG(n. Code, WM_NOTIFY), ¬ify, NULL); }
Other Kinds of Messages (cont’d) BOOL PASCAL CWnd: : Reflect. Last. Msg(HWND h. Wnd. Child, LRESULT* p. Result) { CHandle. Map* p. Map = afx. Map. HWND(); CWnd* p. Wnd = (CWnd*)p. Map->Lookup. Permanent(h. Wnd. Child); if (p. Wnd == NULL) { // check if the window is an OLE control … return FALSE; } return p. Wnd->Send. Child. Notify. Last. Msg(p. Result); }
Other Kinds of Messages (cont’d) BOOL CWnd: : Send. Child. Notify. Last. Msg(LRESULT* p. Result) { _AFX_THREAD_STATE* p. Thread. State = _afx. Thread. State. Get. Data(); return On. Child. Notify(p. Thread. State->m_last. Sent. Msg. message, p. Thread. State->m_last. Sent. Msg. w. Param, p. Thread. State->m_last. Sent. Msg. l. Param, p. Result); }
Other Kinds of Messages (cont’d) v Message reflection Ø Ø Ø On. Wnd. Msg()에서 On. Notify()를 호출 On. Nofity()는 다시 On. Child. Notify()를 호출하여 control 로 message를 다시 보냄 결국에는 control이 parent에 의존하지 않고 자신의 일 을 처리하게 함
Other Kinds of Messages (cont’d) Ø WM_ACTIVATE ü Ø On. Wnd. Msg()함수에서 _Afx. Handle. Activate()를 호출 WM_SETCURSOR ü On. Wnd. Msg()함수에서 _Afx. Handle. Set. Cursor()함수 호출
Message Loop Hooking v Message loop hooking Ø Ø Message가 해당 handler에 의해 처리되기 전에 어떠한 작업을 하고 싶을때 Pre. Translate. Message() ü CWin. App: : Pre. Translate. Message() Ø Ø ü CWin. App: : Run()함수는 message가 message pump에 의해 처 리되기 전에 위 함수를 호출 TRUE를 return하면 message pump는 해당 message에 관해 서는 처리를 하지 않음 CWnd: : Pre. Translate. Message()
Message Loop Hooking (cont’d) “THRDCORE. cpp” int CWin. Thread: : Run() { // for tracking the idle time state BOOL b. Idle = TRUE; LONG l. Idle. Count = 0; // acquire and dispatch messages until a WM_QUIT message is received. for (; ; ) { // phase 1: check to see if we can do idle work while (b. Idle && !: : Peek. Message(&m_msg. Cur, NULL, PM_NOREMOVE)) { // call On. Idle while in b. Idle state if (!On. Idle(l. Idle. Count++)) b. Idle = FALSE; // assume "no idle" state }
Message Loop Hooking (cont’d) // phase 2: pump messages while available do { // pump message, but quit on WM_QUIT if (!Pump. Message()) return Exit. Instance(); // reset "no idle" state after pumping "normal" message if (Is. Idle. Message(&m_msg. Cur)) { b. Idle = TRUE; l. Idle. Count = 0; } } while (: : Peek. Message(&m_msg. Cur, NULL, PM_NOREMOVE)); } ASSERT(FALSE); // not reachable }
Message Loop Hooking (cont’d) BOOL CWin. Thread: : Pump. Message() { ASSERT_VALID(this); if (!: : Get. Message(&m_msg. Cur, NULL, NULL)) { } // process this message if (m_msg. Cur. message != WM_KICKIDLE && !Pre. Translate. Message(&m_msg. Cur)) { : : Translate. Message(&m_msg. Cur); : : Dispatch. Message(&m_msg. Cur); } return TRUE; }
Message Loop Hooking (cont’d) BOOL CWin. Thread: : Pre. Translate. Message(MSG* p. Msg) { // if this is a thread-message, short-circuit this function if (p. Msg->hwnd == NULL && Dispatch. Thread. Message. Ex(p. Msg)) return TRUE; // walk from target to main window CWnd* p. Main. Wnd = Afx. Get. Main. Wnd(); if (CWnd: : Walk. Pre. Translate. Tree(p. Main. Wnd->Get. Safe. Hwnd(), p. Msg)) return TRUE; // in case of modeless dialogs, last chance route through main // window's accelerator table if (p. Main. Wnd != NULL) { CWnd* p. Wnd = CWnd: : From. Handle(p. Msg->hwnd); if (p. Wnd->Get. Top. Level. Parent() != p. Main. Wnd) return p. Main. Wnd->Pre. Translate. Message(p. Msg); } return FALSE; // no special processing }
Message Loop Hooking (cont’d) “Win. Core. Cpp” BOOL PASCAL CWnd: : Walk. Pre. Translate. Tree(HWND h. Wnd. Stop, MSG* p. Msg) { // walk from the target window up to the h. Wnd. Stop window checking // if any window wants to translate this message for (HWND h. Wnd=p. Msg->hwnd; h. Wnd != NULL; h. Wnd=: : Get. Parent(h. Wnd)) { CWnd* p. Wnd = CWnd: : From. Handle. Permanent(h. Wnd); if (p. Wnd != NULL) { // target window is a C++ window if (p. Wnd->Pre. Translate. Message(p. Msg)) return TRUE; } // got to h. Wnd. Stop window without interest if (h. Wnd == h. Wnd. Stop) break; } return FALSE; // no special processing }
- Pjesma utjeha kose
- Snu financial aid
- What is snu
- Fazy snu wykres
- Snu summer school
- Snu
- Math snu
- Zoom snu
- Snu cse
- Snu gsis
- Blackboard snu
- Mfc-002
- Ser y hacer del equipo zonal mfc
- Mfc nacional capacitaciones
- Mfc internals
- Decalogo del mfc
- Mfc
- Mfc internals
- Mfc cmas
- Mfcl5750
- Windows foundation classes
- Guia del matrimonio promotor mfc primer nivel
- Asistente eclesial mfc nacional
- Mfc 원 그리기
- Mfc vs wpf
- Exigencias basicas del mfc
- P$drawbitmap
- Mfc critical section
- Mfc feature pack
- Exigencias basicas del mfc
- Hora santa por las familias mfc
- Mfc app
- Conclusion paragraph format
- Write recommendations of yeshpal committee
- Introduction to yacc
- Dare essay 5th grade
- Introduction examples for essays
- Abstract vs introduction
- Long introduction
- Email writing topics
- Intro paragraph bridge
- Introduction answer the question
- Informative essay thesis
- Persuasive editorial article examples
- Pros and cons essay introduction example
- Example of research introduction
- Writing a diary
- What does an introduction include
- Three chunk paragraph
- Introduction of mesopotamian civilization
- Expected behavior in work immersion
- Wireless communication introduction
- Band introduction speech
- Introduction of personal selling
- Humour introduction
- Informal email
- Thematic essay introduction example
- What are the qualities of good writing
- Smart note taker.
- Introduction to e business
- Introduction to semiconductors
- Introduction of infosys
- Introduction of transport
- Acids and bases webquest
- Introduction to dynamic web content
- Pollution introduction
- Pupil of plato
- Water purification conclusion
- Victorian poetry introduction
- Victorian era novel characteristics
- Exposé sur victor hugo
- Vegetable production introduction
- Anecdote example in essay
- Urinary system introduction
- Introduction of uric acid
- Types of selling situations in personal selling
- Famous epic poem by homer
- Oltp vs olap
- Enduring issues essay introduction
- Data structures unit 1
- Introduction to computer organization and architecture
- Unit 1 introduction to accounting
- Uninterruptible power supply
- Ultrasonic testing applications
- Different types of hooks for writing
- Application of cro
- Who is charlotte in tuesdays with morrie
- Introduction of clutch
- C b e transistor
- A transformer is a device used to
- Introduction to transducers
- How does tragic love affect teenagers today
- Introduction to traffic management
- Touch screen purpose
- Introduction of tpr
- Motor equivalent circuit
- They say / i say introduction examples
- Thermochemistry
- Therapeutic communication introduction
- Theme of paragraph
- World wide web introduction
- Short story
- Cars model research introduction
- Prayer introduction
- Poisson random variable variance
- Good thesis statements for the odyssey
- Who was homer
- Introduction to the microscope
- Alphonse daudet class 12
- Hook in introductory paragraph
- Introduction paragraph examples high school
- Introduction of western education in india
- The iliad text
- The great gatsby introduction essay
- The great gatsby chapter 3 title
- What method does the giver use to train jonas?
- Hook introduction examples
- Peristalsis in digestive system
- Introduction for a comparative essay
- Introduction to heart
- Body conclusion introduction
- Running header apa
- Roadmap introduction paragraph example
- What is team teaching
- Introduction of system analysis and design
- Introduction of symposium
- Swot analysis of tata motors
- Intro paragraph for argumentative essay
- Sample introduction of guest speaker for webinar
- Introduction to meghalaya
- Best introduction lines for students
- Internship portfolio introduction
- Define stoichiometry
- Stations of the cross introduction
- Spm jiscmail
- Specific purpose statement
- Spectrophotometer introduction
- Introduction to waste management
- Introduction to waste management
- Conclusion of solar tree
- Manual testing concepts
- Karl marx sociology
- Project introductions
- Introduction about social media
- Soapstones example
- An introduction to group communication
- 00104 15 introduction to power tools answer key
- Module 00105 exam introduction to construction drawings
- What are engineered plans for motors pumps
- Single entry system introduction
- Simple machines
- Conclusion of distillation
- What is laghu katha
- Express themselves
- Introduction of sheet metal
- Superplastic forming
- She stoops to conquer introduction
- Introduction of hamlet
- Sonnet 148
- Seven segment display introduction
- Ooa&d
- Examples of service marketing
- Introduction to server side programming
- 1/3 serial dilution
- Intraspecific hybridization
- Introduction of a product example
- Analytical text response structure
- 9-2 tangents
- Introduction to quadratic functions
- Mudalier commission
- Three dumb routers
- Sdhsdh
- Investigatory project format
- Nas v san
- Food writing sample
- Introduction body conclusion example
- Introduction to clinical chemistry
- Rural banking - introduction