객체지향프로그래밍응용 Chap 2. 프로그램의뼈대 2012.09.17. 오병우 컴퓨터공학과금오공과대학교
윈도우프로그램각부분의명칭 타이틀바 메뉴 프레임윈도우오브젝트 메뉴오브젝트 도구모음 툴바오브젝트 스크롤바 클라이언트영역 뷰오브젝트 상태표시줄 상태바오브젝트 Department of Computer Engineering 2
Win32 Application API 프로그래밍 A typical Hello World! application Department of Computer Engineering 3
AFX 구조 CFrameWnd 윈도우의프레임 ( 틀 ) 을관리 CView 데이터를보여주는윈도우 CDocument 데이터를저장, 처리 ( 눈에는안보임 ) CWinApp 위의세오브젝트를묶어주고, 프로그램을구동시킴 ( 눈에는안보임 ) Department of Computer Engineering 4
윈도우프로그램의객체지향적분할 프레임윈도우와뷰를분리한이유 일관된사용자인터페이스 프레임에부여된역할즉, 창의크기조절, 확대, 축소등을사용자가할필요없이기본적으로제공 뷰에서는단지실제보여주어야할데이터만을사용자가적절하게계산하여제공 도큐먼트와뷰를분리한이유 데이터저장및처리 (CDocument) 와이를보여주는 (CView) 을분리함으로써 클래스의역할을분담 클래스를단순화시킴 같은데이터라도보여주는형태가다를수있음 즉, 하나의 Document 에는한개이상의 View 가존재 Department of Computer Engineering 5
프로그램의뼈대를이루는클래스계층구조 CObject CCmdTarget CWinApp 거의모든 MFC 클래스의기반클래스 이벤트를받는기능 프로그램을구동시키는기능 CDocument CWnd CFrameWnd CView 데이터를저장하고처리하는기능 윈도우에관련된기능 ( 눈에보이는오브젝트 ) & 메시지처리기능 프로그램윈도우프레임 ( 외곽 ) 을관리하는기능 데이터를보여주는윈도우를관리하는기능 Department of Computer Engineering 6
응용프로그램전체를관리 초기화, 실행, 종료담당 프레임창생성 MFC 응용프로그램 CWinAPP 클래스 CWinAPP 로부터파생된클래스의오브젝트를오직하나만가져야함 반드시전역변수여야함 가장간단한 MFC 프로그램 #include <afxwin.h> CWinApp app; 프로젝트 - ( 맨밑에 ) 속성 구성속성 - 일반 Department of Computer Engineering 7
CWinAPP 클래스 ( 계속 ) 메시지루프 ( 무한루프 ) InitInstance(); Run(); ExitInstance(); CWinApp 프로그램시작 InitInstance( ) 무한루프 Run( ) ExitInstance( ) 프로그램종료 Department of Computer Engineering 8
멤버함수 Override CWinApp 파생클래스의동작 CWinApp 파생클래스를만들어야함프로그램시작 상속 CMyApp InitInstance( ) InitInstance( ) 무한루프 Run( ) ExitInstance( ) 프로그램종료 Department of Computer Engineering 9
CWinApp 파생클래스의동작 CWinApp 프로그램시작 상속 CMyApp InitInstance( ) InitInstance( ) 무한루프 Run( ) ExitInstance( ) ExitInstance( ) 프로그램종료 Department of Computer Engineering 10
사용자인터페이스클래스 사용자에게나타나는영역을갖는클래스 윈도우클래스 CWnd: 다른창클래스들에대한기반클래스로서메시지들에대한기본핸들러를제공 CFrameWnd: 프레임윈도우관리및자식윈도우관리 CControlBar: 도구모음, 상태표시줄, 기타컨트롤표시줄클래스의기반클래스 CDialog: 다이얼로그처리 CView: Document/View 모형의응용프로그램에서클라이언트영영관리 (Document 데이터출력및사용자입력처리 ) 그래픽클래스 CDC: 디바이스컨텍스트클래스들에대한기반클래스로서그래픽기능제공 CGdiObject: GDI (Graphic Device Interface) 오브젝트 ( 펜, 브러시, 폰트등 ) 의기반클래스 메뉴클래스 CMenu: 메뉴관리 Department of Computer Engineering 11
MFC Classes Department of Computer Engineering 12
실습목적 : MFC 의기본동작원리이해 MFC 뼈대이해실습 #include <afxwin.h> CWinApp app; 1. F10 눌러서 Debug MFC 가제공하는 Entry Function 은 _twinmain() AfxWinMain() CWinApp 의오브젝트얻음 Document/view 초기화 ( 주로재정의안함 ) AfxWinMain() 호출 2. F11 두번눌러서 AfxWinMain() 함수살펴볼것 3. Find Source 대화상자가나오면 Cancel 클릭, ~F5 누르고, Debug 탭살펴볼것 CWinApp::Run( ) 에서출력 인스턴스초기화를위해주로재정의 메시지를처리하다가 WM_QUIT 을만나면 ExitInstance( ) 호출 Department of Computer Engineering 13
MFC 뼈대이해실습 ( 계속 ) #include <afxwin.h> class MyApp : public CWinApp public: virtual BOOL InitInstance(); }; BOOL MyApp::InitInstance() AfxMessageBox(" 파생클래스의 InitInstance() Overriding"); } return TRUE; MyApp app; CWinApp 클래스의기반클래스는 CWinThread 클래스 CWinThread::InitInstance() 에서 Overriding 된함수가실행됨 (Debugging 하여확인 ) Department of Computer Engineering 14
MFC 뼈대이해실습 ( 계속 ) #include <afxwin.h> class MyApp : public CWinApp public: virtual BOOL InitInstance(); }; MS-Windows 운영체제관련기본적인동작 ( 프레임 ) class CMainWnd : public CFrameWnd public: CMainWnd(); }; BOOL MyApp::InitInstance() m_pmainwnd = new CMainWnd; m_pmainwnd->showwindow(m_ncmdshow); m_pmainwnd->updatewindow(); // WM_PAINT 메시지발생 } return TRUE; CMainWnd::CMainWnd() Create(NULL, " 윈도우시스템프로그래밍 "); } MyApp app; Department of Computer Engineering 15
MFC 뼈대이해실습 ( 계속 ) < 전략 > CMainWnd::CMainWnd() CString wc = AfxRegisterWndClass(0, AfxGetApp()->LoadStandardCursor(IDC_WAIT), (HBRUSH)::GetStockObject(LTGRAY_BRUSH), AfxGetApp()->LoadStandardIcon(IDI_QUESTION)); } Create(wc, " 윈도우시스템프로그래밍 ", WS_OVERLAPPED WS_CAPTION WS_SYSMENU WS_MINIMIZEBOX WS_HSCROLL WS_VSCROLL, CRect(0, 0, 200, 200)); MyApp app; Department of Computer Engineering 16
MFC 프로그래밍 CObject CCmdTarget 파생클래스 오직한개의 Global Object CWinApp 1.Inherit MyApp 2. Function Overriding CDocument 1.Inherit CMyDocument 2. Function Overriding CWnd CFrameWnd 1.Inherit CMainWnd 2. Function Overriding CView 1.Inherit CMyView 2. Function Overriding Department of Computer Engineering 17
Message Event 정보를프로그램에서다룰수있도록구조화 MSG 구조체로정의 윈도우핸들, 메시지식별번호, 추가정보, 시각, 커서위치포함 메시지종류 시스템정의메시지 (0x0000~0x7fff) 운영체제가이미식별번호를예약 사용자정의메시지 (0x8000~0xffff) 사용자가추가로식별번호할당 메시지식별매크로 (winuser.h 등 ) Prefix 버튼컨트롤 (BN_), 에디트컨트롤 (EN_), 리스트박스컨트롤 (LBN_) 등이있으나일반윈도우메시지 (WM_) 가가장많이사용됨 WM_CREATE (0x0001) : 창이생성될때 WM_LBUTTONDOWN: 왼쪽마우스버튼누를때 typedef struct tagmsg HWND hwnd; UINT message; WPARAM wparam; LPARAM lparam; DWORD time; POINT pt; } MSG; Department of Computer Engineering 18
발생즉시처리되는메시지 대부분의윈도우관리메시지 Message 처리 WM_CREATE, WM_DESTROY 등 메시지큐사용 우선순위가낮은메시지 FIFO (First in, First Out) Message Loop CWinApp::Run() 함수는 CWinThread::Run() 함수호출 WM_QUIT 을만날때까지 GetMessage(), TranslateMessage(), DispatchMessage() 함수를반복수행 사용자클릭 윈도우 운영체제 응용프로그램 B 의메시지큐에삽입 응용프로그램 B 의메시지루프에서처리 응용프로그램 B 클릭 응용프로그램 A Department of Computer Engineering 19
Message Map 을이해하기위한함수포인터 CALC_ENTRY 식별변호및함수포인터로구성 마지막에 NULL Entry 삽입 많은키보드중에서 a, s, m 만사용 int add() return 5 + 3; } int sub() return 5-3; } int mul() return 5 * 3; } struct CALC_ENTRY char id; int (*funcptr)(); }; CALC_ENTRY _functionmap[] = 'a', &add }, 's', &sub }, 'm', &mul }, 0, 0 } // NULL Entry }; void CFuncPtrDlg::OnOK() // TODO: Add extra validation here UpdateData(TRUE); if (!m_input.compare("")) return; char input = m_input[0]; int i = 0; while (_functionmap[i].id!= 0) if (_functionmap[i].id == input) m_output.format("%d", _functionmap[i].funcptr()); break; } else i++; } if (_functionmap[i].id == 0) m_output = "Not Found"; UpdateData(FALSE); // CDialog::OnOK(); } Department of Computer Engineering 20
Message Map 사용 MFC 의 AppWizard 가모두처리해줌 1. 메시지맵을헤더파일 (.h) 의클래스에선언 class C[ 프로젝트명 ]View : public CView #define afx_msg // Generated message map functions protected: //AFX_MSG(C[ 프로젝트명 ]View) afx_msg void OnLButtonDown(UINT nflags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; 2. 소스파일 (.cpp) 의윗부분에메시지맵정의 BEGIN_MESSAGE_MAP(C[ 프로젝트명 ]View, CView) //AFX_MSG_MAP(C[ 프로젝트명 ] View) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() 3. 메시지핸들러구현 void C[ 프로젝트명 ]View::OnLButtonDown(UINT nflags, CPoint point) // 코드작성 CView::OnLButtonDown(nFlags, point); } 메시지핸들러임을 explicitly 표시 ( 내용은없음 ) // intentional placeholder Department of Computer Engineering 21
Message Handler 를잘못넣어삭제할때 앞페이지의 3 군데를삭제 ( 또는주석처리 ) 해야함 1. 헤더파일 (.h) 의클래스에선언된부분삭제 class C[ 프로젝트명 ]View : public CView // Generated message map functions protected: //AFX_MSG(C[ 프로젝트명 ]View) afx_msg void OnLButtonDown(UINT nflags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; 2. 소스파일 (.cpp) 의윗부분에메시지맵정의에서삭제 BEGIN_MESSAGE_MAP(C[ 프로젝트명 ]View, CView) //AFX_MSG_MAP(C[ 프로젝트명 ] View) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() 3. 메시지핸들러메소드삭제 void C[ 프로젝트명 ]View::OnLButtonDown(UINT nflags, CPoint point) // 코드작성 CView::OnLButtonDown(nFlags, point); } WM_LBUTTONDOWN 을잘못넣었을때 Department of Computer Engineering 22
Message Map Message Map 작동원리 메시지식별번호, Function Pointer 등을포함한 (Array 형태의 ) Mapping Table 1. Message Map 선언내부 1. 마우스오른쪽버튼클릭 _messageentries: 각메시지종류에따라서메시지식별번호, Function Pointer 등을가지는엔트리의 Array messagemap: 기반클래스의 messagemap 에대한 Pointer 및현재클래스의 _messageentries 의 Array 로구성 현재클래스의메시지핸들러검색에서찾지못했다면기반클래스의메시지핸들러검색 DECLARE_MESSAGE_MAP() 2. 선택 private: static const AFX_MSGMAP_ENTRY _messageentries[]; protected: static AFX_DATA const AFX_MSGMAP messagemap; virtual const AFX_MSGMAP* GetMessageMap() const; struct AFX_MSGMAP_ENTRY UINT nmessage; UINT ncode; UINT nid; UINT nlastid; }; struct AFX_MSGMAP const AFX_MSGMAP* pbasemap; const AFX_MSGMAP_ENTRY* lpentries; }; // windows message // control code or WM_NOTIFY code // control ID (or 0 for windows messages) // 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) Department of Computer Engineering 23
Message Map 작동원리 ( 계속 ) 2. Message Map 정의내부 BEGIN_MESSAGE_MAP() GetMessageMap 함수구현 Static Member Variable 인 messagemap 에 value 할당 Static Member Variable인 _messageentries 변수정의및초기화를위한처음부분 ( = ) 전개 BEGIN_MESSAGE_MAP(C[ 프로젝트명 ]View, CView) ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() 메시지핸들러등록 (ON_WM_LBUTTONDOWN()) 메시지식별번호, 메시지핸들러함수포인터등의정보를가지고 Array에메시지처리를위한엔트리삽입 END_MESSAGE_MAP() Array 의끝을식별하기위한 NULL 엔트리삽입 #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 ON_WM_LBUTTONDOWN() \ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT,CPoint)) &OnLButtonDown }, #define END_MESSAGE_MAP() \ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ Department of Computer Engineering 24
처리순서 Message Map 처리과정 1. 현재클래스의메시지핸들러찾아보고있으면핸들러함수호출 2. 없으면기반클래스의메시지핸들러찾음 3. 없으면기반클래스의기반클래스의메시지핸들러찾음 ( 반복 ) 4. 그래도없으면 CWnd::DefWindowProc() 을호출하여 Default 메시지처리 e.g., WM_MINIMIZE CObject CCmdTarget CWinApp CDocument CWnd 3 4 DefWindowProc() CFrameWnd 2 CView 1 CMyView Department of Computer Engineering 25
Message Map vs. 가상함수 Message Map 은메시지핸들러의엔트리수만큼만메모리를사용하므로효율적인방법 가상함수의재정의아님 가상함수는동적바인딩을위하여 run-time 시에정보를유지 응용프로그램에서메시지처리를재정의하는경우가일반적으로적음 메모리의효율적사용을 ( 낭비를없애기 ) 위하여 Message Map 활용 Message Map Virtual Function NULL Department of Computer Engineering 26
Window Message 시스템정의 Message 종류 윈도우에서발생하는메시지 (e.g., WM_LBUTTONDOWN) 메시지핸들러함수의이름및원형이미리결정되어있다. e.g., afx_msg void OnLButtonDown(UINT nflags, CPoint point); Control Notification Message 컨트롤이부모윈도우에게통지하는메시지 (e.g., BN_CLICKED) 메시지핸들러함수의이름은프로그래머가임의로부여할수있지만인자와리턴타입은규칙준수 Command Message 메뉴, 가속기, 도구모음등의사용자인터페이스에서발생시키는메시지 (e.g., WM_COMMAND) Department of Computer Engineering 27