제가 Visual Studio 2010 공식 팀 블로그에 게시했던 글을 스크랩 합니다.


Intro
안녕하세요. MFC 카테고리의 꽃집총각 입니다.
우리는 지금 비주얼 스튜디오 2010에서 새롭에 추가되는 MFC 클래스인 CTaskDialog를 알아보고 있습니다.
지난 시간까지는 2회의 연재에 걸쳐서 태스크 대화상자가 무엇인지, 그리고 태스크 대화상자를 기존의 MessageBox 처럼 간단한 용도로 사용하고자 할 때엔 어떻게 해야 하는지를 함께 살펴보았습니다. 앞서 연재된 포스팅은 아래의 링크를 따라가시면 읽어보실 수 있습니다.

이번에는 태스크 대화상자의 세 번째 포스팅으로, 태스크 매니저가 제공하는 다양한 기능들을 본격적으로 활용하기 위한 방법을 알아보기로 하겠습니다.



태스크 대화상자를 띄우는 또다른 방법 - DoModal() 함수.
지난번 포스팅에서 소개했던 단순 출력 방식은 CTaskDialog::ShowDialog 함수 하나만 호출해 주면 바로 태스크 대화상자를 띄울 수 있었습니다. 8개의 인자를 통해 기본적인 기능을 어느 정도 제어할 수는 있었지만 애초부터가 심플한 사용을 위한 방법이었으므로, CTaskDialog의 여러가지 강력한 기능들을 모두 제어하기에는 무리가 있었습니다.
이번에는 CTaskDialog의 객체를 직접 생성하고, CTaskDialog의 멤버함수들을 통해 다양한 기능을 활용하는 방법을 알아보도록 하겠습니다. 이미 우리에게 익숙한 방식인 CDialog의 파생클래스를 출력하던 방식과 유사합니다. 객체를 만들어 DoModal() 함수를 호출하면 창이 뜨고, 창이 떠있는 동안 함수 내부에서 제어를 쥐고 있다가, 사용자가 창을 끄게 되면 리턴값을 내면서 함수의 호출이 종료되는 방식입니다.

// 객체 생성.
CTaskDialog dlg( L"본문", L"머릿글", L"타이틀" );

// TODO : DoModal을 호출하기 전, dlg의 멤버함수를 호출해 원하는 기능들을 설정한다.

INT_PTR iRet = dlg.DoModal();

switch( iRet )
{
case IDOK:        ... break;
case IKCANCEL:    ... break;
    ...
}

기존의 스타일을 그대로 닮아있어서 훨씬 더 친숙하게 느껴집니다 :)
다만 다른점은, DoModal을 부르기 전에 여러가지 기능을 제공하는 CTaskDialog의 멤버함수를 이용해 태스크 대화상자의 동작 및 기능을 런타임에 디자인 한다는 점입니다. 각각의 기능들은 적절한 이름의 함수로 제공되어 있으며, 매우 직관적이므로 별도의 설명 없이도 쉽게 이해하실 수 있습니다.
함수들의 레퍼런스는 아무래도 MSDN 페이지를 직접 보시는 것이 좋을 것 같습니다. ( http://msdn.microsoft.com/en-us/library/dd293651(VS.100).aspx ) 사실은 제가 한글로 설명을 덧붙인 함수 참고표를 정리해 봤는데, 굳이 부가설명이 필요 없을 뿐더러 오히려 MSDN 페이지보다 더 읽기 힘들어지기만 하네요. 대신 눈에 쏙쏙 들어오는 예제 코드를 통해 주요 함수들의 사용 방법에 대한 설명을 갈음하도록 하겠습니다.
예제는 MS VC++ MVP인 Marc Gregoire의 블로그에 공개된 예제 코드를 기반으로 하여, 주석과 인자들을 한글로 수정하고, 강의의 진행에 맞도록 일부 수정한 버전입니다. 원본 코드는 이곳 ( http://www.nuonsoft.com/blog/2009/06/10/ctaskdialog-in-mfc-in-visual-c-2010/ )에서 다운받으실 수 있습니다.

// 태스크 대화상자 객체 생성.
CTaskDialog taskDlg( L"본문", L"머릿글", L"타이틀" );

// 본문, 머릿글, 타이틀 텍스트 별도 지정
taskDlg.SetContent(_T("여기가 본문이 출력되는 위치입니다.\n")
    _T("당연히 여러 줄로 텍스트를 출력할 수 있고,")
    _T("<a href=\"http://vsts2010.net\">하이퍼 링크</a>도 설정할 수 있습니다.") );

taskDlg.SetMainInstruction(_T("여기는 머릿글을 적는 곳입니다.\n")
    _T("머릿글도 여러 줄로 적을 수 있어요."));

taskDlg.SetWindowTitle(_T("이것이 태스크 대화상자 입니다."));

// 3개의 커맨드 버튼 추가. 한 버튼은 권한 승격이 필요한 작업임을 표시.
taskDlg.AddCommandControl(1, _T("커맨드 버튼 1 "));
taskDlg.AddCommandControl(2, _T("커맨드 버튼 2 "));
taskDlg.AddCommandControl(3, _T("커맨드 버튼 3 \n")
    _T("사용자 권한 설정(UAC) 기능의 권한 승격이 필요함을 표시할 수 있습니다."), TRUE, TRUE);


// 2개의 라디오 버튼 추가.
taskDlg.AddRadioButton(4, _T("라디오 버튼 1"));
taskDlg.AddRadioButton(5, _T("라디오 버튼 2"));

// 보이기, 감추기 버튼으로 사용자가 펼치고 접을 수 있는 추가 메세지 설정.
taskDlg.SetExpansionArea(
    _T("이 메세지는 사용자가 하단의 '자세히' 버튼을 누르면 추가로 보여지는 메세지 입니다.\n")
    _T("이곳에도 하이퍼 링크가 적용됩니다. <a href=\"notepad.exe\">메모장 열기</a>."),
    _T("자세히"), _T("감추기"));

// 꼬릿말 부분 설명 & 아이콘 설정.
taskDlg.SetFooterIcon(MAKEINTRESOURCE(IDI_INFORMATION));
taskDlg.SetFooterText(_T("꼬릿말 출력 위치. \n역시 멀티라인 지원됩니다."));

// 대화상자의 메인 아이콘 설정.
taskDlg.SetMainIcon(m_hIcon);

// 태스크 대화상자의 프로그레스바 설정.
taskDlg.SetProgressBarMarquee();
taskDlg.SetProgressBarRange(0, 100);
taskDlg.SetProgressBarPosition(0);

// 대화상자 하단의 체크박스 표시 설정 및 텍스트 셋팅.
taskDlg.SetVerificationCheckboxText(_T("윈도우 시작시 자동 실행"));
taskDlg.SetVerificationCheckbox(TRUE);

// 하이퍼 링크, 타이머 옵션 추가 설정.
int options = taskDlg.GetOptions();
options |= TDF_ENABLE_HYPERLINKS;
options |= TDF_CALLBACK_TIMER;
taskDlg.SetOptions(options);


// 태스크 대화상자 출력.
INT_PTR iRes = taskDlg.DoModal();

// 사용자가 선택한 커맨드 버튼 ID와 라디오 버튼 ID 확인하기.
int iSelectedCommandControlID = taskDlg.GetSelectedCommandControlID();
int iSelectedRadioButtonID = taskDlg.GetSelectedRadioButtonID();

위의 예제 코드로 생성된 태스크 대화상자의 모습은 아래와 같습니다.

(그림 1) 예제 코드로 생성된 태스크 대화상자의 모습.


(그림 2) 예제코드로 생성한 태스크 대화상자. '자세히' 버튼을 눌러 본문이 확장된 상태.


제가 개인적으로 판단하기에는 주요함수 참고 테이블 보다는 위의 예제 코드가 훨씬 더 사용법을 익히기에 좋아 보입니다. 익스플로러 창을 두 개 띄워서 스크린샷과 소스코드를 대조해서 확인해 보세요. 어지간한 기능은 MSDN을 참고하지 않고도 바로 사용법을 이해하실 수 있을겁니다.



CTaskDialog의 프로그레스바 컨트롤.
이 부분까지 포스팅을 유심히 살펴오신 분이시라면 이상한 점을 한 가지 파악하셨을겁니다. 바로 프로그레스바 컨트롤 입니다. 지난 번 포스팅에서도, CTaskDialog::ShowDialog 함수를 이용해 프로그레스바 컨트롤을 억지로 붙여보기만 하고는 제어하는 방법을 오늘로 미루었는데, 오늘의 예제에서도 프로그레스바의 수치 등을 조절하는 함수는 있었지만 여전히 텅 빈 황량한 프로그레스바만 출력되고 있습니다.
DoModal() 함수를 호출하기 전에 SetProgressBarPosition( ... ) 함수로 프로그레스바 컨트롤의 초기값을 정해줄 수는 있지만, 태스트 대화상자가 한 번 뜨고 나면 DoModal() 함수가 제어를 쥐고 있기 때문에 컨트롤의 값을 조작하도록 손쓸 방법도 마땅치가 않습니다.

(그림 3) SetProgressBarPosition(...)으로 초기값은 정할 수 있다고 치지만, 더이상 어떻게 컨트롤 한다는 말인가?

이럴 때 CTaskDialog 클래스의 주요 기능 중의 하나인 타이머를 이용해야 합니다. 프로그레스바 컨트롤을 다루는 방법을 통해 CTaskDialog의 파생 클래스 생성 및 타이머 사용 방법을 알아보도록 하겠습니다.



CTaskDialog::OnTimer 오버로딩하기.
대화상자가 떠있는 동안은 DoModal()이 제어권을 갖고 있기 때문에, 우리가 CTaskDialog 객체의 외부에서 추가적인 조작을 가하기는 어렵습니다. 하지만 프로그레스바 컨트롤의 값을 애니메이션 하려면 대화상자가 출력되어 있는 동안에 처리가 이루어 져야 하겠지요.
이런 경우에는 CTaskDialog를 상속받는 파생 클래스를 제작하고, 가상 함수인 OnTimer를 오버로딩합니다.
OnTimer의 기본형은 다음과 같습니다.

  • virtual HRESULT OnTimer(_In_ long lTime);

예제 코드에 보면 SetOption( ... ) 함수를 이용해 기본적인 옵션에 TDF_CALLBACK_TIMER 를 추가해주어 타이머 기능을 활성화 하는 처리를 확인할 수 있습니다. 이렇게 플래그를 통해 타이머를 활성화 시켜주면 OnTimer 함수가 약 200 밀리초 (=0.2초) 주기로 호출됩니다.

int options = taskDlg.GetOptions(); options |= TDF_CALLBACK_TIMER;   // 타이머 옵션 추가 설정. taskDlg.SetOptions(options);

타이머가 필요한 대표적인 예가 바로 프로그레스바 컨트롤의 애니메이션 이겠지요. 오버로딩한 OnTimer 함수에서 간단하게 프로그레스바의 값을 진행시키는 코드를 넣어보도록 하겠습니다. 값을 조절하는 건 위에서 언급되었던 SetProgressBarPosition 함수만으로 충분합니다 :)

class CMyTaskDialog : public CTaskDialog { // ... virtual HRESULT OnTimer(_In_ long lTime); } HRESULT CMyTaskDialog::OnTimer(_In_ long lTime) { static int iProgressPos = 0; iProgressPos += 2; if (iProgressPos >= 100) iProgressPos = 0; SetProgressBarPosition(iProgressPos); return S_OK; }

위의 코드는 매번 타이머 함수가 호출될 때마다 프로그레스바 컨트롤의 값을 2씩 증가시켜주다가, 컨트롤이 만땅이 되면 꽉 차면 다시 0으로 초기화 해주고 있습니다. 0.2초에 한 번씩 호출이 되는데 2씩 채우니까... 100을 다 채우려면... 그러니까 1초에 다섯번쯤 호출 되는데... 한 번에 2씩이면 초당 10씩인가...... 뭐 아무튼 실행해보면 적당한 속도로 컨트롤이 애니메이션 되는 모습을 확인할 수 있습니다.
(웃자고 적어본 거 아시죠... 저 덧셈 잘합니다...;;)

프로그레스바 컨트롤에 대한 이야기가 나왔으니까 프로그레스바의 스타일에 대해 몇가지만 더 이야기하고 마무리 짓도록 하지요.



Marquee 타입 프로그레스바
태스크 대화상자의 프로그레스바 컨트롤은 기존의 기본적인 프로그레스바 방식보다는 Marquee 타입으로 많이 쓰일것을 예상하고 디자인된 것으로 보입니다. 프로그레스바 컨트롤을 추가하는 함수의 이름부터가 SetProgressBarMarquee(...) 이니 말입니다. 우리 예제에서는 프로그레스바의 영역을 설정하고 값을 지정해 주어 일반 프로그레스바처럼 사용하였지만, SetProgressBarRange(...) 함수와 SetProgressBarPosition(...)을 주석처리하면 프로그레스바는 기본적으로 Marquee 타입으로 설정되고, 자동으로 애니메이션 됩니다.

(그림 4) Marquee 타입 프로그레스바. 지렁이 같은게 계속 스멀스멀...

윈도우 XP의 부팅속도를 가늠하기 위한 암묵적 의사소통 수단이기도 했지요. 지렁이 몇마리...
바로 그런 식입니다. Marquee 타입 프로그레스바 컨트롤은 내부에 초록색 불빛이 좌에서 우로 이동하는 애니메이션을 반복합니다. 내부적으로 무언가 작업을 처리하고 있음을 나타내는 용도로 사용하는데 적합합니다.



CTaskDialog::SetProgressBarState(...)로 프로그레스바 상태 설정하기.
프로그레스바 관련 함수 중에 SetProgressBarState(...)가 있습니다. 이 함수를 이용해 프로그레스바의 상태를 정상 / 일시 정지 / 에러 세가지 상태 중의 하나로 설정할 수가 있습니다. 인자로 아래의 플래그를 주면 됩니다.

  • PBST_NORMAL - 정상. 디폴트값이며, 프로그레스바가 녹색으로 표시됨.
  • PBST_ERROR - 에러. 프로그레스바가 빨강으로 표시됨.
  • PBST_PAUSED - 일시 정지. 프로그레스바가 노랑으로 표시됨.

상황에 따라 적절히 사용하면 좀 더 친절한 인터페이스를 제공할 수 있겠군요. 아래는 각각의 상태에 대한 프로그레스마 컨트롤의 스크린샷 입니다. 스크린샷을 비교하는 김에 위에 붙였던 Marquee 타입의 스크린샷까지 함께 대조해 보도록 하겠습니다.

(그림 5) 위에부터 차례대로, Marquee 타입, 기본형 정상상태 / 에러상태 /일시정지 상태



Outro
이것으로 MFC 10.0의 새로운 클래스인 CTaskDialog 편을 마무리 하도록 하겠습니다. 사용법은 그렇게 어려울 것도 없는데 막상 풀어서 설명하다보니 글이 꽤나 길어졌습니다. 3차례에 걸친 강좌를 마무리하고 보니, 제가 너무 상세한 설명까지 덧붙여 괜히 글이 장황해 진 것은 아닌가 하는 걱정도 드네요. 분명 내용이 어려울 게 없는데 말입니다 ^^; 하하...
제 글이 너무 지루하거나 수준이 낮으신 분들은 매번 포스팅마다 가장 아랫부분에 따로 정리해두는 Reference 링크를 참조하시기 바랍니다. 앞으로도 강의는 가능하면 초보 개발자나 학생들도 어렵다는 느낌 없이 쉽게 접하고 내용을 익힐 수 있는 수준으로 이어나갈 예정입니다.

VisualStudio는 이미 vs2008부터 Native Programmer들을 위한 지원에 많은 노력을 들이고 있다는 점을 확인할 수 있습니다. 그런 부분을 가장 확실하게 보여주는 점이 MFC 라이브러리의 보강 입니다.
vs2010에서도 MFC는 많은 신기능들을 소개하고 있습니다. 다음 강의에도 좀 더 흥미있는 주제로 포스팅을 이어 나가도록 하겠습니다.
그럼 다음 강좌에서 또 뵙도록 하겠습니다.
감사합니다 ^^*


Reference

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

제가 Visual Studio 2010 공식 팀 블로그에 게시했던 글을 스크랩 합니다.

Intro
안녕하세요. MFC 카테고리의 꽃집총각 입니다. 이번에는 지난번 포스팅 [MFC] 태스크 대화상자(Task Dialog) - (1/3) : 기능 소개 편에 이어서, 사용하기 편을 준비했습니다.
이번 포스팅에서 다룰 내용은

  1. 비스타 이전 OS에서 태스크 대화상자 이용에 대한 이슈
  2. 태스크 대화상자의 기본적인 구성
  3. 소스코드 한 줄로 태스크 대화상자 사용하기 - ShowDialog 함수.

이렇게 세 가지 내용을 알아보도록 하겠습니다. 3번 ShowDialog 함수의 소개가 이번 포스팅의 주된 내용입니다. 태스크 대화상자를 사용하기 위해서는 CTaskDialog의 객체를 생성해 DoModal() 함수를 실행하는 방법과, 객체를 생성하지 않고 간단히 CTaskDialog::ShowDialog(...) 함수를 이용하는 방법이 있습니다. 후자는 간편하게 함수호출 한번으로 태스크 대화상자를 이용할 수 있는 장점이 있고, 전자는 보다 디테일한 설정 및 활용이 가능하다는 장점이 있습니다. 오늘은 간단한 사용법인 ShowDialog 함수에 대해 알아보고, CTaskDialog의 객체를 선언하는 방법은 다음 포스팅에서 별도로 다루도록 하겠습니다.



CTaskDialog::IsSupported() - 비스타 이전 OS들을 위한 대비
본격적인 CTaskDialog의 기능들을 알아보기 전에, 먼저 한가지 정리해야 할 점이 있습니다. 이전 포스팅에서 짧게 언급한 바 있지만, 태스크 대화상자는 비스타 이전 OS에서는 제공되지 않습니다. 그래서 만약 개발한 응용프로그램이 윈도우 XP 등에서 실행된다면 태스크 대화상자 출력 시점에 오류를 일으킵니다.  

(그림 1) 윈도우 XP에서 태스크 대화상자 출력을 시도할 때 나오는 오류 화면.


그렇기 때문에 개발한 프로그램이 비스타 이전 OS에서도 실행되어야 한다면 이런 경우를 위한 별도의 처리를 직접 해주어야만 합니다. 이럴 땐 어쩔 수 없이 기존의 메세지 박스나, CDialog를 상속받은 클래스를 직접 제작해 주어야 겠지요.
이런 처리를 찾아볼 수 있는 좋은 예가 바로 인터넷 익스플로러 8.0의 '세션 복구 기능' 입니다. 익스플로러 8.0은 비정상 종료되었다가 실행되는 경우 이전에 열려있던 페이지를 자동 복구할 것인지를 물어보는 창을 띄우는데, 실행중인 윈도우가 비스타 이전 버전이라면 일반 대화상자를, 비스타 이후 버전이라면 태스크 대화상자를 출력합니다.

(그림 2) 인터넷 익스플로러 8.0의 세션복구 기능에서 쓰인 태스크 대화상자.


(그림 3) 비스타 이전 OS에서는 태스크 대화상자 대신 기존의 대화상자를 출력합니다.


이런 식으로 OS 차원의 태스크 대화상자 사용 가능여부를 확인하고자 할 때에는, 직접 OS의 버전을 얻어와서 기능 지원 여부를 판별할 수도 있겠지만 CTaskDialog::IsSupported() 정적 함수를 이용해서 쉽게 확인할 수 있습니다.

if( CTaskDialog::IsSupported() ) // 태스크 대화상자 사용 가능 여부를 확인
{
    // 태스크 대화상자 호출.
    CTaskDialog::ShowDialog(message, emptyString, title, 0, 0, TDCBF_OK_BUTTON);
}
else
{
    // 지원하지 않는 OS인 경우는 예전 방식으로 처리.
    AfxMessageBox(message);
}
(코드 1) CTaskDialog::IsSupported() 함수를 통해 태스크 대화상자 사용 가능 여부를 확인.

참고로 윈도우 서버 2008은 비스타 이전에 나온 OS이므로 태스크 대화상자를 사용할 수 있습니다. 물론 비스타나 윈도우 7 처럼 예쁜 모양은 아니지만요 ^^;

(그림 4) 윈도우 서버 2008에서 출력되는 태스크 대화상자의 모습. 출처 : http://mariusbancila.ro/blog/2009/03/10/task-dialog-in-mfc/

 


태스크 대화상자의 기본적인 구성
태스크 대화상자는 기본적인 구성은 MSDN에 있는 아래의 샘플 스크린샷에 아주 잘 나타나 있습니다.

(그림 5) 태스크 대화상자의 기본적인 구성. (출처 : MSDN)


(그림 5)에서 알 수 있듯이 태스크 대화상자는 다양한 컨트롤들과 기능들을 제공합니다. 오늘은 우선 예전 AfxMessageBox() 수준의 심플한 사용방법을 알아보겠습니다. 간단한 메세지를 사용자에게 노출하거나, 기본적인 버튼을 통해 사용자 입력을 받아오고자 할 때는 오늘 소개하는 ShowDialog 함수를 이용하는 것이 좋습니다.


첫 번째 예제 : 일단 한 번 띄워봅시다
자, 이제 정말로 태스크 대화상자를 사용하기 위한 방법을 알아보죠. 가장 간단하게 태스크 대화상자를 출력하는 방법은 정적 함수인 CTaskDialog::ShowDialog() 를 사용하는 방법입니다. 이 함수를 사용하면 CTaskDialog의 객체를 만들지 않고도 바로 태스크 대화상자를 띄울 수 있습니다.
일단 속 시원하게 코드부터 보겠습니다! ShowDialog 정적 함수를 이용한 코드와, 실제 출력된 태스크 대화상자의 모양입니다.

INT_PTR ret = CTaskDialog::ShowDialog( L"본문 들어가는 곳", L"제목 들어가는 곳", L"타이틀 적는 곳", IDS_STRING102, // 첫 번째 커맨드 버튼. IDS_STRING104 ); // 마지막 커맨드 버튼. if( ret == IDYES ) { // blah blah... }

(코드 2) 드디어 등장했습니다! 태스크 대화상자를 띄우는 첫 번째 코드입니다!

(그림 6) (코드 2)를 통해 출력한 태스크 대화상자의 모습.


태스크 대화상자의 설명을 위해 두 차례나 걸친 포스팅에서 수없이 떠들었던 것에 비해, 너무나도 단순합니다. 그냥 함수 호출 하나 했더니 뜨는군요. 제목, 본문, 타이틀을 설정하는 것은 인자로 직접 문자열을 넣어주고 있으므로 추가 설명이 없이도 쉽게 확인하실 수 있습니다.
스크린샷을 보면 우리의 첫 번째 태스크 대화상자에서는 세 개의 커맨드 버튼을 가지고 있습니다. 커맨드 버튼은 예, 아니요, 확인, 취소 등과 같은 기존 대화상자의 기본적인 버튼 이외에 자유로운 출력 문자열을 설정할 수 있는 의사 입력 버튼 입니다. 태스크 대화상자의 가장 중앙부에 위치하며, 사실상 태스크 대화상자에서 가장 중요한 컨트롤이라고 할 수 있습니다. ShowDialog 함수를 이용할 때 커맨트 버튼을 넣기 위해서는 응용 프로그램의 리소스 파일에 있는 스트링 테이블을 참조합니다. 스트링 테이블에 커맨드 버튼의 설명으로 사용할 문자열들을 차례로 입력하고, 해당 스트링 아이디의 처음과 마지막 값을 ShowDialog의 네 번째, 다섯 번째 인자로 넣어줍니다.

(그림 7) 커맨드 버튼을 추가하기 위해 스트링 테이블을 사용하는 예시.


(그림 6)이 우리의 첫 번째 대화상자 예시에 쓰인 스트링 테이블 입니다. 태스크 대화상자에는 스트링 테이블의 문자열이 출력되고, 사용자가 해당 커맨드 버튼을 선택한 경우 ShowDialog의 리턴값으로 스트링 ID가 반환됩니다. 예제의 세 번째 커맨드 버튼처럼 화면에 여러 줄의 텍스트를 출력하고 싶다면 문자열 안에 개행문자를 이용해 여러 줄의 텍스트를 넣어주면 됩니다.



CTaskDialog::ShowDialog(...) 함수를 좀 더 자세히 알아봅시다.
ShowDialog 함수의 인자는 모두 8개 입니다. 예제에서는 다섯개의 인자만을 사용했고 나머지 인자들은 기본값이 쓰였습니다. 함수의 전체적인 기본형은 아래와 같습니다.  

static INT_PTR CTaskDialog::ShowDialog(
   const CString& strContent,
   const CString& strMainInstruction,
   const CString& strTitle,
   int nIDCommandControlsFirst,
   int nIDCommandControlsLast,
   int nCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON,
   int nTaskDialogOptions = TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS,
   const CString& strFooter = _T("")
);

다섯 번째 인자까지는 설명이 되었으니 나머지 인자를 보자면,
  • nCommonButtons : 태스크 대화상자 오른쪽 하단에 출력되는 기본 버튼들을 설정합니다. 기본값으로 '예', '아니오' 두 개의 버튼이 출력됩니다.
  • nTaskDialogOptions : 태스크 대화상자의 여러가지 옵션을 조절할 수 있는 인자입니다. 옵션에 대한 플래그 값은 아래에서 보다 자세히 설명합니다.
  • strFooter : 태스크 대화상자의 가장 아래쪽에 출력되는 꼬릿말 부분입니다.
기본 버튼으로 설정할 수 있는 플래그는 총 여섯가지이고, CommCtrl.h에 정의되어 있습니다.
  • TDCBF_OK_BUTTON - 확인
  • TDCBF_CANCEL_BUTTON - 취소
  • TDCBF_YES_BUTTON - 예
  • TDCBF_NO_BUTTON - 아니오
  • TDCBF_RETRY_BUTTON - 재시도
  • TDCBF_CLOSE_BUTTON - 닫기
태스크 대화상자의 옵션을 조절할 수 있는 플래그는 총 열 여섯가지이고, 역시 CommCtrl.h 파일에 정의되어 있습니다. 이 중에서, ShowDialog() 함수와 함께 쓰일 수 있을 만한 플래그 몇 가지만을 정리해 보면 아래와 같습니다.
  • TDF_ENABLE_HYPERLINKS
    하이퍼 링크를 활성화 합니다. 텍스트에 하이퍼 링크를 넣고 싶을 때에는 문자열에서 <a href=\"http://vsts2010.net\">하이퍼 링크</a>처럼 HTML 문법으로 설정할 수 있습니다.
  • TDF_ALLOW_DIALOG_CANCELLATION
    태스크 대화상자의 오른쪽 상단에 창 닫기 버튼이 생기고, Esc키나 Alt + F4로도 창을 끌 수 있게 됩니다. 닫기버튼이나 키보드 조작으로 창을 닫은 경우는 IDCANCEL이 리턴됩니다.
  • TDF_USE_COMMAND_LINKS
    커맨드 버튼을 사용하도록 설정합니다. 이 플래그가 설정되지 않으면 커맨드 버튼으로 설정한 버튼들도 기본 버튼들처럼 출력됩니다.
  • TDF_USE_COMMAND_LINKS_NO_ICON
    커맨드 버튼에 아이콘을 출력하지 않고 텍스트만 표시하게 설정합니다.
  • TDF_SHOW_PROGRESS_BAR
    프로그레스바 컨트롤을 표시합니다.
  • TDF_SHOW_MARQUEE_PROGRESS_BAR
    프로그레스바 컨트롤을 Marquee 형식으로 출력합니다.
  • TDF_POSITION_RELATIVE_TO_WINDOW
    태스크 대화상자를 부모 윈도우의 중앙 위치에 나타나도록 합니다. 이 플래그가 설정되지 않으면 바탕화을 기준으로 중앙 위치에 출력됩니다.
  • TDF_RTL_LAYOUT
    태스크 대화상자에 출력되는 텍스트들을 오른쪽에서 왼쪽으로 출력합니다.
  • TDF_CAN_BE_MINIMIZED
    태스크 대화상자의 오른쪽 상단에 최소화 버튼이 생기고, 버튼을 누르면 창이 최소화 됩니다.
위의 아홉가지 플래그 정도가 ShowDialog 함수를 이용한 간단한 사용시에도 유용하게 쓰일만한 기능들 입니다. 이 중에 프로그레스바와 관련된 플래그 같은 경우, TDF_SHOW_PROGRESS_BAR를 넣어주면 실제로 프로그레스바가 표시되지만, 프로그레스바의 값을 조절한다거나, 오류상태를 표시하는 등의 세밀한 처리를 하는 것은 어렵습니다. 이런 경우는 ShowDialog 함수를 통한 호출보다는 직접적으로 CTaskDialog 의 객체 혹은 CTaskDialog 파생클래스의 객체를 만들어서 처리해야 합니다. TDF_SHOW_MARQUEE_PROGRESS_BAR 플래그로 Marquee 형식을 출력할 수도 있지만 이런 경우도 대게는 타이머를 설정하고 자체적으로 동작이 진행중임을 표시하기 위해 주로 사용하므로, 이또한 ShowDialog를 통한 출력에는 생뚱맞습니다.

(그림 8) 예제 대화상자에 꼬릿말과 프로그레스바를 추가한 모습.


위의 (그림 8) 스크린샷을 봅시다. 꼬릿말은 그럭저럭 쓸만 하다고 하지만, 텅 빈 프로그레스바는 보는 이의 마음까지 황량하게 만듭니다. 아무래도 좀 더 추가적인 처리가 필요해 보입니다. ShowDialog 함수를 통해서도 프로그레스바 컨트롤을 출력할 수는 있지만, 이를 세밀하게 제어하는 것은 무리입니다. 이를 포함해 여러가지 컨트롤의 처리에 대한 내용은 다음 포스팅에 설명하도록 하겠습니다.



Outro
이번 포스팅 에서는 태스크 대화상자 미지원 OS를 식별하는 방법과, 태스크 대화상자의 기본적인 구성, 그리고 메시지 박스 수준의 간단한 태스크 대화상자 출력방법을 알아보았습니다. 정적 함수 ShowDialog()를 사용하면 AfxMessageBox()를 이용하는 것보다 좀 더 다양하게 머릿말, 본문, 꼬릿말 등으로 구분에 메세지를 설정할 수 있고, 모던한 디자인의 레이아웃을 갖춘 태스크 대화상자를 출력할 수 있습니다.
다음 포스팅에서는 태스크 대화상자가 제공하는 다양한 컨트롤 들을 활용할 수 있는 방법을 알아보도록 하겠습니다. 리소스 에디터로 직접 다이얼로그를 디자인하던 불편함에서 벗어나 함수호출 몇 줄만으로 다양한 기능을 추가하는 방법들을 정리할 예정입니다. 보다 미리 관련 내용을 확인하고 싶으신 분들은 MSDN(http://msdn.microsoft.com/en-us/library/bb760441(VS.85).aspx)과 다음의 페이지( http://www.nuonsoft.com/blog/2009/06/10/ctaskdialog-in-mfc-in-visual-c-2010/ )를 참고하시기 바랍니다.
그럼 다음 포스팅에서 뵙겠습니다.
감사합니다 ^^*


Reference
Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

제가 Visual Studio 2010 공식 팀 블로그에 게시했던 글을 스크랩 합니다.


Intro
안녕하세요. MFC 카테고리의 꽃집총각입니다. 이번에는 앞서 말씀 드렸던 대로, MFC 10.0에서 새로 추가되는 클래스 중에 하나인 CTaskDialog에 대해서 알아보도록 하겠습니다. 이번 포스팅에서는 CTaskDialog클래스와 이를 이용해 구현할 수 있는 기능인 태스크 대화상자(Task Dialog; 이하 한글 표기만을 사용합니다)에 대한 전반적인 개념을 정리하고, 다음시간 부터는 실제 사용방법 및 주의 사항 등을 정리해 보도록 하겠습니다.


태스크 대화상자 소개
태스크 대화상자(Task Dialog)는 윈도우 비스타 버전에서 새롭게 선보인 컨셉의 대화상자 입니다. 태스크 대화상자를 이용해 개발자들은 기존의 메세지 박스를 이용해 구현하던 기능을 보다 손쉽고 강력하게 처리할 수 있습니다. 이전부터 널리 사용되고 있는 AfxMessageBox(...)를 이용해서도 간한단 정보의 노출이나 사용자 의사선택을 처리할 수 있었지만, 추가적인 기능을 확장해 넣기에는 번거로운 점이 많았습니다. 어떻게든 예, 아니오로 대답할 수 있는 연속적인 질문을 생각해내 메세지 박스를 연거푸 출력하거나, 리소스 에디터를 열고 새로운 대화상자를 직접 만들어 주어야 했습니다. 하지만 태스크 대화상자를 이용하면 개발자가 대화상자를 커스터마이징 하기 위해 필요한 거의 대부분의 기능을 옵션으로 제공하고 있어, 손쉽고 풍부한 기능확장을 아주 심플하게 해낼 수 있습니다.

원래 길게 말해봐야 한 번 보는게 더 와닿는 법입니다. 아래의 스크린샷이 바로 태스크 대화상자의 예시 입니다.

(그림 1) Internet Explorer 8.0의 세션 복구 기능에서 등장하는 태스크 대화상자.


(그림 2) 지난번 강좌에서 설명한 리스타트 매니저에서 사용되는 태스크 대화상자.


(그림 3) 윈도우 7의 업데이트 옵션 설정할때 등장하는 태스크 대화상자.


아마도 윈도우 비스타나 윈도우 7을 사용해보신 분이라면 이곳 저곳에서 많이 보셨던 형식의 대화상자 일겁니다. 위에서 예를 든 세 가지 스크린샷도 모두 프로그래머가 별도로 만든 것이 아니라, 운영체제나 인터넷 익스플로러 등에서 쓰인 녀석들 입니다. Visual Studio 2010의 MFC기반 응용 프로그램에서는 이러한 태스크 대화상자를 몇 줄 안되는 코드만으로 쉽게 제어할 수 있는 강력한 클래스를 제공합니다. 그 클래스가 바로 CTaskDialog 입니다 :)


CTaskDialog로 할 수 있는 일들.
거의 예/아니오 내지는 확인/취소 정도의 선택밖에 제공할 수 없었던 기존의 메세지 박스와는 달리 태스크 대화상자는 아래와 같은 풍부한 커스터마이징 옵션을 제공합니다.

  • 사용자 지정 아이콘
  • 메인 헤더 텍스트 (멀티라인 지원)
  • 본문 텍스트 (당연히 멀티라인 지원)
  • 프로그레스바 컨트롤 표시 가능.
  • 라디오 버튼 표시 가능.
  • 커맨드 버튼 - 대화상자의 가장 중앙에 나타나는, 사용자의 의사 선택을 받는 버튼 - 표시 가능.
  • 추가적인 본문 텍스트를 보이거나 숨길 수 있게 해주는 확장/축소 버튼 표시 가능.
  • 체크박스 표시 가능.
  • 꼬릿말 텍스트 (멀티라인 지원)
  • 대부분의 컨트롤 및 텍스트에 하이퍼 링크 지원.
  • 타이머 기능 제공.

위처럼 다양한 옵션을 제공하기 때문에 어지간한 대화상자 UI의 구성은 번거롭게 다이얼로그 리소스에 버튼을 배열하고 CDialog 파생클래스를 만들어 일일이 코딩을 해야 하는 수고를 들이지 않아도 CTaskDialog 를 이용해 아주 심플하게 처리할 수 있게 되었습니다. 개발 편의성 뿐만 아니라 실용성 측면에서도 실제 비스타 이후 버전의 윈도우 자체에서도 널리 쓰이고 있는 것을 보면 그 실용성은 입증 되었다 볼 수 있겠네요.
각각의 기능들에 대한 자세한 사용 방법은 다음 강좌에 보다 본격적으로 다루도록 하겠습니다.



태스크 대화상자와 운영체제, API들의 관계
이번 강좌에서는 이 부분을 좀 더 일찍 정리하고 싶었습니다. 실제 기능의 사용에는 크게 상관없지만 그래도 기본 개념의 정리가 중요한 법인데, 지난 번 강좌에서는 너무 늦게 말은 한 것 같아서요. 노파심에 다시 한 번 운영체제 및 API들과 기능간의 관계를 정리해 보겠습니다.

  1. 꼭 MFC에서만 할 수 있는 건 아닙니다. MFC는 보다 편리하게 쓸 수 있도록 도와줍니다.
    태스크 대화상자는 기존의 메세지 박스보다 확장성 측면에서 훨씬 유용하게 쓰일 수 있는 새로운 컨셉의 대화상자 입니다. 이 태스크 대화상자는 윈도우 비스타에서 처음 소개되었으며, 태스크 대화상자를 제어할 수 있는 Windows API들도 함께 제공 되었습니다. MFC에서는 이러한 API들을 쉽고 편하게 사용할 수 있도록 랩핑한 클래스를 제공하는 겁니다. 그래서 굳이 MFC를 사용하지 않아도 태스트 대화상자를 이용할 수는 있지만, MFC의 CTaskDialog를 이용해 보다 편하게 작업을 할 수 있습니다.
  2. 윈도우 비스타 이전 버전에서는 사용할 수 없습니다.
    하지만 애석한 점 한가지는, 비스타 이전 버전의 윈도우에 대한 대비책은 딱히 제공되지 않는다는 점입니다. 지난번 포스팅에서 소개했었던 리스타트 매니저도 마찬가지로 비스타 이후 버전에서만 동작했었습니다. 하지만 리스타트 매니저는 응용 프로그램을 윈도우 XP에서 실행해도 기능을 하지 않을 뿐 오류를 내지는 않았지만, 태스크 대화상자를 사용하는 응용 프로그램을 윈도우 XP에서 실행한다면 태스크 대화상자 출력시점에 에러를 내게 됩니다. 화면상에 출력되어야 하는 기능이니 리스타트 매니저 처럼 오동작 없이 넘어가기가 어렵기 때문입니다.


Outro

늘 마찬가지지만 제가 설명해 드리는 MFC의 새로운 기능들은 이해하기 쉽고 빠르게 개발에 적용할 수 있는 기능들 입니다. MFC의 기본적인 방향성 자체가 보편적인 기능을 손쉽게 다룰 수 있게 제공되는 라이브러리 이기도 하고요. 그래서 초보 개발자나, 학생 분들도 어렵지 않게 내용을 따라오실 수 있다고 생각합니다. 그래도 설명 중 잘 이해가 되지 않는 부분이나 잘못된 점이 있다면 피드백을 부탁 드리겠습니다.

그럼 이번 포스팅에서는 간략하게 기능소개 정도로 글을 마치고, 다음 글에서 본격적으로 태스크 대화상자를 사용하는 방법을 다뤄 보도록 하겠습니다. 다음 글을 기다리기가 지루하신 분들은 MSDN( http://msdn.microsoft.com/en-us/library/bb760441(VS.85).aspx )이나 다음의 글( http://www.nuonsoft.com/blog/2009/06/10/ctaskdialog-in-mfc-in-visual-c-2010/ )을 참고하세요.
그럼 다음에 뵙겠습니다.
감사합니다 ^^*


Reference

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

월요일날 마이크로소프트에 데모 시연 동영상 촬영하러 갔다가,
강차장님 노트북에서 재미있는 것을 발견.
차장님께는 여태 이것도 설치 안했냐고 꾸지람을 듣긴 했지만 ^^;...

Windows 7 Training Kit, Visual Studio 2010 Training Kit 이라는 예제 코드 모음 팩이 있다.
vs2010 킷은 차장님에게 바로 받아왔고 Windows 7은 지금 다운받는 중...

http://www.microsoft.com/downloads/details.aspx?familyid=1C333F06-FADB-4D93-9C80-402621C600E7&displaylang=en

2010킷 보다는 Windows 7 킷이 좀 더 재밌어 보인다.
이거 먼저 공부해야지.

Channel 9에 온라인 강좌까지 있다. http://channel9.msdn.com/learn/courses/Windows7/
좋지 아니한가!
Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

제가 Visual Studio 2010 공식 팀 블로그에 게시했던 글을 스크랩 합니다.

Intro
안녕하세요. MFC 카테고리의 꽃집총각 입니다.
지난번 [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기 편에 이어서 오늘은 리스타트 매니저 시리즈의 마지막인 ‘활용하기’ 편입니다. 이번 시간에는 실제로 리스타트 매니저의 동작을 확인해 볼 수 있는 샘플을 만드는 과정을 정리하고, 실제 동작에 관련된 이슈들을 몇 가지 정리해 보겠습니다. 


예제 프로그램작성 – 1. MFC Application Wizard 설정 하기.

자, 우선은 VIsual Studio 2010을 열어서 Ctrl + Shift + N 을 힘차게 누질러서 새 프로젝트 생성 창을 띄우고, MFC Application을 선택해 MFC 어플리케이션 위자드(Application Wizard;응용 프로그램 마법사)를 띄웁니다.

(그림 1) MFC Application을 선택해 새 프로젝트를 생성합니다.

어플리케이션 위자드가 뜨면 왼쪽 메뉴에서 Application Type을 선택하고, 기본 비주얼 스튜디오(Visual Studio) 형식으로 되어 있는 프로젝트 스타일 항목을 오피스(Office) 형식으로 변경해 줍니다. 비주얼 스튜디오 스타일의 여러 가지 도킹 pane들은 이번 예제에는 크게 쓸모가 없으니까, 괜히 걸리적 거리기만 하거든요 ^^;


(그림 2) MFC Application Wizard > Application Type 항목 설정.

Document Template Properties 항목을 선택하고, 파일 확장자(File extension) 란에 임의의 확장자를 입력해줍니다. 파일 확장자를 명시하게 되면 별도의 코딩을 추가하지 않아도 생성된 프로젝트에 자동 생성되는 소스코드 부분에 Document의 저장/로딩 처리가 추가됩니다. 파일 확장자를 입력하면 오른쪽에 있는 필터 이름(Filter name)칸도 자동으로 채워집니다. 저는 custom-document-format의 뜻으로 cdf라고 적어봤어요.

( 그림 3) MFC Application Wizard > Document Template Properties 항목 설정.

그리고 가장 중요한 부분! Advanced Features 항목에 가서 리스타트 매니저 지원 사항들을 확인합니다. 리스타트 매니저와 관련된 세 개의 체크사항들이 기본적으로 체크되어 있기 때문에 수정을 할 필요는 없지만 그래도 올바로 체크되어 있는지 확인해야 겠지요. 


(그림 4) MFC Application Wizard > Advanced Features 항목 설정.

자, 이제 마지막으로, Generate Class 페이지로 가서 뷰 클래스의 부모 클래스를 CView –> CEditView로 변경해 줍니다. CEditView를 상속받은 클래스는 기본적으로 뷰 영역이 메모장 프로그램처럼 캐럿이 깜박이는 에디트 컨트롤 형식으로 되어있어서, 별다른 처리 없이도 문서의 저장을 확인할 수 있는 문자열 형식의 데이터를 입력할 수 있게 됩니다.


(그림 5) MFC Application Wizard > Generated Class 항목 설정.

이제 어플리케이션 마법사의 모든 설정이 끝났습니다. Finish를 눌러 프로젝트를 생성합니다.


예제 프로그램작성 – 2. 크래시 발생 버튼 추가하기.

리스타트 매니저가 올바르게 동작하는지 알아보기 위해서는 프로그램이 비정상 종료 되어야 합니다. 샘플 프로그램의 리본 UI에 간단하게 패널 하나와 버튼 하나를 추가하고, 핸들링 함수를 추가해서 아래와 같이 잘못된 포인터 연산을 하는 코드를 넣어줍니다. 어플리케이션 위자드에서 오피스 형식의 Application Type을 선택하셨다면 아마 기본적으로 리본 UI가 붙어있을겁니다. 그렇지 않다면 툴바든, 마우스 입력이든 상관 없이 아무 이벤트나 받아서 고의적으로 예외를 발생시키는 코드를 넣어주면 됩니다.

  
(그림 6) 리본에 누르면 터지는 버튼을 넣어줍니다.

 

 예제 프로그램 작성 – 3. 문서 자동 저장 간격 조절하기.
지난번에 리스타트 매니저 사용 팁을 정리하면서, 문서 데이터 자동 저장 기능의 기본 저장 간격 시간은 5분이라고 말씀 드린 적이 있습니다. 우리는 매우 바쁜 사람이니까, 이 시간을 당겨보죠. 이왕이면 프로그램을 띄우고 나서 바로 확인할 수 있도록 아주 짧게 잡아보겠습니다. 한 10초 정도면 나쁘지 않겠죠?

(그림 7) CWinApp 파생클래스의 생성자에서 m_nAutosaveInterval의 값을 10000 미리세크(=10초)로 설정해줍니다.

시간 설정에 대한 내용은 지난회 포스팅이었던 [MFC] 리스타트 매니저(Restart Manager) - (2/3) : 사용하기 편을 참고하시면 됩니다.

 

예제 프로그램 실행!
이제 빌드를 하고 프로그램을 실행시켜서 에디트 뷰에 블라블라 테스트용 잡담을 늘어놓은 후, 10초가 지나 임시 문서가 만들어졌을 만한 충분한 시간을 제공한 뒤에 ‘누르면 폭발!’ 버튼을 야심차게 눌러주면 프로그램이 크래시가 나고 리스타트 매니저가 동작하면서 프로그램을 다시 띄워 주겠지요? 하지만 실제로 실행해보시면 아마 리스타트 매니저는 커녕 그냥 프로그램만 죽어버리고, 프로그램을 닫든지 디버깅을 하든지 니 맘대로 하라는 쓸쓸한 대화상자만 출력되고 말 겁니다.


(그림 8) 크래시가 났지만, 재시작도 문서 복구도 아무 것도 일어나지 않았습니다.
우리는 대 사기극에 휘말린 걸까요?


(그림 9) ‘그래, 내컴은 닷넷 디버거가 깔려 있어서 그럴 거야! 일반 사용자들은 이렇지 않겠지!’
라는 일말의 희망도 부질없습니다. 닷넷 미설치 PC에서는 위와 같은 창이 출력됩니다.

이 시점에서 몇 가지 더 알아두어야 할 것이 있습니다. 우리의 샘플 프로그램에서 리스타트 매니저가 동작하지 않는 이유와 함께, 실제로 리스타트 매니저를 사용하려고 할 때 알아두면 좋은 몇가지 정보들을 함께 정리해 보도록 하겠습니다.

  

(중요*) 리스타트 매니저 사용시 고려사항.

  1. 우리가 살펴보고 있는 MFC 10.0의 리스타트 매니저 기능이 실은 첫 번째 포스팅에서 설명 드렸던 것처럼 윈도우 비스타 시스템에서 선보인, OS 차원에서 제공되는 기능입니다. MFC 10.0에서는 이 기능을 좀 더 쉽게 사용할 수 있게끔 추가처리를 지원하는 것입니다. 예를 들자면 win32 GDI의 DC(Device Context)와 MFC의 CDC 클래스 관계 정도가 되겠네요. MFC의 리스타트 매니저도 내부 구현으로 들어가면 비스타 이후 OS에서 지원하는 윈도우 API인 RegisterApplicationRestart, RegisterApplicationRecoveryCallback 등의 함수를 사용해 재시작 및 문서 복구 기능을 제공하고 있습니다. MFC를 사용하지 않은 일반 Win32 윈도우 프로그램이나, 콘솔 프로그램에서 까지도 재시작 및 복구 기능을 사용할 수 있습니다. 하지만 MFC를 이용하면 보다 쉽게 사용할 수 있게 되는 것이지요.
  2. 프로그램 재시작 기능의 핵심이 되는 RegisterApplicationRestart 함수는 사용시 한가지 주의해야 할 사항이 있습니다. 만약 프로그램의 초기화 코드에 오류가 있어서 인스턴스가 실행되자마자 크래시를 내는 상황인데 리스타트 매니저가 동작한다면 어떻게 될까요? 아마 프로그램은 뻗고, 실행되고, 다시 뻗고, 다시 실행되는 무한 재실행을 반복하게 될겁니다. 이런 경우를 피하기 위해서 비스타의 응용 프로그램 재시작 기능은 초기 재시작 후 60초 동안은 크래시로 인한 비정상 종료가 있었다고 해도 동작하지 않습니다. 우리가 실행시킨 샘플 프로그램은 구동한지 60초가 지나지 않았기 때문에, 재실행 기능이 실행되지 않았던 겁니다. 보다 자세한 내용은 MSDN(http://msdn.microsoft.com/en-us/library/aa373347(VS.85).aspx)에서 확인하실 수 있습니다.
  3. 자동 저장되는 문서 파일의 경로는 CDataRecoveryHandler::GetAutosavePath 함수로 알아낼 수 있습니다. 프로그램을 실행하고 해당 경로에 가보면 실제로 자동 저장되는 버전의 문서파일을 확인하실 수 있습니다. 우리의 샘플 프로그램은 10초마다 착실하게 문서를 잘 저장하고 있네요 :) 임시 파일 저장 경로의 변경은 CDataRecoveryHandler::SetAutosavePath 함수로 가능합니다.

 

 이제 예제를 통해 리스타트 매니저 동작을 직접 확인해 봅시다.
이제는 정말로 리스타트 매니저의 놀라운 동작을 우리 눈으로 직접 확인할 시간입니다. 준비는 이미 다 끝났습니다. 단지 리스타트 매니저가 실행될 수 있도록 예제 프로그램 구동 후 60초의 시간을 기다려 주기만 하면 됩니다.


(그림 10) 리스타트 매니저의 동작을 한 눈에 파악하기 위해
 모두 저장된 파일, 반만 저장된 파일, 저장 안 된 파일을 준비.

60초의 시간이 흐르는 동안 저는 위의 (그림 10)과 같이 세 개의 문서 파일을 준비했습니다. 저장한 문서창 하나, 저장한 후 추가로 내용을 추가한 문서창 하나, 그리고 저장하지 않고 내용만 적은 문서창 하나. 곧 리스타트 매니저가 이들을 각각 제대로 복구해 주는지 실제로 확인해 보겠습니다.
그리고 실제로 우리가 설정한 10초의 간격으로 문서 파일이 저장되는지 확인해 보겠습니다. 저는 윈도우 7을 사용하고 있는데, 임시 문서 기본 저장 경로가 C:\Users\(윈도우계정명)\AppData\Local 으로 잡혀있네요. 윈도우 탐색기로 해당 경로를 열어보면 실제 10초 단위로 갱신되고 있는 임시 저장 파일들이 보입니다.


(그림 11) 착실히 저장되고 있는 자동 임시 저장 파일들.

이제 미리 만들어 두었던 리본 UI의 크래시 버튼을 눌러 프로그램을 비정상 종료 시킵니다. 프로그램이 비정상 종료된 후, 자동으로 재시작 되면서 문서를 임시 저장된 버전으로 복구할 것인지를 묻는 대화상자가 출력됩니다.


(그림 12) 크래시 발생후 리스타트 매니저가 자동으로 응용 프로그램을 재시작.


(그림 13) 응용 프로그램 재시작 후, 임시 저장된 버전의 문서를 복구할 것인지 묻는 대화상자 출력

그림 13의 문서 복구 여부 확인창이 떴을 때 ‘자동 저장 문서 복구’ 항목을 선택하면, 크래시가 나기 전에 저장해 두었던 문서의 텍스트들이 그대로 모두 복구되는 것을 확인할 수 있습니다. 임시 버전으로 복구된 파일들은 파일명이 출력되는 탭 부분에 [복구됨] 이라는 표기가 붙어서 출력됩니다.


(그림 14) 리스타트 매니저의 '자동 복구 기능'으로 복구된 문서파일의 모습.

 

Outro
이번 포스팅 에서는 리스타트 매니저를 직접 동작시키고 확인해 볼 수 있는 예제 작성 방법과 몇 가지 주의 사항을 짚어 보았습니다. 첨부된 이미지들이 많아서 괜히 길어 보이긴 하지만, 어려운 내용은 없었으리라 생각합니다.
이것으로 3회에 걸친 리스타트 매니저 강좌를 마치고, 다음에는 MFC 10.0에서 새롭게 선보이는 CTaskDialog 클래스에 대한 강좌를 가지고 다시 찾아 뵐 예정입니다.
그럼 다음 강좌에서 뵙도록 할게요.
감사합니다 ^^*

 

Reference

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

제가 Visual Studio 2010 공식 팀 블로그에 게시했던 글을 스크랩 합니다.

Intro
안녕하세요. MFC 카테고리의 꽃집총각 입니다.
지난번 [MFC] 리스타트 매니저(Restart Manager) - (1/3) : 기능 소개 편에 이어서 이번에는 실제로 리스타트 매니저를 사용하려면 어떻게 해야 하는가를 알아보도록 하겠습니다.


리스타트 매니저 사용하기

리스타트 매니저를 새로운 응용프로그램 프로젝트에 적용하는 방법과, 기존에 작성된 응용프로그램에 적용하는 방법에는 조금 차이가 있습니다. 하나씩 나누어서 알아보죠.

1. 새로운 프로젝트에서 리스타트 매니저 사용하기 
새 프로젝트로 MFC Application을 선택하면, MFC Application Wizard로 넘어가면서 프로젝트 설정을 하게 됩니다. 이 때 AdvancedFeatures 페이지에서 Support Restart Manager (리스타트 매니저 지원) 사항을 체크하면 됩니다.

(그림 1) 응용프로그램 마법사의 리스타트 매니저 지원 항목.


(그림 1)에서 보시는 대로 체크버튼이 세 개가 있습니다. 체크버튼의 선택 사항에 따라 지원 범위를 세가지로 선택할 수 있습니다.
  1. ① - 리스타트 매니저 지원 사항만 체크하는 경우
    이 경우 응용 프로그램은 재시작 기능만을 지원하게 됩니다. 응용 프로그램은 업그레이드나 크래시 발생 후 자동으로 재시작하는 기능이 추가되지만, 문서를 자동으로 열어주거나 복구해주는 처리는 하지 않습니다.
  2. ① + ②  - '리스타트 매니저 지원' 항목과 '이전 문서 다시열기' 항목에 체크하는 경우
    재시작 기능과 함께, 이 경우는 이전에 열려있던 문서를 다시 열어주는 기능까지 제공하지만 문서의 자동저장 버전을 복구하는 처리는 하지 않습니다.
  3. ① + ② + ③ - 싸그리 다 체크하는 경우
    자동 재시작 기능, 문서 다시 열어주기 기능, 자동 저장된 버전으로 복구하는 기능까지 모두 제공합니다.

참고 사항:

  • ② 항목을 선택하기 위해서는 반드시 ① 항목을 선택해야 하며 ( case B ), ③ 항목을 선택하기 위해서는 반드시 ② 항목을 선택해야 합니다. ( case C )
  • 다이얼로그 기반의 응용 프로그램에서는 ②, ③ 항목은 자동으로 비활성화 됩니다.

2. 기존의 프로젝트에서 리스타트 매니저 사용하기
기존의 응용프로그램에 적용하기 위해서는 CWinApp 파생 클래스의 생성자에 딱 한 줄의 코드만 추가해주면 끝입니다. (아래 코드는 각각 위에서 설명한 A, B, C 기능과 동일한 기능을 제공합니다.

  1. m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART
  2. m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART_ASPECTS
  3. m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS

사용된 플래그 상수들은 afxwin.h 파일에 정의되어 있으며, 정의된 위치를 찾아가보면 다른 플래들도 더 보이는 군요. 직접 찾아가 보시면 간단한 주석과 flag 결합 상태등을 좀 더 분명하게 확인하실 수 있습니다.

(그림 2) 소스코드에서 설정가능한 리스타트 매니저 지원 flag들 (in afxwin.h)

사실 어플리케이션 위자드에서 체크박스 항목으로 설정한 리스타트 매니저 지원사항들도 처음 기본 소스코드들을 생성해줄 때 위의 소스코드 한 줄을 선택사항에 맞춰 적절하게 적어주는 역할만을 할 뿐입니다.

(그림 3) 리스타트 매니저의 사용 설정 예시.



리스타트 매니저 사용에 대한 몇가지 팁들

팁 하나. 문서의 자동 저장 시간 간격 조절하기
CdataRecoveryHandler::SetAutosaveInterval() 함수를 사용하면 문서가 자동 저장되는 시간 간격을 조정할 수 있습니다. 기본값은 5분으로 설정되어 있습니다. CDataRecoveryHandler 클래스를 얻으려면 AfxGetApp()->GetDataRecoveryHandler()를 호출하면 됩니다.
혹은 CDataRecoveryHandler 클래스에 접근하지 않고도 바로 시간간격을 조절할 수도 있습니다. 바로 CWinApp 파생 클래스의 생성자에서 멤버변수 m_nAutosaveInterval에 값을 지정해주면 동일한 처리가 가능합니다.

(그림 4) 리스타트 매니저가 문서를 자동 저장하는 시간 간격 조절.

두가지 방법 모두 시간 단위는 밀리초로 입력합니다. 프로그램 실행 시 최초 1회만 시간설정을 하면 된다면 간단하게 CWinApp 생성자에서 설정해주면 되고, 사용자에게 설정 가능한 옵션으로 제공해 런타임에도 시간설정을 변경해야 한다면 SetAutosaveInterval() 함수를 사용하면 되겠지요.

팁 둘. 리스타트 매니저 직접 만들기(수정하기)
기본 제공되는 리스타트 매니저 만으로도 꽤나 유용한 기능을 사용할 수 있지만, 개발을 하다 보면 다른 방식의 데이터 저장 기능 등을 구현해야 할 때가 있습니다. 이런 경우에는 CWinApp::GetDataRecoveryHandler()를 오버로딩하고, CDataRecoveryHandler클래스를 상속받아 본인이 직접 구현한 CDataRecoveryHandler 파생 클래스를 붙여주면 됩니다.
참고로 윈도우 비스타 이전의 운영체제에서 이 함수가 시스템에 의해 호출되면 NULL을 리턴합니다. 앞서 말씀 드렸듯이 리스타트 매니저는 비스타 이후 OS에서만 지원됩니다. DataRecoveryHandler를 얻어내고자 하면 NULL이 반환되기 때문에, 비스타 이전 OS에서는 리스타트 매니저가 설정되지 않은 것처럼 동작하게 되지요. 그래서 리스타트 매니저를 사용하는 응용 프로그램을 비스타 이전 OS에서 구동하게 되어도 재시작 및 복구 기능만 비활성화 될 뿐, 오동작이나 다른 문제가 발생하지는 않습니다.

팁 셋. 업데이트 상황 재연하기

리스타트 매니저는 크래시가 발생한 경우 뿐만 아니라, 재실행을 필요로 하는 업데이트 진행시에도 사용할 수 있습니다. 크래시 상황을 재현하는 건 간단히 포인터 조작 오류 등을 넣어서 테스트 할 수 있고요, 인스톨러를 이용한 프로그램 업데이트 상황을 재연하고자 할 때에는 아래의 Rm...으로 시작하는 API 들을 사용해서 재현할 수 있습니다.

  • RmStartSession
  • RmRegisterResources
  • RmShutdown
  • RmRestart

이 API들은 윈도우 비스타부터 제공된 기능인 Restart Manager 를 설정 및 조작하는 함수들입니다. 응용 프로그램의 패치를 위해 윈도우즈 인스톨러 4.0 이상의 버전을 사용하는 경우는 자동으로 리스타트 매니저를 사용하게 되어 있고, 커스텀 인스톨러를 사용하는 경우도 역시 제공되는 API 함수들을 이용해 리스타트 매니저를 사용할 수 있습니다. 인스톨러에서 응용 프로그램의 종료 및 재시작을 제어하는 부분은 MFC 리스타트 매니저의 사용이라는 주제에서는 다소 벗어난 내용이므로, 본 포스팅에서는 다루지 않습니다. 보다 자세한 정보는 "메인 인스톨러에서 리스타트 매니저 사용하기(Using Restart Manager with a Primary Installer; http://msdn.microsoft.com/en-us/library/aa373681(VS.85).aspx)" 글을 참고하시기 바랍니다.
참고로 MSDN의 Restart Manager 부분을 한글 번역한 자료가 데브피아에 있습니다(http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=63&MAEULNo=24&no=11). 이것도 참고하시면 좋겠네요.


Outro
이번 포스팅에서는 MFC 응용 프로그램에서 실제로 리스타트 매니저를 사용하는 방법과 몇가지 팁들을 정리해 보았습니다. 다음 번에는 '활용하기' 편으로, 예제를 위주로 실제 사용시점에 참고할 만한 사항들을 확인해 보는 것으로 리스타트 매니저 편을 마치도록 하겠습니다.
그럼 다음 포스팅에서 또 만나기로 하고 여기서 줄이겠습니다.
감사합니다 ^^*


참고 자료 (reference)

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

Visual Studio 2010 공식 팀 블로그에 게시한 글을 스크랩 합니다.

Intro

안녕하세요. 이번에 팀블로그에 MFC 카테고리를 맡아 합류하게 된 꽃집총각 이라고 합니다.
앞으로 vs 2010에서 새롭게 선보인 MFC의 기능들을 주제로 포스팅을 하려고 합니다.
많은 관심과 격려 부탁드립니다 ^^

리스타트 매니저(Restart Manager) 의 소개
오늘은 첫 번째 포스팅으로, 응용 프로그램의 예상 못한 종료 상황에 요긴하게 사용될 기능인 리스타트 매니저(Restart Manager; 이하 한글 표기만 사용하겠습니다)에 대해 이야기 하고자 합니다.
리스타트 매니저는 윈도우 비스타 시스템에서 소개된 새로운 기능입니다. 이 기능은 응용 프로그램이 재시작을 필요로 하는 업데이트를 할 때나 처리하지 못한 exception 등으로 크래시가 발생했을 때, 데이터가 손실되지 않도록 도와줍니다. 비정상적인 종료가 발생했을 때 사용자가 이전에 작업 하면서 저장하지 못했던 데이터를 자동 저장 해주고, 응용 프로그램이 재시작 됐을 때 비정상 종료 전의 상태로 복구하는 것을 가능하게 만들어줍니다. 멋진 기능이죠!
쉽게 생각하면 이번에 인터넷 익스플로러 8의 새롭게 선보인 '세션 복구' 기능과 비슷하다고 볼 수 있습니다.

(그림 1) 인터넷 익스플로러 8.0의 세션 복구 기능.


익스플로러 버전 8 부터는 오류 등으로 인해 예상치 못한 종료가 발생하면 다음 재시작에서 위와 같은 창이 뜹니다. 세션 복구를 선택하면 종료 전에 열려있던 웹 페이지들이 다시 열리게 되죠. 이와 유사한 복구기능을 리스타트 매니저가 지원합니다. 

vs2010의 MFC를 통해 지원되는 리스타트매니저의 특징들은 간략히 아래와 같이 정리할 수 있습니다.
  1. vs2010으로 새롭게 제작되는 MFC 응용 프로그램들은 MFC 어플리케이션 위자드 (Application Wizard;응용 프로그램 마법사)를 이용해 아주 간단하게 프로그램 재시작 및 복구 기능을 사용할 수 있습니다.
  2. 리스타트 매니저 API중에서 개발자가 설정/변경 가능한 모든 부분들은 오버라이드 할 수 있는 가상멤버의 형식으로 제공됩니다.
  3. 예전에 만들어진 MFC 응용 프로그램에 기본 제공되는 리스타트 매니저의 기능을 붙이고 싶다면, vs2010으로 옮겨온 후 한 줄의 소스코드만 넣으면 됩니다!
  4. 문서파일을 다루는 응용 프로그램의 경우, 문서를 일정 주기마다 임시 파일로 자동 저장하는 기능이 추가됩니다. 자동 저장되는 주기 역시 개발자가 직접 설정할 수 있습니다. 응용 프로그램이 예외상황으로 크래시가 나는 경우, 해당 프로그램은 가장 마지막으로 백업된 임시 데이터를 복구해 새로 시작됩니다.
  5. 복구가 가능한 자동 저장된 버전의 문서가 존재하는 경우, 사용자에게 복구할 것인지를 묻는 UI 창이 기본 제공됩니다.


리스타트 매니저가 제공하는 기능들

MFC의 리스타트 메니저가 제공하는 기능은 크게 '재시작 기능'과 '복구 기능' 두 단계로 나누어 볼 수 있습니다.

1. 재시작 기능 (Restart Support) :
업그레이드나 크래시가 발생한 후 바로 재시작하는 기능. 이 기능은 모든 MFC 프로그램에서 지원됩니다. 다시말해 다이얼로그 기반이나 SDI, MDI 등의 도큐먼트-뷰 기반 등에 상관없이 MFC 프로젝트로 만든 실행파일은 모두 다 사용 가능하다는 의미입니다.

(그림 2) 크래시가 나면 해결 방법을 확인 중이라는 창이 뜹니다. 과연 뭘 하는 중일까요...

(그림 3) 그리고는 곧 '다시 시작하는 중'이라는 창이 뜨고, 프로그램은 자동 재실행 됩니다.

2. 복구 기능 (Application Recover Support) :
복구 기능은 또 다시 두 가지 기능으로 나누어 볼 수 있습니다.
  1. 종료되기 전에 열어두고 있었던 문서를 바로 다시 열어주는 기능.
  2. 자동 저장된 버전의 문서를 복구해 주는 기능.
복구 기능은 문서(Document)에 관련된 기능이니만큼, 도큐먼트-뷰 형식의 MFC 응용 프로그램에서만 사용 가능합니다. 다이얼로그 기반 프로젝트로 만든 실행파일에서는 사용할 수 없어요. 다시 정리하자면, 데이터를 복구한다는 의미는 도큐먼트-뷰 형식의 프로그램에서 Document에 해당하는 문서 데이터 부분을 대상으로 하는 말입니다. 개발자가 임의로 선언해 사용하는 (CDocument 클래스와 크게 상관이 없는) 커스텀한 데이터들은 복구 대상이 아닙니다. 
하지만 이건 기본 제공되는 리스타트 매니저에 대한 설명입니다. 추가 동작이 필요하다면 기본 기능을 확장할 수 있는 가상함수 형식의 인터페이스들을 통해 직접 기능 확장을 하면 됩니다. 또한 기본 제공 기능만으로도 문서형식의 데이터들은 손하나 대지 않아도 크게 손색이 없는 복구 기능을 붙일 수 있습니다.

(그림 4) 복구 가능한 문서가 존재할 때 사용자에게 노출되는 UI.

자동저장된 파일이 존재해 복구가 가능한 경우, 프로그램은 재실행 되자 마자 (그림 4)와 같은 대화상자를 출력합니다.


Outro
실제로 리스타트 매니저를 사용하는 방법까지 정리하면 포스팅이 너무 길어질 것 같네요. 일단은 첫 포스팅이기도 하니 가볍게 기능 소개만으로 끝을 맺고, 다음 포스팅에서는 지극히 쉽고 심플한 리스타트 매니저 사용법에 대해서 정리해 보겠습니다. 얼른 적용해 보고 싶으신 분들은 아래에 있는 참고자료 링크들을 찾아가 보세요. 정말 너무너무 쉽습니다!
그리고 리스타트 매니저는 비스타 이후의 OS에서 지원가능합니다. (비스타, 윈도우7, 윈도우 서버 2008) 그럼 리스타트 매니저를 사용하는 MFC 프로그램이 xp에서 크래시가 나면 어떻게 될까요? 자동 저장 기능은 그냥 disable만 되고 마는 것일까요? 이런 부분들도 차곡차곡 정리해서 포스팅 하도록 하겠습니다.
글 내용 중 잘못된 부분이나 부족한 부분은 댓글로 의견 주세요. 참고 반영 하겠습니다.
감사합니다 ^^*

참고자료 링크 (reference)

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

DirectShow 사용 코드에서 데드락이 발생해, 해결 방법을 찾아보다가 알아낸 사실. 요약하자면 '메인스레드가 아닌 sub-thresd에서 SendMessage를 부르면 데드락의 발생 가능성이 있고, 이를 해결하기 위해 이런 이런 방법이 있다' 이말인거다. DirectShow 자체에서의 대처방안도 있지만 그건 별도로 포스팅한다.

원본 출처 : http://todayis.tistory.com/157


'SendMessage()'라는 함수를 통해서 프로그래머는 일종의 사용자 메시지를만들 수 있다.
같은 스레드 내에서 이 함수를 사용할 경우에는 크게 문제 될 것이 없지만, 다른 스레드로 SendMessage()를 사용해 메시지를 날리게 될 경우 상당히 좋지않은 결과를 초래할 수도 있다.

SendMessage() 함수는 메시지를 받은 윈도우 프로시저가 리턴하기 전까지는 리턴하지 않기 때문에, 다른 스레드로 메시지를 날리게 될 경우 그 스레드에서 리턴하지 않는 한 SendMessage()를 한 스레드는 홀드되는 상태로 남게 된다. '데드록(DeadLock)'가 되는 것이다.

이를 해결하는 방법으로 여러가지가 있겠지만, 간단히 몇가지를 살펴보면 다음과 같다.
'InSendMessage()''ReplyMessage()'를 이용하여 SendMessage()로 부터 받은 메시지를 처리하는 부분이다.
if(InSendMessage())             // 다른 스레드로부터 메시지가 들어왔는지 확인
    ReplyMessage(TRUE);      // SendMessage() 로부터 전달된 메시지를 바로 리턴한다.

위와 같은 처리를 해주면, SendMessage()를 호출한 윈도우는 리턴되지 않더라도 즉시 다른 일을 할 수 있다.

다른 방법으로는 SendMessage()를 이용하지 않고 다음의 함수를 사용하는 방법이다.
BOOL SendNotifyMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
LRESULT SendMessageTimeout( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam,              
                                                UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult );

'SendNotifyMessage()' 함수는 SendMessage()와 유사하지만, hWnd 가 다른 스레드의 윈도우일 경우 대기를 하지 않고 즉시 리턴하는 함수이다.
'SendMessageTimeout()' 함수는 지정한 시간이 지나면 메시지의 처리 여부와 상관없이 즉시 리턴함으로 메시지를 보낸 윈도우가 홀드되는 것을 막는다.

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요

가장 전반적이고, 명료한 설명을 담은 포스트.
http://himskim.egloos.com/1524155


네이트 통. [General > Vista 호환성] 카테고리의 링크글들 참조.
http://tong.nate.com/ryu0423



http://ko.wikipedia.org/wiki/%EC%82%AC%EC%9A%A9%EC%9E%90_%EA%B3%84%EC%A0%95_%EC%BB%A8%ED%8A%B8%EB%A1%A4 (사용자 계정 컨트롤)


http://blog.naver.com/jingon23?Redirect=Log&logNo=50020334156

▣ 버튼 등의 컨트롤을 XP Style로 보여주기 위한 Manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity
      name="Microsoft.Windows.MyCoolApp"
      processorArchitecture="x86"
      version="1.0.0.0"
      type="win32"/>

   <description>Application description here</description>
   <dependency>
      <dependentAssembly>
         <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
         />

      </dependentAssembly>
   </dependency>
</assembly>



▣ Vista에서 권한 상승을 위한 Manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
      <security>
         <requestedPrivileges>
            <requestedExecutionLevel
               level="requireAdministrator"
               uiAccess="False"/>

         </requestedPrivileges>
      </security>
   </trustInfo>
</assembly>


    ▷ level
        - asInvoker : 부모 Process와 같은 권한으로 실행
        - highestAvailable : 현재 사용자 권한에서 가능한 최고 권한으로 실행
        - requireAdministrator : Admin 권한으로 실행. 일반 사용자인 경우 권한 상승 확인 과정 거침
    ▷ uiAccess
        - false :
        - true :


▣ XP Style + Vista 권한 상승 Manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity
      name="Microsoft.Windows.MyCoolApp"
      processorArchitecture="x86"
      version="1.0.0.0"
      type="win32"/>

   <description>Application description here</description>
   <dependency>
      <dependentAssembly>
         <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="x86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
         />

      </dependentAssembly>
   </dependency>
   <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
      <security>
         <requestedPrivileges>
            <requestedExecutionLevel
               level="requireAdministrator"
               uiAccess="False"/>

         </requestedPrivileges>
      </security>
   </trustInfo>
</assembly>



[출처] [불펌] XP Style + Vista 권한 관련 Manifest 사용|작성자 로한


 권한 상승이 필요한 COM module 만들기

http://cpplog.tistory.com/5


mt.exe 버전에 대한 오류
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1255791&SiteID=1

요약 하자면, version 5.2.3790.2075 dated 10/19/2006 인 mt.exe는 정상 동작 한다는 소리. Vista SDK에 포함되어 있다고 함.

Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요


Using the ATL Windowing Classes


이글은 코드구루의 내용을 연습삼아 번역한 것이라서 번역은 엉망이다.


요즘 ATL 이란 놈이 머리를 아프게 한다...

ATL은  배우는 것은 힘들지만 MFC보다 작기 때문에 배우는데 짧은 기간이 걸린다고 한다.

근데 힘들다는 것은 그만큼 오래 걸린다는 것고 같은 의미라고 느껴진다.

세상에 쉬운것은 없군...

ATL 은 COM을 지원하기 위해 디자인 되었지만 윈도우를 모델링 하는 클래스 영역도 포함한다고

한다. 그리고 ActiveX 같은 윈도우를 가지는 객체도 만들수 있다.

아래는 ATL 에서의 주요 윈도우 클래스들이다.


 CWindow - 윈도우를 조작하기 위한 Win32 APIs의 작은 랩퍼 클래스이다.

                    윈도우 핸들과 HWND 를 CWindow 로 변환하는 오퍼레이터를 포함한다.

                    그러므로 윈도우 핸들을 필요로하는 어떤 함수에 CWindow 오브젝트를

                    넘길수 있다.

 CWindowImpl - 이미 존재하는 윈도우를 서브클래싱 하거나 이미 존재하는 클래스를

                    수퍼클래싱 하거나 , 윈도우 베이스의 새로운 윈도우를 만들때

                    사용한다.
 CContainedWindow - 다른 클래스의 메세지 맵을 위한 메세지 경로를 구현한 윈도우

                    클래스이다. 이 클래스는 하나의 클래스에 메세지 처리를 집중하는 것을 허락한다.

 CAxWindow - 컨트롤을 만들거나 존재 하는 컨트롤에 붙임으로써 ActiveX control

                    호스트 윈도우 구현을 지원한다.
 CDialogImpl - 모달이나 모달리스 다이얼로스를 구현한다. IDOK 나 IDCANCEL 같은

                    기본 메세지 경로를 지원한다.
 CSimpleDialog - 단순 모달 다이얼로그를 주어진 리소스 ID로 구현한다. IDOK나 IDCANCEL과

                    같은 기본 메세지 맵을 기지고 있다.
CAxDialogImpl - CDialogImpl 과 같이 모달과 모달리스를 를 구현하는 베이스 클래스로 사용되

                    며 상속된 클래스에 기본 메세지 맵을 제공한다.
                    추가로 ActiveX 컨트롤을 지원한다. ATL 오브젝트 위저드에서 CAxDialogImpl에

                    상속된 클래스를 프로젝트에 넣은 것을 지원한다.

CWndClassInfo -  새로운 윈도우 클래스의 정보를 보관한다.

                    특별히 WNDCLASSEX를 캡슐화한다.

CWndTraits and CWinTraitsOR - ATL 윈도우 오브젝트의 스타일을 캡슐화한다.

Message Maps

ATL을 공부하는데 시간을 투자하는 것이 꺼려지는 이유 중 하나가 괴상한 메세지 맵이다.
그러나 MFC의 메세지의 매크로를 처음 봤을 때 이해가 됬는가?
사실 ATL의 맵은 더 쉽다....(과연 그럴까?)

베이스 추상클래스의 CMessageMap 으로부터의 상속과 CWindowImpl 에서 상속된 클래스들은

윈도우 메세지들을 처리할 수 있도록 해준다.
CMessageMap 에 순수 가상함수로 정의 된 ProcessWindowMessage 는 CWindowImpl에서

상속된 클래스의 BEGION_MSG_MAP 과 END_MSG_MAP 매크로를 통해 구현된다.

ATL은 MFC와 유사한 포멧의 메세지 핸들러 가졌고 추가적으로 BOOL& 형의 아규먼트를 받는다.
이 아규먼트는 메세지가 진행중인지 아닌지를 나타내고 TURE 가 기본값이다.

FALSE 은 메세지를 가지고 있지 않다는 것이다.
FALSE인 경우 ATL 은 메세지 맵에서 보다 먼 핸들러 함수를 찾는다.

 FLASE를 셋팅함으로 어떤 액션을 가 할 수 있는데 기본 프로세싱을
허용하거나 메세지 핸들링을 끝내기 위해 다른 핸들러 함수를 허용할 수 있다.


메세지 맵의 매크로는 다음과 같이 3가지 있다.
     1. 모든 메세지를 위한 메세지 핸들러
     2. WM_COMMAND 메세지를 위한 커맨드 핸들러
     3. WM_NOTIFY 메세지를 위한 통지 핸들러

MESSAGE_HANDLER 핸들러 함수를 위한 윈도우 메세지 맵
COMMAND_HANDLER 메뉴 나 통지 코드, 컨트롤, 단축키의 ID 에 기초한 WM_COMMAND 메세지 핸들러 함수의 메세지 맵
COMMAND_ID_HANDLER 메뉴 , 컨트롤, 단축키의 ID 에 기초한 WM_COMMAND 메세지 핸들러 함수의 메세지 맵
COMMAND_CODE_HANDLER 통지 코드에 기초한 WM_COMMAND 메세지 핸들러 맵
NOTIFY_HANDLER 컨트롤 식별자나 통지코드에 기초한 WM_NOTIFY 메세지 핸들러
NOTIFY_ID_HANDLER 컨트롤 식별자에 기초한 WM_NOTIFY 메세지 핸들러
NOTIFY_CODE_HANDLER 통지 코드에 기초한 WM_NOTIFY 메세지 핸들러

예로 ATL 다이얼로그 폼에서 클래스에서 자식 컨트롤를 가진다면 아래와 같이 메세지 맵이 보여진다.

BEGIN_MSG_MAP(CMyDialog) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1) COMMAND_ID_HANDLER(IDOK, OnOK) COMMAND_CODE_HANDLER(EN_ERRSPACE, OnErrEdits) NOTIFY_HANDLER(IDC_LIST1, NM_CLICK, OnClickList1) NOTIFY_ID_HANDLER(IDC_LIST2, OnSomethingList2) NOTIFY_CODE_HANDLER(NM_DBLCLK, OnDblClkLists) END_MSG_MAP()
MFC 아키텍쳐에서는 서로 다른 메세지 스키마 루틴의 사용도 허락한다. 
:(상위 계층을 통한 윈도우 메세지 루틴과 가로지는 doc-view 클래스를
가로지는 커맨드 메세지)
첫 스키마는 탬플릿 클래스 구현의 부분적인 느슨한 계층을 가진 ATL 에서는
사용할 수 없다.
두번째 스키마도 MFC 아키텍쳐와 동등하지 않기 때문에 적절치 않다
.
(...먼소린지.. @@;)

Alternate Message Maps

Alternate Message Map은 CContainedWindow 를 통해 사용하도록 디자인 되었다.

이 클래스는 다른 클래스로 메세를 보낼때 쓰여진다.
이는 부모 윈도우에 의해 자식 윈도우로 메세지를 보내는 것도 허락된다.
CContainedWindow 생성자는 메세지 맵의 주소를 필요로 한다. 그리고 메세지 맵내에
선택적 메세지맵의 ID를 필요로 한다.
예로, 윈도우 컨트롤에 기초한 ATL 컨트롤을 만들 때 객체 마법사는 CContainedWindow 멤버가
끼워 넣어진 컨트롤을 위한 클래스를 생성한다.
본질적으로, 이 컨테이너 윈도우는 ActiveX control 에 기초한 특유의 윈도우 컨트롤  슈버클래싱
한다. 

class ATL_NO_VTABLE CMyButton : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CMyButton, &CLSID_MyButton>, public CComControl<CMyButton>, //... { public: CContainedWindow m_ctlButton; CMyButton() : m_ctlButton(_T("Button"), this, 1) { } BEGIN_MSG_MAP(CMyButton) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) CHAIN_MSG_MAP(CComControl<CMyButton>) ALT_MSG_MAP(1) END_MSG_MAP() //...
버튼은 WNDCLASS 스타일이고 캡션이 없다. 이 포함 클래스의 포인터는 두가지 파라미터를
보내고 , 그리고 첫변째 값은 CContainedWindow 생성자 alternate messge map에
대한 생성자가 넘어온다.
만약 컨트롤의 WM_LBUTTONDOWN을 핸들링 하기 원한다면 아래와 같이 메세지 맵을

업데이트 하면 된다. 이와 같은 벙법은 부모윈도우의 메세지맵에 메세지가 보내어 지고,
메세지 맵의 선택적 부분에도 보내어 진다.





BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
END_MSG_MAP()

그래서 alternate message map들은 메세지 핸들러들을 통합하기 위한 전략이다.

Chained Message Maps

Chaining message maps 은 다른 클래스나 오프젝트안의 메세지 맵을 통해 메세지를 보낸다. ATL은 몇몇 map - chaining 매크로를 제공한다.

CHAIN_MSG_MAP(theBaseClass) 베이스 클래스의 기본 메세지 맵에 메세지를 보낸다.
CHAIN_MSG_MAP_ALT(theBaseClass, mapID) 베이스 클래스의 alternate 메세지 맵에 메세지를 보낸다.
CHAIN_MSG_MAP_MEMBER(theMember) 데이터 맴버로 명시된 기본 메세지 맵에 메세지를 보낸다.(CMessageMap에서 상속된)
CHAIN_MSG_MAP_ALT_MEMBER(theMember, mapID) 명시된 데이터 맴버의 alternate 메세지 맵에 메세지를 보낸다.

예로, 윈도우 컨트롤에 기초한 ATL 컨트롤을 만들 때 오브젝트 마법사는 다음과 같은 코드를 작성
한다.

BEGIN_MSG_MAP(CMyButton)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CComControl<CMyButton>)
ALT_MSG_MAP(1)
END_MSG_MAP()

이 WM_CREATE 와 WM-SETFOCUS  메세지의 명세는 이 클래스에 의해서 조정될 것이다.
그러나 어떤 다른 메세지는 베이스 클래스인 CComControl<> 에 보내어질 것이다.
또한 이 메세지 핸들에 false 가 지정되어 있다면 이들 메세지는 더 먼 핸들링을 위해
베이스 클래스를 지나갈 것이다.
데이터 맴버에 보낸 메세지는 이와 같이 맵을 업데이트 해야 한다.

BEGIN_MSG_MAP(CMyButton) MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) CHAIN_MSG_MAP(CComControl<CMyButton>) ALT_MSG_MAP(1) CHAIN_MSG_MAP_MEMBER(m_ctlButton) END_MSG_MAP()
이것은  m_ctlButton 이 컨테이너 윈도우의 멤버이라는 가정하이고,
CContainedwindow 에서 상속받은 클래스의 인스턴스이다는 전제하에 이다.
class CMyButtonControl : public CContainedWindow { //... BEGIN_MSG_MAP(CMyButtonControl) MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) END_MSG_MAP() 

그래서, chained 메세지 맵은 다른 메세지 맵으로 부터의 chain - route 메세지들을 허락한다.
MFC에 의 변환된 스키마를 보내는 메세지 개념과 유사하다.

Reflected Messages

부모 윈도우는 reflected 메세지로서 메세지를 뒤로 보내는 것에 의해 자식 컨트롤에게 메세지 를 보낸것을 조정할 수 있다.
이는 원본 메세지 + 플래그 일 것이다. 컨트롤이 메세지를 얻었을 때 컨테이너로 부터 반사된 것으로서 식별이 가능하다.
예로 자식 컨트롤에서 WM_DRAWITEM 메세지를 조정하고 싶을 때이다.
이와 같은 작업을 위해 REFLECT_NOTIFICATIONS 는 반드시 부모 윈도의 메세지맵에 나타난다.
BEGIN_MSG_MAP(CMyDialog) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDOK, OnOk) NOTIFY_HANDLER(IDC_EDIT1, EN_CHANGE, OnChangeEdit1) REFLECT_NOTIFICATIONS() END_MSG_MAP()
REFLECT_NOTIFICATIONS 매크로는 CWindowImpl::ReflectNodifications를
호출하기 위해 확장한다.
template <class TBase> LRESULT CWindowImplRoot<TBase>::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
함수는 메세지르 보내는 자식 컨트롤 을 위한 윈도우 핸들을 추출한다.
그리고 메세지를 아래와 같이 보낸다.
::SendMessage(hWndChild, OCM_ _BASE + uMsg, wParam, lParam);

자식 윈도우 reflected message 핸들들DMS
MESSAGE_HANDLER 매크로 와 olectrl.h에 미리 정의된 reflected 메세지 IDs

를 사용한다.

BEGIN_MSG_MAP(CMyContainedControl) MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP()

이 예제에서 보여진 OCM_DRAWITEM  은 다음과 같이 정의 되어 있다.
#define OCM_ _BASE (WM_USER+0x1c00) #define OCM_COMMAND (OCM_ _BASE + WM_COMMAND) //... #define OCM_DRAWITEM (OCM_ _BASE + WM_DRAWITEM)

DEFAULT_REFLECTION_HANDLER 매크로는 원본 메세지와 DefWindowProc를 지나온 메세지를 변환한다.

Recipe 1: ATL Window App

 

이것은 ATL Window 클래스를 이용해 단순한 어플리케이션을 쉽게 만들 목적으로
디자인된 단순한 예제이다.

ATL Com AppWizard 는 COM 오브젝트를 위한 호스트를 제공하기 위해 디지인 되어 있다.
만약 non-COM 어플리케이션 이라면 위저드는 더 많은 것을 필요로한다.
그래서 ATL 어플리켄이션을 만들기 위해 두가지 선택이 필요하다.

     1. ATL COM AppWizard EXE server
     2. 수동적으로 추가해 주는 Win32 Application

여기서는 위저드가 생성한 코드를 피하고 가벼운 두번째 방법으로 따르겠다.
 1. Win32 Application 을 Simple option 으로 생성
---ATL
 2. stdafx.h에 ATL 헤더와 외부 참조 CComModule 를 추가한다.
  #include <atlbase.h>
  extern CComModule _Module;
  #include <atlcom.h>
  #include <atlwin.h>

 3. main CPP 에 ATL 객체 맵을 추가하고 CComModule 오브젝트를 정의한다.
  CComModule _Module;
 
  BEGIN_OBJECT_MAP(ObjectMap)
  END_OBJECT_MAP()

 4. 프로젝트와 같은 이름의 IDL 파일을 생성한다.
  library SomethigOrOther
  {
  };
--Window
 5. CWindowImpl<CMyWidnow> 를 베이스 클래스로 한 CMyWindow를 생성한다.
    그리고 stdafx.h 를 인클루드 해 준다.
 6. 새로운 클래스에 메세지 맵을 정의한다.
  BEGIN_MSG_MAP(CMyWindow)
  END_MSG_MAP()

 7. WM_DESTROY 메세지를 위한 핸들러를 추가하고 아래오 같이 코드를 넣어준다.
  LRESULT OnDestroy(UINT uMsg, WPARARM wParam, LPRARAM lParam, BOOL& bHandled)
  {
   PostQuitMessage(0);
   return 0;
  }

 8. 유사하게 WM_PAINT 핸들러를 추가하고 코드를 추가한다.
  LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bhandled)
  {
   PAINTSTRUCT ps;
   HDC hDC = GetDC();
   BeginPaint(&ps);
   TextOut(hDC,0,0,_T("HelloWorld"),11);
   EndPaint(&ps);
   return 0;
  }

----WinMain
 9. WinMain 의 위와 아라에 CComModule::Init과 Term을 넣는다.
  _Module.Init(NULL,hINstance);
  //....
  _Module.Term();
 10.  Init 과 Term 사이에 윈도우 클래스의 인스턴스를 정의하고 Create을 호출해서 초기화한다.
  CMyWindow wnd;
  wnd.Create(NULL, CWindow::rcDefault, _T("Hello"),
                    WS_OVERLAPPEDWINDOW|WS_VISIBLE);
  MSG msg;
  while(GetMessage(&msg,NULL,0,0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

 11. 이제 빌드하고 실행해보라.
      단순히 클라이언트 영역에 "Hello World" 메세지를 볼 수 있을것이다.

Recipe 2: ATL Frame-View App

이 프로젝트에서는  ATL 윈도우 클래스 들로 MFC SDI Frame_view 패러다임 모델을 생성 할
것이다. 뷰와 메뉴와 다이얼로그를 넣어볼 것이다.
 1. Recipe 1 과 같이 프로젝트를 생성한다.
---Mainframe Window
 2. CWindowImpl<CMainFrame,CWindow,CFrameWinTraits>로 부터 상속받은
     CMainFrame을 생성한다.  WNDCLASS 구조체 이름을 정의하고  메세지 맵을 추가한다.
  DECLARE_WND_CLASS(_T("MyFrame"))

  BEGION_MSG_MAP(CMainFrame)
  END_MSG_MAP()
 3. OnFinalMessage를 상속받아야 한다.
- 몇개 안되는 ATL 가상함수로 WM_NCDESTROY 메세지를 받았을 때 호출된다.
  void OnFinalMessage(HWND /*hwnd*/)
  {
   ::PostMessage(0);
  }

 4. 이제 WinMain 에 몇몇 코드를 넣는다. 위와 아래에 CComModule의 초기화와 종결화를 넣는다.
  _Module.Init(NULL, hInstance, NULL);
  _Module.Term();

 5. mainframe를 include 하고 Init과 Term 사이에 프레임의 인스턴스를 정의하고
   Create를 호출해서 초기화한다.
  CMainFrame mf;
  mf.Create(GetDesktop(),CWindow::reDefault, _T("My App"),0,0,0);
  mf.ShowWindow(SW_SHOWNORMAL);
  MSG msg;
  while(GetMessage(&msg,0,0,0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

 6. Bake and Serve
-- View Window
 7. 이제 view 클래스를 넣어보자.
    CWindowImpl<CViewWin,CWindow,CWinTraits>에서 상속 받는 CViewWin을 생성하자.
 8. stdafx.h를 인클루드하고 WNDCLASS와 메세지 맵을 정의한다.
    CMainFrame 에 맴버변수로 CViewWin의 인스턴스를 추가하고
    frame의 WM_CREATE에 view 를 생성하라.
  LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  { 
   m_wndView.Create(m_hWnd, CWindow::rcDefault, _T("MyView"),0,0,0);
   return 0;
  }

 9. 또한 WM_SIZE에서 view의 사이즈를 구현해 준다.
  LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  {
   RECT r;
   GetClient(&r);
   m_wndView.SetWindowPos(NULL,&r,SWP_NOZDRDER | SWP_NOACTIVATE);
   return 0;
  }


User Interface

이제 WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP 메세지를 핸들링 할것이다

 

          

10. 먼저, 두개의 POINT 데이터를 맴버로 두고, 생성자에서 -1 로 초기화한다.
     시작 부터 끝날 때까지 각각의 라인을 그리는 트랙을 유지해야한다.
     m_stratPoint.x = m_startPoint.y = -1;
     m_endPoint.x = m_endPoint.y = -1;

 11. 다음으로 OnLButtonDown,에서 라인의 시작점을 저장한다.
  LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam,
                                        LPARAM lParam, BOOL& bHandled)
  {
   m_startPoint.x = LOWORD(lParam);
   m_startPoint.y = HIWORD(lParam);
   return 0;
  }
 12. OnLButtonUP 에서 시작 점을 초기화한다.
  LRESULT OnLButtonUP(UINT uMsg, WPARAM wParam,

                                    LPRARAM lParam, BOOL& bHandled)
  {
   m_startPoint.x = m_startPoint.y = -1;
   return 0;
  }

 13. OnMouseMove 에서는 좀더 많은 작업을 해줘야 한다.

      먼저 끝점을 저장한다. 그리고 DC를 얻어 MoveToEX 와 LineTo 를
     사용해서 라인을 그린다. 마지막으로 끝점을 시작점에 저장한다.
  LRESULT OnMouseMove(UINT uMsg, WPARAM wParam,
                                       LPARAM lParam, BOOL& bHandled)
  {
   m_endPoint.x = LOWORD(lParam);
   m_endPoint.y = HIWORD(lParam);
   HDC hdc = GetDC();
   if(m_startPoint.x != -1)
   {
    MoveToEx(hdc, m_strarPoint.x, m_startPoint.y , NULL);
    LineTo(hdc, m_endPoint.x, m_endPoint.y);
    m_startPoint.x = m_endPoint.x;
    m_startPoint.y = m_endPoint.y;
   }
  }

 

Recipe 3: ATL Menus

펜 컬러를 넣는 단순한 메뉴를 넣어보자
 1. 프로젝트를 계속 이어가서, 첫번째로, 뷰 클래스에 COLORREF 형의 m_color 맴버 변수를
     넣는다.  초기화는 생성자에서 블랙으로 한다.
    그리고 이것을 사용해서 OnMouseMove핸들러에서 펜을 만들고 DC 에서 선택한다.
     DC를 사용후에는 원래 펜을 선택한다.
  HPEN hp = CreatePen(PS_SOLID, 2, m_color);
  HPEN op = (HPEN)SelectObject(hdc, hp);


 2. 메뉴에 리소스를 삽입한다. 상위 레벨에 Color 이라는 캡션을 추가하고 메뉴 아이템으로
     red, green, blue 를 추가한다.
 3. WinMain 에서 리소르 헤더를 인클루드하고 mainframe 을 생성하기 전에 메뉴를 로드하고
     Create 를 호출한다.
  HMENU hMenu = LoadMenu(_Module.GetResourceInstance(),
                                            MAKEiNTERESOURDE(IDR_MENU1));
  mf.Create(GetDesktopWindow(), CWindow::rcDefault, _T("My App"), 0,0, (UINT) hMenu);

 4. 메뉴 아이템을 위해 command 핸들러를 main frame 에 넣어준다.
  BEGIN_MSG_MAP(CMainFrame)
   MESSAGE_HANDLER(WM_CREATE, OnCreate)
   MESSAGE_HANDLER(WM_SIZE, OnSize)
   COMMAND_ID_HANDLER(ID_COLOR_RED,OnColorRed)
   COMMAND_ID_HANDLER(ID_COLOR_GREEN, OnColorGreen)
   COMMAND_ID_HANDLER(ID_COLOR_BLUE, OnColorBlue)
  END_MSG_MAP()

 5. 3개의 핸들러에 대해서 해야할 작업을 적어준다.
  LRESULT OnColorRed(WORD wNotifyCode, WORD wID, HWND hWndCtrl, BOOL& bHandled)
  {
   m_wndView.m_color = RGB(255,0,0);
   return 0;
  }


Recipe 4: ATL Dialogs

단순한 다이얼로그 리소스를 추가해 보자.

1. 위의 프로젝트에 이어서 "View" 메뉴를 넣고 "Dialog"메뉴 아이템을 넣자.
   command 핸들러를 main - frame 메세지 맵에 넣는다.
  COMMAND_ID_HANDLER(ID_VIEW_DIALOG, OnViewDialog)
 2. 여기서는 CSimpleDialog 를 직접 사용할 것이다.
    먼저 새로운 다이얼로그 리소스를 넣고, static box 를 넣는다.
    메뉴에 대한 명령 핸들러에서 이것을 이용한다.
  LRESULT OnViewDialog(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
   CSimpleDialog<IDD_DIALOG1> dlg;
   dlg.DoModal();
   return 0;
  }

 3. 좀더 멋을 부리고 싶다면 CSimpleDialog 로 부터 상속 받을 수 있다.
    그래서 첫번째로 리소스 에디터에서 combobox 를 넣는다.

    

4. 그리고 CListDialog 라는 CSimpleDialog<IDD_DIAOG1>로 부터 상속 받은 새로운 클래스를
   만든다. stdafx.h 를
   인클루드 하는 것을 잊지말라. 새로운 클래스에 메세지 맵을 추가하고 맵에
   베이스 클래스의 메세지 맵에 묶기 위한 코드를 기입한다.
  BEGIN_MSG_MAP(CListDialog)
   CHAIN_MSG_MAP(CSimpleDialog<IDD_DIALOG1>)
  END_MSG_MAP()
 
 5. 다음으로 combobox에 문자열을 추가하기 위해 WM_INITDIALOG에 코드를 추가해
    줄 것이다. 먼저 콤보박스에 sort 속성을 제거한다.
  LRESULT OnIntiDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
  {
   CWindow combo(GetDlgItem(IDC_COMBO1));
   combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Red");
   combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Green");
   combo.SendMessage(CB_ADDSTRING, 0 , (LPARAM)"Blue");
   return CSimpleDialog<IDD_DIALOG1>::OnInitDailog(uMsg,wParam,lParam,bHandled);
  }
 
6. Note : CHAIN_MSG_MAP 매크로 는 맵의 마지막 진입점이다.
 7. DDX/DDV는 어떻게 하는가?
  COMMAND_ID_HANDLER(IDOK, OnOK)
 8. OnOK는 아래와 같이 적어준다. m_text라는 CComBSTR 형 맴버변수에 텍스는 저장할 것이다.
  LRESULT OnOK(WORD, WORD nID, HWND, bool&)
  {
   CComBSTR text;
   GetDlgItemText(IDC_COMBO1,m_text.m_str);
   ::EndDilog(m_hWnd, wID);
   return 0 ;
  }
 9. 마지막으로 다이얼로그로 부터 추출한 텍스트를 사용하기 위해 메뉴 아이템 핸들러를
    업데이트 한다.
  LRESULT OnViewDialog(WORD wnotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
  {
   CListDialog dlg;
   if(IDOK ==  dlg.DoModal())
   {
    if(dlg.m_text == CComBSTR("Red"))
     m_wndView.m_Color = RGB(255,0,0);
    else if(dlg.m_text == CComBSTR("Green"))
     m_wndView.m_color = RGB(255,0,0);
    else if(dlg.m_text == CComBSTR("Blue"))
     m_wndView.m_color = RGB(0,0,255);
   }
   return 0;
  }

만약 툴바와 상태바로 이 app 를 확장하고 싶다면 ATL CStatusBarCtrl 과 CToolBarCtrl 클래스를
이용할 수 있다. 이것은 atlcontrols.h 에정의 되어 있지만 MS가 기본적으로 지원하지 않는다.

'프로그래밍 팁 > Win32/MFC' 카테고리의 다른 글

Windows API 메시지 데드록(DeadLock) 관련 함수  (0) 2009.09.18
manifest 관련  (0) 2008.11.03
ATL::CWindow 사용하기  (0) 2008.07.15
IME  (0) 2008.05.06
[MFC] 리스트 컨트롤 더블클릭 이벤트  (0) 2008.01.24
[MFC] tooltip 만들기  (0) 2008.01.15
Posted by leafbird 트랙백 0 : 댓글 0

댓글을 달아 주세요