'전체 글'에 해당되는 글 171건

  1. 2007.08.20 블로그를 등록시켜 볼까?
  2. 2007.08.17 작업관리자가 응답없음을 판별하는 방법
  3. 2007.08.17 [강좌] WTL로 프로그래밍하기#5 – Message Map(2) 1
  4. 2007.08.17 [강좌] WTL로 프로그래밍하기#4 – Message Map(1)
  5. 2007.08.14 [강좌] WTL로 프로그래밍하기#3 – Mix-in class

블로그를 등록시켜 볼까?

사회&문화 2007. 8. 20. 11:37

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

 

 

이곳을 각종 포털에 등록을 해보려고 하였습니다.

일단 포털로 검색을 해서 나오는 리스트는 아래와 같습니다
.

네이버

야후
다음
파란
엠파스
네이트닷컴
드림위즈
프리챌
...

위의 8개정도에 등록을 시도하였습나다. 전반적으로 등록은 무료로 이루어 지고 있구요, 로그인을 필요로 하는 곳이 많았습니다. 등록하면서 로그인을 유도하다니.. 사실 조금 귀찬았는데, 등록을 하는 사람이 필요해서 하는 것이니 어쩔 없더군요.. 등록하기 편리한곳은 네이버와 야후였습니다.
다음은 각각 싸이트에 등록하면서 느낀점을 적어 보았습니다
.

네이버 - 등록 신청

네이버에 등록을 하려고 하니 회원 가입이 필요없이 무료로 등록이 가능했습니다.
페이지 하단에 있는 "홈페이지등록" 눌러서 차례대로 입력하면 되더군요
..
일반심사 과정을 통해서 등록을 하면 되더군요.. 역시 편리한 네이버
..

야후 - 등록 신청

야후도 회원 가입이 필요없이 네이버와 비슷하게 일반심사과정을 통해서 등록이 가능했습니다.
글로벌이라서 그런지 여기도 편리하군요
..

다음 - 등록 포기

다음에등록을 하려고 하니.. 이런 일반빠른심사 특급심사.. 전부 유료인가? 아니면 내가 못찾은 건가? 애드하우인지 뭔지 이런거만 있고 무료로 등록이 않되나 보내.. 찾다가 못찾아서 등록 포기

파란 - 등록 신청
가입을 하라고 하내요.. 예전에 가입해둔 아이디가 있어서 로그인을 하고나니 일반심사가 숨어있었습니다. 찾아서 일반등록에 신청 완료..

엠파스&네이트 - 등록 신청

여기도 일반등록이 있어서 로그인을 하라고 하고, 주소는 왜물어 보는건지.. 어째든 등록 완료..
엠파스와 네이트가 같은 회사였군요.. 별로 관심이 없어서 이제 알게 된네요
..

드림위즈 - 등록 신청

여기도 로그인일 필요하군요.. 그리고 주소도 물어보고.. 아무튼 등록절차는 간단합니다.

아 그리고 데브피아에 강좌도 올려서 PR을 하고 있습니다. 덕분에 방문자가 조금씩 생기네요~ ^^
 



 



:

작업관리자가 응답없음을 판별하는 방법

개발 2007. 8. 17. 14:53

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

예전에 어디에서가 비슷한 내용을 본적이 있다. 그때 본 내용은 WM_GETICON 이라는 메시지를 보내서 바로 응답이 오면 프로그램이 작동중이고, 응답이 않오면 프로그램이 멈춘상태(응답없음)라는 것이다. 당연히 메시지를 보냈는데, 응답이 않오면 작업관리에서 보이는 것처럼 응답없음 상태로 보는 것이다.

  그런에 이와 유사한 기능을 하는 api가 있었다. 바로 IsHungAppWindow 라는 함수이다. 그래서 테트스겸 사용을 해볼려고 하니까 아래처럼 링크에러가 발생한다.

WindowsAPIDlg.obj : error LNK2019: __imp__IsHungAppWindow 외부 기호("protected: virtual int __thiscall CWindowsAPIDlg::OnInitDialog(void)" (?OnInitDialog@CWindowsAPIDlg@@MAEHXZ) 함수에서 참조)를 확인하지 못했습니다.
Debug/WindowsAPI.exe : fatal error LNK1120: 1개의 확인할 수 없는 외부 참조입니다.

이럴수가!!  분명히 msdn에서 말하기 아래처럼 정보가 있었는데..

Minimum DLL Version user32.dll
Header Declared in Winuser.h, include Windows.h
Import library User32.lib
Minimum operating systems Windows 2000

허허 참 이상하다..  그래서 검색을 조금 해보니까.. user32.dll을 직접 로딩해서 사용해라는 것이다. 허허.. msdn 이거 않되겠구만~ 엉터리 정보를 가지고 있다니..
그래서 할 수 없이 아래처럼 직접 로딩해서 사용하면 된다.

typedef BOOL (WINAPI *PROC_IsHangAppWindow)(HWND);
PROC_IsHangAppWindow IsHangAppWindow;
HMODULE hUser32 = GetModuleHandle("user32");
IsHangAppWindow = (PROC_IsHangAppWindow) GetProcAddress(hUser32, "IsHungAppWindow");

BOOL b = IsHangAppWindow(hWnd);

확인결과 잘 작동한다. 리턴값은 응답없음이면 TRUE 를 리턴한다. 
단점은 윈도우2000 이상에서만 된다는..



:

[강좌] WTL로 프로그래밍하기#5 – Message Map(2)

개발 2007. 8. 17. 11:59

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

WTL로 프로그래밍하기#5 – Message Map(2)

 

저자 : hanburn

날짜 : 2007.08.18

환경 : WTL7.5, VS-2003

 

 

지난시간에 WM_XXX 메시지 핸들러를 알아 보았는데, 이번에는 나머지에 대해서 알아보자.

일단 WM_COMMAND 메시지에 해당하는 핸들러로 COMMAND_HANDLER 가 있다.

 

 

BEGIN_MSG_MAP(CListDlg)

        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel)

        COMMAND_HANDLER(IDOK, BN_CLICKED, OnBnClickedOk)    

END_MSG_MAP()

 

 

COMMAND_HANDLER 3개의 인자를 받는데, 각각 id, code, func 이다. Id 어떤 컨트롤(혹은 매뉴 id)에서 발생된 것인지를 나타내고, code 명령의 세부사항이다. code 각각 컨트롤 마다 다르기 때문에 이것또한 (MFC 비해서)번거로운 일이다. 일일이 msdn 참고해야 하기 때문이다. Win32 코딩을 했던 사람들은 익숙하겠지만, MFC 개발자에게는 낯설 수가 있다.

COMMAND_HANDLER 정의를 한번 보도록 하자.

 

#define COMMAND_HANDLER(id, code, func) \

if(uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) \

{ \

        bHandled = TRUE; \

        lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \

        if(bHandled) \

               return TRUE; \

}

 

별로 특별한 것인 없다. ㅋㅋ

 

유사한 것으로는 COMMAND_ID_HANDLER COMMAND_CODE_HANDLER가 있는데, 차이점은 이름에서 알 수 있듯이 전자는 ID에 매칭이 되고, 후자는 CODE에 매칭이 되는 핸들러 이다. 각각의 매크로 정의를 보면 쉽게 알 수 있다.

 

 

#define COMMAND_ID_HANDLER(id, func) \

if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \

{ \

        bHandled = TRUE; \

        lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \

        if(bHandled) \

               return TRUE; \

}

 

#define COMMAND_CODE_HANDLER(code, func) \

if(uMsg == WM_COMMAND && code == HIWORD(wParam)) \

{ \

        bHandled = TRUE; \

        lResult = func(HIWORD(wParam), LOWORD(wParam), (HWND)lParam, bHandled); \

        if(bHandled) \

               return TRUE; \

}

 

쉽게 설명하면 COMMAND_ID_HANDLER 는 코드는 무시하고 ID가 매칭되면 해당 핸들러를 호출해주는 것이고 COMMAND_CODE_HANDLER ID는 무시하고 코드가 매칭되면 해당 핸들러를 호출해주는 것이다. 3개를 섞어서 사용하다 보면 이상한 경우가 생길수가 있다. 아래의 예를 보면..

 

BEGIN_MSG_MAP(CListDlg)

        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel)

        COMMAND_ID_HANDLER(IDOK, OnCommandOK)

        COMMAND_CODE_HANDLER(BN_CLICKED, OnCommandBnClicked)

ENG_MSG_MAP

 

위의 설명에 앞서서 메시지 맵에 들어있는 매크로의 순서대로 위에서부터 검사를 한다는 것을 알아야 한다. 위에서 cancel 버튼을 클릭한다고 하면 위에서부터 검사해서 첫번째에 걸리므로 OnBnClickedCancel이 호출되고, OK 버튼을 클릭했을때는 두번째에 걸리므로 OnCommandOK가 호출이 되는 것이다. 다른 버튼이 있다고 할 때, 그 버튼을 클릭하면 세 번째에 걸리므로 OnCommandClicked가 호출이 되는 것이다.

 

COMMAND 메크로 중에서 범위를 지정하는 것은 2개밖에 없다. COMMAND_RANGE_HANDLER COMMAND_RANGE_CODE_HANDLER 이렇게 두개가 있는데, 전자는 ID 범위를 받고, 후자는 코드 범위를 받는다.

 

 

그럼 이제 NOTIFY에 대해서 알아보도록 하자. NOTIFY 핸들러는 WM_NOTIFY메시지를 처리하는데 WM_COMMAND에서 NOTIFY로만 변경된거 외에는 메시지 핸들러는 똑같다. 똑같이 NOTIFY_HANDLER, NOTIFY_ID_HANDLER, NOTIFY_CODE_HANDLER, NOTIFY_RANGE_HANDLER, NOTIFY_RANGE_CODE_HANLDER 가 존재한다. 다른점은 핸들러 함수의 파라미터가 다르다.

 

BEGIN_MSG_MAP(CListDlg)

NOTIFY_HANDLER(IDC_FILE_LIST, NM_CLICK, OnNMClickFileList)

END_MSG_MAP()

 

LRESULT OnNMClickFileList(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/);

 


두번째 파라미터가 각각의 상황별로 포인터 변형되어 사용되는 것을 다 알 것 이니까,
이제는 MFC와 좀 다른 것(?) 들을 알아보도록 하자.

 

BEGIN_MSG_MAP(CListDlg)

REFLECT_NOTIFICATIONS()

        CHAIN_MSG_MAP(CBaseClass)

END_MSG_MAP()

 

위와 나온 매크로도 자주 쓰이는 것들이니까, 알아 두어야 한다. 먼저 CHAIN_MSG_MAP은 말그대로 메시지 맵에 체인을 걸어주는 것이다. 무슨말인고 하면,  CListDlg가 다른 클래스로부터 상속된 클래스라고 하면 베이스 클래스로 메시지 체인을 걸어주게 되는 것이다. CHAIN_MSG_MAP() 매크로가 맨 위에 있다면 메시지들은 일단 베이스 클래스에서 해당 메시지의 핸들러가 있는지 확인을 하고 없으면, CListDlg에서 처리를 하게되고 반대로 CHAIN_MSG_MAP 매크로가 메시지 맵의 마지막에 있으면, CListDlg에서 처리하지 않은 메시지 들에 대해서 베이스 클래스로 전달을 하는 역할을 하게 된다. 이 매크로는 상속관계에서 많이 쓰이고, Mix-in Template 클래스를 사용할 때도 사용해야 한다.

 

다음으로 REFLECT_NOTIFICATIONS() 매크로는 이름에서도 알수 있듯이 NOTIFY reflet 해주는 역할이다. 차일드 컨트롤 윈도우에서 발생하는 notify 메시지에 대해서 차일드 윈도으로 다시 보내주는 역할을 하는 것이다. MFC에서는 =NM_ 계열의 핸들러라가 있는 것을 알것이다. 이것이 바로 notify 메시지를 차이드가 자체적으로 처리하는대, WTL에서는 부모에서 위의 메크로를 써주어야 차일드에서 notify를 처리할 수 있는 것이다.

읽어 주셔서 감사합니다~



:

[강좌] WTL로 프로그래밍하기#4 – Message Map(1)

개발 2007. 8. 17. 11:53

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

WTL로 프로그래밍하기#4 – Message Map(1)

 

저자 : hanburn

날짜 : 2007.08.17

환경 : WTL7.5, VS-2003

 

 

 

WTL에서 메시지 처리는 MFC와 유사하게 메시지 매크로를 이용하는데, 조금 다른 점이 라면 메시지 매크로가 헤더파일에 있다는 점입니다. 위치만 변경된 것이므로 뭐 크게 다를건 없구요..

메시지 맵은 BEGIN_MSG_MAP(클래스이름) / END_MSG_MAP() 매크로를 사용하여 MFC와 유사하게 처리합니다.

 

WTL에서는 메시지 종류에 따라서 크게 3가지로 분류하여 처리를 합니다.

 

WM_COMMAND

WM_NOTIFY

WM_계열의 일반 메시지

 

그럼 하나하나씩 알아보도록 하겠습니다. 먼저 일반 메시지 처리부터 살펴보도록 하죠.. WM_ 계열의 일반 메시지는 MESSAGE_HANDLER() 매크로를 이용하여 처리를 합니다. 함수 원형은 다음과 같다.

 

BEGIN_MSG_MAP(CMyDlg)

           MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

END_MSG_MAP()

 

LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

 

 

일반적인 메시지 핸들러 함수는 위에처럼 4개의 인자를 가지고 있는데, 첫번째 uMsg는 처리하는 메시지에 해당하구요, wParam, lParam은 각 메시지의 전달되는 값 그대로 이고, 마지막으로 bHandled는 해당 핸들러에서 메시지를 처리했는지 않했는지를 표시해준다. 예를 들어서 해당 메시지를 처리하고나서 ATL의 기본 WindowProc() 함수에서 해당 메시지를 처리하게 하려면 bHandled 값을 FALSE로 설정하면 된다. 이게 무슨 말인가?? 쉽게 설명하면 다음의 예를 보자

 

// MFC 버전

void CMyDialog::OnMouseMove(UINT nFlags, CPoint point)

{

        ...     // 어떤처리

        __super::OnMouseMove(nFlags, point);

}

// WTL 버전

LRESULT OnMouseMove (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

        ...     // 어떤처리

        bHandled = FALSE;

}

 

bHandle FALSE로 해주면 WM_MOUSEMOVE 메시지를 CMyDialog에서 처리를 않했다고 표시하는 것이다. 그래서 WM_MOUSEMOVE 메시지가 WindowProc으로 전달이 되는 것이다. MFC 에서는 WM_MOUSEMOVE 이벤트를 처리하고 베이스 클래스의 OnMouseMove를 호출해주는 것과 비요해서, WTL에서는 베이스 클래스의 가상함수를 호출하는 역활을 bHandled 변수가 담당하게 되는 것이다.

그럼 좀더 자세하게 MESSAGE_HANDLER의 정의부를 살펴보자.

 

#define MESSAGE_HANDLER(msg, func) \

if(uMsg == msg) \

{ \

        bHandled = TRUE; \

        lResult = func(uMsg, wParam, lParam, bHandled); \

        if(bHandled) \

               return TRUE; \

}

 

역시나 정의된 것을 직접 보는 게 제일 편하다. ㅋㅋ 해당 함수를 호출하기 전에 bHandled true로 설정하고 함수를 호출하고 나서 bHandled를 체크하여 return TRUE를 해주고 있구나. 그렇다면 WTL에서는 LRESULT 값이 무시가 되는 건가? 그렇다 현재 7.5 버전에서는 무시를 하고 있다. (END_MSG_MAP 정의 부분을 보면 알 수 있다.)

 

그리고 정의부를 본 김에 BEGIN_MSG_MAP과 함께 전체를 풀어보면 아래와 같다.

 

// BEGIN_MSG_MAP() 부분

public: \

BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \

{ \

        BOOL bHandled = TRUE; \

        (hWnd); \

        (uMsg); \

        (wParam); \

        (lParam); \

        (lResult); \

        (bHandled); \

        switch(dwMsgMapID) \

        { \

        case 0:

 

// MESSAGE_HANDLER()부분

if(uMsg == msg) \

{ \

                bHandled = TRUE; \

                lResult = func(uMsg, wParam, lParam, bHandled); \

                if(bHandled) \

                        return TRUE; \

}

 

// END_MSG_MAP 부분

        break; \

        default: \

        ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)\n"), dwMsgMapID); \

        ATLASSERT(FALSE); \

        break; \

        } \

return FALSE; \

}

 

이렇게 메시지 메크로를 풀어보니까 그냥 메시지에 따라서 if문으로 구성되어 있는 간단한 처리방식인데, 사용하기 편하게 매크로로 정의를 한 것 뿐이다. 요건 MFC도 마찬가지다. ^^

 

WTL에서는 MFC와 다르게 메시지를 unpack을 않해주고 있기 때문에, 직접 WPARAM, LPARAM에서 원하는 정보를 추출하여 사용하여야 한다. 이것이 조금 불편한 점이다. <atlcrack.h> 헤더파일을 추가해주면 MFC와 비슷하게 전달되는 파라미터를 각각의 메시지에 맞게 변환해준 값을 전달해 주는데, 이것도 조금 불편하다. 그 이유를 설명하면, 다음의 unpack을 사용하는 WTL의 메시지 매크로 모습을 보자.

 

BEGIN_MSG_MAP_EX(CMiniNewsWnd)

        MSG_WM_CREATE(OnCreate)

        MSG_WM_LBUTTONUP(OnLButtonUp)

        MSG_WM_MOUSEMOVE(OnMouseMove)

END_MSG_MAP()

 

LRESULT        OnCreate(LPCREATESTRUCT lpcs);

void           OnLButtonUp(UINT nID, CPoint pt);

void           OnMouseMove(UINT nFlag, CPoint pt);

 

이것만 보면 어랏! MFC와 똑같네~’ 라고 할 수 있는데, 맞다 똑같다. 우리가 쓰던인자 그대로 뽑아서 넘겨준다. 그런데 왜 불편하다는 거야? 라고 물어보면 대답은 다음과 같다. MFC에서는 해당 메시지마다 위자드가 자동으로 핸들러를 추가해주지만, WTL에서는 위자드를 통해서 핸들러를 추가하게 되면 기본적인 MESSAGE_HANDLER 형식으로 추가를 해주기 때문이다. MSG_WM_ 형식은 수동으로 추가를 해야 하는데, 누가 각각의 메시지의 파라미터를 다 외우고 다니나? 그래서 추가를 하려면, 해당 MSG_WM_XXX의 정의를 보고 추가해주어야 한다. 이때 또 난감한 것이 있으니 다음과 같은 경우이다.

 

 

#define MSG_WM_SETCURSOR(func) \

if (uMsg == WM_SETCURSOR) \

{ \

        SetMsgHandled(TRUE); \

        lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \

        if(IsMsgHandled()) \

               return TRUE; \

}

 

WM_SETCURSOR 핸들러를 추가하기 위해서 MSG_WM_SETCURSOR의 정의를 찾아본다. 하지만, 두번째, 세번째 인자는 lParam의 low/hi 워드를 표기 사는데 이것의 의미를 알기 위해서는 msdn을 참고해야 한다. , 각각 메시지 마다 파라미터의 의미를 대 외우지 않으면 msdn을 찾아야 하는 것은 필수이기 때문이다.

따라서 MESSAGE_HANDLER MSG_WM_XXX 중에서 맘에 드는 것을 선택적으로 사용하기 바란다. 참고로 필자는 두 개를 섞어서 사용한다.

 

그리고 MESSAGE_RANGE_HANDLER로 있다. 메시지의 범위를 처리하는 것인데, 사용자 정의 메시메시지 처리할 때 유용하다. 뭐 이건 MFC를 사용해 본 사람은 다 알고 있으리라 생각하고 넘어간다.

 

오늘은 여기서 짜르고 다음 시간에 나머지 부분에 대해서 알아보도록 하겠습니다.
읽어 주셔서 감사합니다~


PS: 이 글에대해서 링크 및 퍼가는 것을 허용합니다. 그러나 꼭 출처를 밝혀 주시기 바랍니다.



:

[강좌] WTL로 프로그래밍하기#3 – Mix-in class

개발 2007. 8. 14. 10:00

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

WTL로 프로그래밍하기#3 – Mix-in class

 

저자 : hanburn

날짜 : 2007.08.14

환경 : WTL7.5, VS-2003

 

 

지난 시간에 위자드를 통해서 생성되는 코드들을 살펴 보았습니다. MainDlg의 코드를 살펴보다가 이번시간으로 내용을 넘겼는데, 그 이유는 먼저 살펴 볼 내용이 있어서 입니다. 그럼 시작도록 하겠습니다.

 

 

템플릿에 익숙하지 않은 분들이 계시다면 부지런히 공부를 하시기 바랍니다. ㅎㅎ 이번에는 template의 한가지 사용예제를 먼저 살펴보자. C++ 표준에서는 클래스의 상속리스트로 해당 클래스를 사용 할 수가 있다. 이게 무슨 말인가? 쉽게 아래처럼 사용이 가능하다는 것이다.

 

template <class T>

class B1

{

public:

        void SayHi()

        {

               T* pT = static_cast<T*>(this);

               pT->PrintClassName();

        }

 

        void PrintClassName()

        {

               cout<< "This is B1" << endl;

        }

};

 

class D1 : public B1<D1>

{

 

};

 

class D2 : public B1<D2>

{

public:

        void PrintClassName()

        {

               cout<< "This is D2" << endl;

        }

};

 

 

Class D1 : public B1<D1> 이렇게 템플릿클래스에 자신을 매개변수로 해서 상속을 받을 수가 있는 것이다. 이런 방법은 ATL, WTL에서 자주 사용되는 방법이므로 유의깊게 보기 바란다. 물론 이미 알고 있는사람들은 그냥 대충 넘어가거나 잘못된 내용이 있으면 태클을 걸어주길 바랍니다.^^

D1 SayHi()를 호출하면 D1 PrintClassName을 오버라이드하지 않았기 때문에 베이스인 B1 PrintClassName이 호출이 되고, D2 SayHi()를 호출하면 D2 PrintClassName이 오버라이드 되어서 D2 PrintClassName이 호출되는 것이다. 이건 바로 C++의 다형성을 구현하는 가상함수의 개념이 아닌가? 그런데 한가지 다른 점이 있다. 각각 실행될 때 어떻게 되는지 자세히 살펴보자.

 

먼저 D1의 경우에는 템플릿이 아래처럼 컴파일 시간에 확장될 것이다.


void B1<D1>::SayHi()

{

D1* pT = static_cast<D1*>(this);

 

    pT->PrintClassName();

}

 

그리고 D2의 경우에는 템플릿이 아래처럼 컴파일 시간에 확장될 것이다.


void B1<D2>::SayHi()

{

D2* pT = static_cast<D2*>(this);

 

    pT->PrintClassName();

}

 

중요한 점은 컴파일 시간에 어떤 함수가 불려질지 결정이 된다는 점이다. ? 가상함수는 실행시간에 다형성이 결정되는데, 컴파일 시점에 결정된다면 오히려 안좋은 것이 아닌가? 라고 의문을 가질 수 있는데, 반대로 컴파일 시점에 호출되는 함수가 결정된다는 것은 다음과 같은 이점이 있다.

1.     객체의 포인터를 필요로 하지 않는다.

2.     Vtbls 가 없어서 메모리가 절약되고 수행시간이 더 빠르다.

3.     Vtbl이 초기화되지 않아서 발생할 수 있는 문제점들이 없다. (널참조 같은)

4.     컴파일 시간에 문제점들이 해결될 수 있다.

 

위의 예제는 가상함수를 사용하지 않고 tempalate을 이용하여 다형성을 구현한것이다. 참 흥미로운 부분이다.

 

아래의 예제를 하나 더보도록 하자

 

template<class T>

class CSingleton

{

public:

       

        static T* getInstance(VOID)

        {

               if( TRUE == ::IsBadReadPtr(m_pInstance, sizeof(T)) )

                       m_pInstance = new T();

 

               return m_pInstance;

        }

 

        static VOID releaseInstance(VOID)

        {

               if( FALSE == ::IsBadReadPtr(m_pInstance, sizeof(T)) )

                       delete m_pInstance;

 

               m_pInstance = NULL;

        }

       

 

private:      

        static T* m_pInstance;

};

 

// Init static member

template<class T> T* CSingleton<T>::m_pInstance = NULL;

 

위의 클래스는 디자인패턴에서 Singleton을 구현한 template 클래스이다. 사용은 아래처럼 하면 간단하게 해당 클래스가 Singleton클래스로 동작을 하게 된다.

 

CMyClass : public Singleton<CMyClass>

{

Public:

}

 

이런 CSingeton 같은 template 클래스를 Mix-in class 라고 한다. Mix-in class는 해당 기능을 가지고 있고, 다른 클래스에 템플릿 상속을 통해서 쉽게 기능을 추가 할 수 있는 클래스 이다. WTL에서는 이런 방식으로 템플릿 클래스의 상속을 통해서 기능을 붙이는 방식으로 설계가 되어 있다. 오 놀라워라~

 

~ 그럼 다시 우리의 MainDlg.h를 살펴보면, 여러 가지 기능들을 상속으로 추가하는 모습들을 볼 수 있는데, 하나하나씩 살펴보도록 하자.

 

class CMainDlg :       public CDialogImpl<CMainDlg>,

public CUpdateUI<CMainDlg>,

                       public CMessageFilter,

public CIdleHandler

{

 

 

먼저 CDialogImpl 클래스다. 이것은 CDialog를 실제적으로 구현하고 있는 클래스로 아래와 같은 상속구조를 가지고 있다.

 

CMainDlg : CDialogImpl : CDialgImplBaseT : CWindowImplRoot : TBase, CMessageMap

 

Public을 빼고 그냥 베이스가 되는 클래스를 오른쪽으로 표시한 것이다. Dialog도 윈도우니까 기본적인 윈도우의 구현인 CWindowImpl에서 상속된 것들을 쭉 타고 내려온다. WTL은 인터페이스와 구현을 CWindow CWindowImlp 이라는 2개의 클래스로 구분을 하였다는 것 정도만 알고 넘어가도록 하자. 하부구조를 다 설명하려면 WTL Architecture라는 강좌를 통해서 하도록 하겠다.

 

그 다음으로는 CUpdateUI 라는 클래스를 살펴보자. 이 클래스는 메뉴 등의 UI의 상태를 관리해주는 역할을 하는 것으로, 내부적으로 UI요소들을 map으로 관리하면서, BEGIN_UPDATE_UI_MAP 매크로를 통해서 해당 map을 구성하게 된다. 그리고 UIAddMenuBar(), UIAddToolBar(), UIAddStatusBar() 등의 함수를 통해서 map을 설정하게 되고, UIEnable(), UISetCheck() 등의 함수를 통해서 UI를 관리하게된다. 이것은 나중에 SDI 형태의 프로그램을 볼 때 더 자세하게 다루도록 하겠다.

 

그 다음으로는 CMessageFilter 클래스이다. 이것은 클래스 이름에서 알수 있듯이 메시지를 필터링 할수 있는 인터페이스 클래스이다.

 

class CMessageFilter

{

public:

        virtual BOOL PreTranslateMessage(MSG* pMsg) = 0;

};

 

클래스를 상속 받으면 반드시 PreTranslateMessage 재정의 해주어야 한다. MFC Pretranslate 함수와 같은 역할을 한다. 메시지가 전달되기 전에 먼저 처리해주는 역확을 한다.

 

다음은 CIdleHandler 인데, 이것도 이름에서 바로 있듯이 OnIdle 핸들러를 제공하는 것이다.

 

class CIdleHandler

{

public:

        virtual BOOL OnIdle() = 0;

};

 

그런데 단순히 CMessageFilter CIdlHandler를 상속만 받으면 어떻게 PretranslateMessage OnIdle이 호출될까 라고 궁금하지 안은가? 그 원리는 OnInitialDialog 에 있는 코드에 있다.

 

// register object for message filtering and idle updates

CMessageLoop* pLoop = _Module.GetMessageLoop();

ATLASSERT(pLoop != NULL);

pLoop->AddMessageFilter(this);

pLoop->AddIdleHandler(this);

 

위의 코드가 메시지 루프에 각각을 추가해주기 때문에 메시지를 전달하는 과정에서 수를 부리는 것이다. 이것을 보면 바로 Pretranslate 에는 OnInitialDialog 다음에 오는 메시지만 Filtering을 할 수 있게 된다. WM_INITDIALOG 같은 메시지는 안잡히게 되는 것이다.


그럼 다음에는 메시지 맵에 대해서 알아보도록 하겠습니다. ^^



: