안녕하세요 ~ MFC 카테고리의 꽃집총각 입니다.

이번에는 그 동안 함께 멀티터치를 공부하면서 다뤄보았던 예제 프로그램들의 소스를 정리해 보았습니다.

아래 코드를 다운 받으세요 :)


Gesture : WM_GESTURE 메세지를 이용해 화면상의 사각형을 이동, 회전, 확대/축소 하는 예제.

Touch : WM_TOUCH 메세지를 이용해 클라이언트 영역에 터치 입력 궤적을 따라 선을 그려주는 예제.

Manipulation : IManipulationProcessor 인터페이스를 활용해 Microsoft Surface에서와 같은 Manipulation 움직임을 제어하는 예제.

Manipulation Inertia : IManipulationProcessor, IInertiaProcessor 인터페이스를 모두 활용, 조작(Manipulation) 및 관성(Inertia) 모두를 적용한 예제.

조작(Manipulation)과 관성(Inertia) 처리 방법은 아직 포스팅으로 설명 드리지 못한 부분입니다만 샘플 코드 정리하면서 함께 정리해 공개합니다.

모든 코드는 기본적으로 MS에서 제공하는 예제 코드를 기반으로 하고, 제가 포스팅 하면서 추가로 변경한 부분들의 코드도 일부 포함되어 있습니다. 참고 부탁 드립니다. 

신고
크리에이티브 커먼즈 라이선스
Creative Commons License

Intro

안녕하세요 ~ MFC 카테고리의 꽃집총각 입니다.

지난 포스팅에서는 드디어 WM_TOUCH를 이용한 멀티터치 UX의 구현방법에 대한 이야기가 시작되었습니다. channel9에 공개된 예제를 함께 작성해 보면서 WM_TOUCH 메세지를 다루는 방법을 알아보고 있는데요, 어떨 때 WM_TOUCH를 이용해야 하는지, WM_GESTURE가 아닌 WM_TOUCH를 받고 싶을 땐 어떻게 처리해 주어야 하는지, 그리고 메세지를 받을 때마다 호출되는 CWnd::OnTouchInput 등에 대한 설명이 있었습니다.

그리고 실제로 OnTouchInput() 함수에서 터치 메세지를 제어할 예제 프로그램인 ‘TouchPad’ 프로젝트를 생성하고 기본적인 전처리 단계를 알아보았지요. 이에 이어서 오늘은 OnTouchInput() 함수의 구현부터 본격적으로 알아보도록 하겠습니다 ^^

 

Task 3 : 드로잉 코드는 가져다 씁시다. 터치에 집중해야죠 ㅎㅎ

지금 함께 작성해 보려고 하는 TouchPad는 윈도우 그림판처럼 멀티 터치 입력에 대해 라인을 그려주는 예제입니다. 한 발 더 나가서, 서로 다른 입력으로 드로잉 하는 라인은 색상도 다르게 표시되도록 할거예요. 하지만 사용자로부터 입력된 터치 정보의 처리방법에 집중하기 위해, 드로잉 코드는 샘플에서 제공된 클래스를 사용하도록 하겠습니다. 먼저 예제 프로젝트에 아래의 파일을 추가해 주세요.

 이 클래스들을 이용해 우리가 구현할 코드에 대해 간략히 설명해 보기로 하지요. 우리는 각각의 터치 입력에 대해서 라인(stroke. 스트로크라고 부르겠습니다.)을 그려내는 기능을 추가할 것인데, 이를 이해서 두 개의 스트로크 모음을 유지할겁니다. 한 쪽에서는 이미 드로잉이 끝난 스트로크 데이터를 모아 유지하게 되고, 또 다른 나머지 한 쪽에서는 현재 터치입력으로 드로잉하고 있는 데이터를 유지하게 됩니다. 스크린을 터치하는 한 개 혹은 다수의 입력을 받아 m_StrkColDrawing 멤버변수가 유지하는 스트로크 정보에 CPoint 형식의 좌표 데이터를 추가합니다. 그리고 스크린에서 손가락을 떼면, 손가락을 따라 그려지고 있던 스트로크 정보를 m_StrkColDrawing 변수에서 m_StrkColFinished로 이동시킵니다. 이 때 동시에 다수의 터치 입력이 동시에 일어나면 이를 구분하기 위해 서로가 다른 색상을 가진 스트로크를 출력하게 할겁니다.

 그럼 소스코드 상에서의 수정사항을 알아보기로 하지요. 먼저 위에 첨부되어있는 파일들을 다운받아서 프로젝트에 넣은 후, StdAfx.h 헤더파일에 새로 추가된 헤더들을 인클루드 해주세요.

// stdafx.h 파일에 추가해 주세요.
#include "Stroke.h"  
#include "StrokeCollection.h" 

그리고 ChildView.h 파일로 가서 CChildView 클래스에 private 멤버 변수로 아래의 세 변수를 추가해 줍니다.

private:
    int m_iCurrColor;                    // The current stroke color
    CStrokeCollection m_StrkColFinished; // The user finished entering strokes
                                         // after user lifted his or her finger.
    CStrokeCollection m_StrkColDrawing;  // The Strokes collection the user is
                                         // currently drawing.

이 중에서 m_iCurrColor는 드로잉 되는 스트로크의 색상을 결정하기 위한 인덱스로 쓰일 겁니다. 생성자에서 0으로 초기화 해주세요.

CChildView::CChildView() : m_iCurrColor(0)
{
}

드로잉이 다 끝나서 m_StrkColFinished 변수에 담겨 있을 스트로크 정보들을 화면에 출력해 주기 위해 CChileView::OnPaint() 함수에 아래의 코드를 추가해 줍니다.

// 드로잉이 완료된 스트로크 정보를 출력한다.
m_StrkColFinished.Draw(&dc);

 

Task 4 : 터치 데이터를 다뤄보자 (드디어!)

이제 드로잉 코드의 기본적인 설정들은 끝을 냈고, 지금부터 우리는 WM_TOUCH 메세지에 대해 아래의 세 가지 경우에 따른 코딩을 추가해 넣을 것입니다.

  1. 사용자가 스크린에 터치를 시작한 시점의 처리. – Touch Input Down
  2. 터치한 손가락을 스크린 상에서 움직이는 동안의 처리. – Touch Input Move
  3. 스크린에서 이동하던 손가락을 떼어 터치 입력이 끝나는 시점의 처리. – Touch Input Up

각각의 경우를 처리하기 위한 코드를 클래스에 추가해 봅시다. ChildView.h 헤더파일에서 위의 세가지 경우에 호출할 CChildView 클래스의 멤버함수를 선언해 줍니다.

protected:
    // Handlers for different touch input events
    BOOL OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput);
    BOOL OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput);
    BOOL OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput);

그리고 ChildView.cpp 파일에 아래의 구현을 넣어주세요.

// OnTouchInputDown - 멀티터치 입력이 시작될 때 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputDown(CPoint pt, PTOUCHINPUT pInput)
{
    // 새로운 스트로크 객체를 생성하고 Point 정보를 추가합니다.
    COLORREF strokeColor = GetTouchColor((pInput->dwFlags & TOUCHEVENTF_PRIMARY) != 0);
    
    CStroke* pStrkNew = new CStroke(pInput->dwID, strokeColor);
    pStrkNew->Add(pt);
    
    // 새로 생성한 스트로크 객체를 현재 드로잉 중인 스트로크 정보를 보관하는 
    // m_StrkColDrawing 변수에 추가해 줍니다.
    m_StrkColDrawing.Add(pStrkNew);
 
    return TRUE;
}
 
// OnTouchInputMove - 멀티터치 입력이 지속적으로 들어오고 있을 때의 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputMove(CPoint pt, PTOUCHINPUT pInput)
{
    // 한 번에 다수의 터치 입력이 들어올 수 있다.
    // TOUCHINPUT 구조체의 dwID 변수를 이용해 
    // 현재 드로잉 하고 있는 스트로크들의 데이터 집합을 찾아낸다.
    int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);
 
    if (strokeIndex >= 0) // 해당 ID의 데이터를 찾아낸 경우.
    {
        CStroke* pStrk =  m_StrkColDrawing[strokeIndex];
    
        // 터치 입력이 들어온 좌표값 Point 정보를 스트로크에 추가해 준다.
        pStrk->Add(pt);
 
        // 스트로크를 다시 그려준다. 
        pStrk->Draw(GetDC());
    }
    
    return TRUE;
}
 
// OnTouchInputUp - 멀티터치 입력이 끝날 때의 처리를 구현하는 함수.
BOOL CChildView::OnTouchInputUp(CPoint pt, PTOUCHINPUT pInput)
{
    // 한 번에 다수의 터치 입력이 들어올 수 있다.
    // TOUCHINPUT 구조체의 dwID 변수를 이용해 
    // 현재 드로잉 하고 있는 스트로크들의 데이터 집합을 찾아낸다.
    int strokeIndex = m_StrkColDrawing.FindStrokeById(pInput->dwID);
 
    if (strokeIndex >= 0) // 해당 ID의 데이터를 찾아낸 경우.
    {
        CStroke* pStrkCopy = m_StrkColDrawing[strokeIndex];
 
        // 찾아낸 스트로크 정보를 m_StrkColDrawing 변수에서 제거하고...
        m_StrkColDrawing.RemoveAt(strokeIndex);
        
        // 제거한 CStroke 클래스의 포인터를 m_StrkColFinished 변수에 추가해준다.
        m_StrkColFinished.Add(pStrkCopy);
    }
    
    return TRUE;
}
 

 각각의 함수 본문에는 터치가 발생한 시점에 걸맞는 드로잉 코드들이 구현되어 있습니다. 샘플 원본에는 영어로 달려있던 주석을 제가 한글로 바꾸고 추가 설명도 좀 덧붙여 두었습니다. 아직까지는 별다른 설명 없이 코드만 보더라도 어렵지 않게 이해하실 수 있을 거예요.

 이 함수들의 본문보다 더 중요한 부분은, 이 함수들을 '언제 호출해 주어야 하느냐' 하는 점입니다. 멀티 터치 데이터의 입력 시점에 따른 상태를 확인하기 위해 주의깊게 확인해야 할 부분은 지난 포스팅에서 선언만 해두고 빈 함수 몸통만 덩그라니 남겨두었던 CChildView::OnTouchInput(...) 함수입니다. 이곳에서 현재 멀티터치 데이터를 조회해 현재 입력 시점이 어떻게 되는지, 혹은 부가적인 다른 정보들을 어떻게 얻어내는지를 알아보는 것이 이번 포스팅의 중요한 목표라고 볼 수 있죠. 아래의 코드를 유심히 봐주세요.

BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput)
{     
    if ((pInput->dwFlags & TOUCHEVENTF_DOWN) == TOUCHEVENTF_DOWN)
    {
        return OnTouchInputDown(pt, pInput); // 터치 시작!
    }
    else if ((pInput->dwFlags & TOUCHEVENTF_MOVE) == TOUCHEVENTF_MOVE)
    {
        return OnTouchInputMove(pt, pInput); // 터치 입력 진행중.
    }
    else if ((pInput->dwFlags & TOUCHEVENTF_UP) == TOUCHEVENTF_UP)
    {
        return OnTouchInputUp(pt, pInput);    // 터치 입력 마무리.
    }
 
    return FALSE;
}

OnTouchInput 함수의 인자로 TOUCHINPUT 구조체의 포인터가 전달되는데, 이 구조체의 멤버 변수인 dwFlag에 담겨있는 flag의 상태를 조회함으로써 현재 터치 입력에 대한 여러 가지 정보들을 알아낼 수 있습니다. 위에 적힌 코드에서도 dwFlag를 이용해 터치 데이터의 추가 정보들을 알아내고 있습니다. 위의 함수 본문을 복사해 OnTouchInput 함수의 구현을 대체해 주세요.

이 중 일련의 멀티터치 입력이 처음 시작될 때의 처리를 위한 OnTouchInputDown(…) 함수를 한 번 보기로 하죠. 여기에서도 TOUCHINPUT 구조체의 dwFlag 를 조회하는 코드를 볼 수 있습니다. 바로 TOUCHEVENTF_PRIMARY 플래그가 켜져 있는지를 확인해 보는 코드네요. 입력중인 터치 정보가 여러 개인 경우, 이 중 가장 먼저 시작된 (PRIMARY) 터치 정보 인지를 확인하는 부분입니다. PRIMARY 터치는 스트로크의 색상을 검정으로 유지하고, 그렇지 않은 경우는 미리 정의된 몇 가지 색상 정보를 rotation해가며 결정해 주기 위해서 PRIMARY 데이터 여부를 확인하고 있습니다. 스트로크의 색상을 결정해주는 GetTouchColor() 함수의 선언 및 구현도 아래와 같이 추가해 주세요.

// in ChildView.h 
private:
    COLORREF GetTouchColor(bool bPrimaryContact);
COLORREF CChildView::GetTouchColor(bool bPrimaryContact)
{
    static COLORREF c_arrColor[] =  // 2번째 색상 정보를 미리 정의하는 static array.
    {
        RGB(255, 0, 0),             // Red
        RGB(0, 255, 0),             // Green
        RGB(0, 0, 255),             // Blue
        RGB(0, 255, 255),           // Cyan
        RGB(255, 0, 255),           // Magenta
        RGB(255, 255, 0)            // Yellow
    };
 
    COLORREF color;
    if (bPrimaryContact)
    {
        // primary contact 는 항상 black으로 표시한다.
        color = RGB(0,0,0);         // Black
    }
    else
    {
        // secondary 색상을 결정 한 후,
        color = c_arrColor[m_iCurrColor];
 
        // array 의 다음 색상으로 인덱스를 이동시켜 준다.
        m_iCurrColor = (m_iCurrColor + 1) % (sizeof(c_arrColor)/sizeof(c_arrColor[0]));
    }
 
    return color;
}

 이제 추가해야 할 코딩의 마지막 입니다. 드로잉을 하면서 추가해 주었던 스트로크 정보들의 해제 처리를 소멸자에 넣어주세요. 멀티터치 데이터의 조작 방식을 알아보기 위한 이번 예제는 동적 메모리 할당의 효율적인 처리 같은 관점에서는 그다지 효율적이지 못합니다. 그래서 실행해보면 다소 반응 속도가 느리다고 느껴지실 수도 있습니다만, 이번 예제가 어디까지나 MFC 프로젝트에서 WM_TOUCH 메세지의 조작 방식을 알아보기 위함임을 감안해 주세요 ㅎㅎㅎ 그래도 양심상 할당한 메모리의 해제는 해주어야겠지요 ^^;… CChildView의 소멸자에 아래 코드를 넣어주시면 이제 코드 추가는 모두 완료입니다!

CChildView::~CChildView()
{
    for (int i = 0; i < m_StrkColDrawing.GetCount(); ++i)
    {
        delete m_StrkColDrawing[i];
    }
 
    for (int i = 0; i < m_StrkColFinished.GetCount(); ++i)
    {
        delete m_StrkColFinished[i];
    }
}

 

Task 5 : 프로그램을 실행하고 직접 스크린을 ‘멀티’터치해 봅니다.

이제 빌드하고 프로그램을 실행해 봅니다. 스크린 터치를 통해서 view 영역에 동시에 서로 다른 라인이 그려지는 기능을 직접 확인해 보세요. 샘플 프로그램을 훌륭하게 서로 다른 panning 제스처를 인식해 각각의 입력에 따른 스트로크를 출력하게 됩니다.

 

  

Outro

이번 글에서는 지난번 포스팅에 이어서 WM_TOUCH 메세지를 사용해 멀티터치 UX를 구현하는 TouchPad 예제를 완성해 보았습니다. WM_TOUCH 메세지가 발생할 때마다 호출되는 CWnd::OnTouchInput() 함수에서

  1. Touch 입력의 시작, 진행 중, 종료 시점을 알아내는 방법
  2. DWORD 타입의 ID값을 이용해 서로 다른 터치 입력을 구분하는 방법
  3. 동시에 여러 개의 멀티터치가 발생할 때, 먼저 일어난(primary) 터치 데이터를 구별하는 방법

… 등을 알아보았습니다. 또한 이를 활용해 동시 다발적인 멀티터치 입력에 대해 각기 다른 panning 제스처로 인식해 궤적을 그려내는 방법을 예제 구현과 함께 알아보았습니다. 이를 통해 WM_TOUCH 메세지를 제어하는 방법을 보다 쉽게 이해할 수 있으셨을 겁니다.

Native 코드로 멀티터치 UX를 구현하는 방법 중에 아직 소개해 드리지 않은 영역이 있습니다. MFC로 제공되는 기능은 아니지만, Com-Interface 형태로 제공되는 Manipulation(MS Surface에서와 같은 위젯 처리 기법. 한번에 여러 개의 제스처를 동시 적용한다.), Inertia(관성)의 제어기법 등이 그것입니다. 다음 포스팅에서는 이러한 부분에 대해서 알아보도록 하겠습니다.

그럼 다음에도 재미있는 내용으로 다시 찾아 뵙도록 하겠습니다. 

감사합니다 ^^*

 

Reference

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Intro

    안녕하세요 ~ MFC 카테고리의 꽃집총각 입니다.

    지난번 포스팅까지는 멀티터치를 좀 더 손쉽게 구현할 수 있도록 해주는 제스처(GESTURE)를 이용한 방법을 알아보았습니다. 기본적으로는 OS 자체에서 이미 한 번 가공하고 난 데이터를 WM_GESTURE라는 메세지를 전달해 줍니다. 이런 방식을 통해 프로그래머는 가공된 데이터를 바로 사용하기만 하면 된다는 장점도 가지지만, 좀 더 커스텀한 나만의 터치입력 제어처리를 하기에는 다소 제한적이라는 단점도 동시에 얻게 됩니다. 오늘은 이런 경우를 위해, OS의 처리를 아무 것도 거치치 않은 순수 터치입력 데이터인 WM_TOUCH메세지를 이용해 멀티터치 UX를 구현하는 방법을 알아보도록 하겠습니다.

    이전과 동일하게 본문의 내용은 msdn의 channel9에 공개되어 있는 예제를 기본 내용으로 합니다. 원문의 출처는 ( http://channel9.msdn.com/learn/courses/Windows7/Multitouch/Win7MultitouchMFC/Exercise-1-Build-a-Multitouch-Application/ ) 입니다.

    어떨 때 WM_TOUCH를 이용해야 하나요?

    그럼 일단 예제를 살펴보기 전에, WM_GESTOURE로 구현할 수 없는 멀티터치 UX의 예제란 어떤 게 있을지 한 번 이야기 해 보겠습니다. 위에 있는 이미지는 Windows7에 기본으로 포함되어있는 그림판 입니다. 제 PC에서 마우스 두 개를 꽂고, 가상 멀티터치 드라이브를 통해 마우스의 입력을 터치 입력으로 인식하게 변경한 다음, 그림판의 view 영역에 동시에 두 개의 브러시를 통해 그림을 그리고 있는 모습입니다. 멀티터치… 멀티터치니까 당연히 두 개의 panning 제스처 인식이 동시에 처리되는 게 맞겠죠. 제가 마우스가 하나 더 있다면 세 개도 동시에 드로잉 할 수 있을 겁니다. (손은 두 개지만… ㅡ,.ㅡ;..)

    ‘좋아, 나는 팀 블로그에서 제스처를 이용한 멀티터치 프로그래밍 방법을 익혔으니까 이걸 내가 직접 한 번 짜봐야지!’ 하고 마음을 먹어봅니다. 하지만… 어떻게 구현해야 할 지 막상 감이 잡히질 않는군요. panning 제스처니까 CWnd::OnGesturePan(CPoint ptFrom, CPoint ptTo) 함수를 상속받아서 구현하면 될까요? 근데 입력이 동시에 두 개가 들어오면 함수가 어떤 식으로 호출될까요?

    제스처를 통한 구현방법을 사용하는 경우엔 먼저 입력된 한 개의 panning 제스처만 인식이 됩니다. 만약에 제스처를 통해 위에 올린 스크린샷과 같은 저런 터치를 입력하면, OS는 저 것을 두 개의 panning이 아닌 rotate 내지는 zoom 제스처로 번역하게 되겠지요. 그림판에 저렇게 두 개의 라인이 드로잉 되는 모습을 보고 있어서 그렇지, 실제로 저 입력 동작을 지난 번 예제인 사각박스 움직이기 샘플에 입력했다면 당연히 zoom 내지는 rotate의 효과를 기대하게 될 겁니다.

    바로 이런 경우, WM_GESTURE가 아닌 WM_TOUCH 메세지를 사용하면 되겠습니다. 동시에 입력되는 두 개의 터치 데이터를 그대로 전달받아서 각각의 panning 제스처로 인식하도록 직접 처리하면 되겠지요. 그렇게 하려면 어떻게 해야 하는지 지금부터 함께 예제를 작성하면서 알아 보도록 하겠습니다.

    Task 1 : MFC Application 프로젝트를 만들자.

    WM_GESTURE 활용 예제와 유사하게 이번에도 프로젝트 생성부터 단계적으로 알아보도록 하겠습니다. MFC 응용 프로그램 마법사에서 MFC 표준 스타일의 SDI 타입을 지정해 줍니다. wizard의 주요한 설정 화면을 이번에도 스크린샷으로 대신하겠습니다. 지난번과 달리 이번엔 한글판 스크린샷을 찍었네요 ㅎㅎ

     

     

     

     

    Task 2 : 하드웨어 상태 확인 및 Touch WIndow 설정

    하드웨어 상태 확인은 지난번에 [MFC/윈도우 7 멀티터치] #2 : 제스처(gesture)를 이용한 구현(上) 편에서 해주었던 부분과 동일합니다. 자세한 설명은 지난 번 글을 참고해 주시고요, 간단히 CTouchPadApp::InitInstance() 함수에 추가해줄 소스코드만 다시 한번 적어보기로 하지요.

    BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);   
    if ((digitizerStatus & (NID_READY | NID_MULTI_INPUT)) == 0)  
    {   
        AfxMessageBox(L"현재 터치 입력이 불가능한 상태입니다.");   
        return FALSE;   
    }   
        
    BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);   
       
    CString str;  
    str.Format(L"현재 %d개의 터치를 동시 인식할 수 있습니다.", nInputs);  
    AfxMessageBox(str);

    위에 있는 코드를 CTouchPadApp::InitInstance() 에 넣고 프로그램을 실행시켰을 때, 아래와 같은 안내 메세지가 나오는걸 확인해 주세요.

    하드웨어가 터치를 인식할 수 있는 장치인지를 확인하고 난 다음엔 예제 프로그램의 view 영역이 WM_TOUCH 메세지를 받을 수 있게끔 등록해 주는 절차가 필요합니다. 그렇지 않으면 터치 입력이 들어왔을 때 예전과 똑같이 WM_GESTURE만 날아오게 될 테니까요. 터치 메시지를 받는 윈도우로 등록해 주기 위해서는 CWnd::RegisterTouchWIndow() 함수를 호출해 주면 됩니다. 이 처리를 CChildView의 OnCreate() 함수에서 해주기로 합시다.

    Ctrl + Shift + X 키를 눌러서 MFC Class Wizard 창을 띄워줍니다. WM_CREATE 메세지를 처리하는 핸들러를 추가해주세요. 그리고 핸들러에 아래의 간단한 코드를 추가하면 끝입니다 :)

        if (!RegisterTouchWindow())
        {
            ASSERT(FALSE);
        }

    CWnd::RegisterTouchWIndow() 함수를 호출해주면 윈도우를 터치 윈도우로 등록하게 됩니다. 함수의 인자가 default value 때문에 생략되었는데, 터치 윈도우 등록을 해제하고 싶은 경우도 RegisterTouchWIndow( FALSE ) 를 호출해서 처리합니다. 터치 윈도우로 등록되고 나면 더이상 WM_GESTURE 메세지는 발생되지 않으며, 아무 가공도 거치지 않은 저레벨의 순수 터치 입력 데이터를 전달해 주는 메세지인 WM_TOUCH가 발생하게 됩니다.

    헌데 우리는 지금 Win32 프로젝트가 아니라 MFC 프로젝트를 보고 있지요 ㅎㅎ WM_TOUCH를 메시지 프로시저 함수에서 바로 얻어오자는 게 아닌 이상, WM_TOUCH 발생시 호출되는 함수를 알아봐야겠습니다. 우리는 CWnd::OnTouchInput() 함수를 이용하면 되겠군요. 하지만 이 함수는 MFC 클래스 마법사에서는 표시되지 않으니, 직접 손으로 선언과 구현부를 적어주어야 합니다. ChildView.h 파일과 ChildView.cpp에 각각 아래의 코드를 넣어주세요.

    // ChildView.h 파일에 추가.
    // Overrides
    protected:
        virtual BOOL OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput);

    // ChildView.cpp 파일에 추가. BOOL CChildView::OnTouchInput(CPoint pt, int nInputNumber, int nInputsCount, PTOUCHINPUT pInput) { // TODO: Handle Tocuh input messages here return FALSE; }

    자, 이제 low-level의 터치 데이터를 직접 받아 처리하기 위한 모든 준비가 끝이 났습니다. 이제 사용자가 프로그램의 view 영역에 터치 입력을 할 때마다 OnTouchInput(…) 함수가 호출될 겁니다. 본격적인 터치 데이터의 활용 방법은 다음 포스팅에서 알아보도록 하겠습니다 ㅎㅎ

    Outro

    이번 포스팅에서는 WM_TOUCH를 이용해 멀티터치 UX를 구현해야 하는 경우에 대한 설명, 윈도우를 터치 윈도우로 등록하는 방법, WM_TOUCH가 발생할 때마다 호출되는 CWnd의 멤버함수 등에 대해 알아보았습니다. WM_TOUCH의 프로그래밍 방법을 알아보기 위한 예제인 TouchPad라는 이름의 프로젝트를 생성부터 기본 설정까지 함께 알아보았고요. 다음 포스팅 에서는 예제를 완성시켜 나가면서, low-level 터치 데이터를 조작하는 코드를 함께 알아보도록 하겠습니다.

    무더운 여름날씨에 건강 조심하시고 다음 포스팅에서 뵙도록 하겠습니다.
    감사합니다 ^^*

    Reference

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Intro

    안녕하세요~ MFC 카테고리의 꽃집총각 입니다.

    이번 포스팅에서는 앞 글에서 이야기 중이었던 예제 프로그램의 나머지 부분을 마저 작성하고, MFC 기반의 프로젝트에서 제스처를 이용해 멀티터치 프로그래밍을 제어하는 방법에 대한 보충 설명을 정리해 보도록 하겠습니다. 앞에 적은 글과 이어지는 내용이니까, 앞부분을 보지 못하신 분들은 ( http://vsts2010.net/292 ) 이 글을 먼저 읽어주세요 ^^ 지난번 글에서는 멀티터치 동작을 확인하기 위한 Drawing 처리까지 함께 알아봤습니다. 앞부분에서 진행됐던 내용을 소제목만 간추려서 알아보면,

    * 제스처를 인식하는 첫 번째 예제 프로그램 *

    1. MFC Application 프로젝트를 만들자
    2. 하드웨어 상태를 알아봅시다
    3. View 영역에 사각형을 그리자!

    …와 같은 내용이었습니다. 두 번째 스텝에서 알아봤던 하드웨어의 멀티터치 인식 가능여부 확인방법을 제외하고는 멀티터치와 직접적으로 연관된 내용은 많이 없었군요. 이번 시간부터는 본격적으로 터치 입력을 제어하는 코드를 함께 알아보겠습니다.

     

    Task 4 : 터치 인식 코드를 넣을 차례!

    자, 이제 드디어 제스처를 제어하는 코드를 알아볼 시간입니다. 사용자 경험(UX;User Experience) 분야에서 가장 뜨거운 이슈인 멀티터치! 뭔가 색다르고 놀라운 구현방식이 있을 거 같지만 실망(?)스럽게도 구현방법은 기존의 MFC 프로그래밍과 별다른 바가 없습니다. 그저 적당한 함수를 오버라이딩 해서 필요한 코드를 넣어주면 그만이거든요. 바로 이 점이 새롭게 변화된 MFC 추가기능들이 마음에 드는 이유 중에 하나입니다. 바로 ‘친숙한 인터페이스’ 말입니다 :)

    MFC의 윈도우 랩핑 클래스인 CWnd에는 이번에 새롭게 추가된 제스처 관련 함수들이 있습니다. 이 중에서 아래의 다섯 가지 제스처 핸들링 함수를 오버라이딩 합니다. 각각의 함수들은 이름에 명시되어 있는 제스처가 입력됐을 때 호출됩니다. 아래의 코드를 ChildView.h 파일의 class CChildView 선언에 넣어줍니다.

    // Overrides
    protected:
        // Gesture handlers
        virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);
        virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);
        virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);
        virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);
        virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);

    기본적으로 터치가 인식되면 애플리케이션으로 WM_GESTURE 메세지가 날아옵니다. Win32 API만을 이용해 제스처를 제어하려면 WndProc 프로시저 함수에서 WM_GESTURE를 잡아서 처리해야 하지만, MFC 애플리케이션의 경우 자체적으로 각각의 제스처에 대한 전용 핸들링 함수들이 나뉘어져 있기 때문에, 제어가 필요한 제스처에 해당하는 함수를 용도에 맞게 따로따로 오버라이딩 해서 사용하면 됩니다.

    Note : 그런데 CWnd의 선언을 살펴보면 (afxwin.h 파일에 있습니다.) 위에 나열된 함수 말고도 OnGesture 함수가 정의되어 있는 것을 확인하실 수 있습니다. 함수의 원형은 아래와 같습니다.

    afx_msg LRESULT OnGesture(WPARAM wParam, LPARAM lParam);

    오호라~ 이것은 WM_GESTURE 메세지가 넘어올 때마다 해당 메세지를 직접 제어할 수 있는 함수인가 보군요~ 함수의 인자로 wParam, lParam이 모두 날아오니 win32 프로젝트에서 하는 것처럼 코딩하고 싶을 땐 이걸 상속받아서 작업하면 되겠구나 ~ … 라고 생각하고, 실제로 테스트 코드도 만들어 봤었지만 안되더군요 @.@… 똑똑한 여러분들은 이유가 무엇인지 바로 찾으셨을 거라고 생각합니다만… 저 함수는 가상 함수가 아닙니다 ^^;… 그저 이름만 보고 상속받아 써야지 했는데, 나중에 보니 재정의 할 수 없는 일반 멤버함수더라고요 ㅎㅎ 아마도 CWnd가 자체적으로 처리하는 코드를 구현한 부분이 아닌가 생각됩니다. 일반적으로는 각각의 제스처마다 독립적으로 호출되는 위의 다섯 가지 함수를 이용하면 되고요, 경우에 따라 부득이하게 WM_GESTURE 메세지를 직접 제어하고 싶을 때엔… 이전에 그랬던 것처럼 WndProc을 직접 제어하도록 하고 그곳에서 WM_GESTURE를 받은 경우에 대한 switch-case 문을 넣어주면 되겠죠 ^^

    이제 위에 소개된 다섯 개의 함수들에서 view 영역에 그려지고 있는 사각형을 제어하는 코드들을 넣어줍니다. 아래에 함수들의 본문 코드가 있습니다.

    BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)
    {
        int dx = ptTo.x - ptFrom.x;
        int dy = ptTo.y - ptFrom.y;
        
        if (dx != 0 || dy != 0)
        {
            m_drawingObject.Move(dx, dy);
            RedrawWindow();
        }
        return TRUE;
    }
     
    BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
        {
            m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;
        }
        else if (lDelta != 0)
        {
            m_dblZoomRatioTotal += lDelta;
            double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;
            
            m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);
            
            m_dblZoomRatioStart = m_dblZoomRatioTotal;
            RedrawWindow();
        }
        return TRUE;
    }
     
    BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
        {
            // Make the first center, the rotating one
            m_ptCenter = ptCenter;
        }
        else if (dblAngle != 0.)
        {
            m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);
            RedrawWindow();
        }
        
        return TRUE;
    }
     
    BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)
    {
        if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)
        {
            m_drawingObject.ShiftColor();
            RedrawWindow();
        }
        
        return TRUE;
    }
     
    BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)
    {
        m_drawingObject.TogleDrawDiagonals();
        RedrawWindow();
        
        return TRUE;
    }
     

    함수의 이름과 전달되는 인자들 모두 직관적입니다. 함수 본문의 예제 코드들도 그리 어렵지 않군요. 위의 코드들을 복사해 적어준 뒤 빌드하고 프로그램을 실행해 봅니다. 애플리케이션이 뜨면 사각형을 손으로 움직여 보세요. 터치 입력에 따라 사각형은 이동하고, 늘어나고, 회전할겁니다 :)

    네…? 근데 뭔가가 안 된다고요?

     

    Task 5 : 다 되는 거 같은데 회전만 안되네요 ㅡㅜ…

    여기까지 진행하고 테스트를 해보면 다른 제스처는 다 인식을 하는데, 회전(rotate)만 제대로 안 되는 현상을 겪으실 겁니다. 참고로 이번 예제에서 확인하실 수 있는 다섯 가지 제스처에 대한 동작을 간략히 설명 드리면 아래와 같습니다.

    panning 손가락을 스크린에 대고 이동한다.
    zoom 두 손가락을 스크린에 대고 벌리거나 모은다.
    rotate 두 손가락을 스크린에 대고 회전시킨다.
    press and tab 한 손가락을 스크린에 댄 상태에서 다른 손가락으로 스크린을 빠르게 터치한다.
    two finger tab 두 손가락을 동시에 스크린에 붙였다 뗀다.

    그런데 다른 건 다 잘되는데 아마 rotate 제스처만 동작하지 않을 거예요. 그리고 다른 제스처들은 다 인식할 테지만… 사각형 내부에서 일어난 제스처인지를 판단하는 처리가 없었기 때문에, 꼭 사각형 내부가 아니더라도 client 영역 내에서 발생한 유효한 제스처라면 모두 다 인식하는 걸 확인할 수 있으실 겁니다.

    그 이유는 코드가 잘못된 것이 아니라, 윈도우 자체에서 다른 제스처들은 모두 기본적으로 활성화 되어있는데, rotate만은 기본적으로 비활성화 되어있기 때문입니다. 활성화 하려면 다 해두든가 아님 말든가 할 것이지 왜 rotate만 천대(?)하는지 자세한 내막은 모르겠습니다만… 그것이 현실이군요. rotate를 활성화하기 위해, 제스처의 설정(config)를 제어하는 방법을 알아보도록 합시다.

    MFC에서는 제스처의 설정을 쉽게 컨트롤 할 수 있도록 CGestureConfig라는 클래스를 제공합니다. CChildView의 선언( in ChildView.h )에 CGestureConfig 타입의 멤버변수를 하나 추가해줍니다.

    class CChildView 
    {
        // ...
     
    // Fields
    protected:
        // Holds gesture configuration
        CGestureConfig m_gestureConfig;
        
        // ...
    }

    그리고 새롭게 부활한 반가운 인터페이스, 마법사 중의 마법사 MFC Class Wizard를 띄워서 OnCreate 함수를 오버라이딩 합니다. (단축키 Ctrl + Shift + X 입니다 ^^)

    그리고 아래의 코드를 넣어주세요.

     
    int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
     
        // TODO: Add your specialized creation code here
        GetGestureConfig(&m_gestureConfig);
        
        // Only rotation is not enabled by default
        m_gestureConfig.EnableRotate();
        
        SetGestureConfig(&m_gestureConfig);
     
        return 0;
    }
     
     
     
    그리고 나서 다시 한번 빌드하고 실행해 봅니다. 이제 rotate 제스처도 제대로 인식하는 첫 번째 예제 프로그램이 완성 되었습니다 ^^*
     
    자 드디어 첫 번째 예제의 완성입니다~ !! 짝짝짝 ~
     

    Task 6 : panning이 왜 이렇게 뻑뻑하지? 아이폰은 안 그런뎅..

     
    task5 까지가 channel9에서 소개한 MFC gesture 기능의 첫 번째 예제입니다. 필요한 부분과 불필요한 부분을 적절하게 구분해 잘 설명해 두어서, 한번 설명을 따라 작성해 보시는 것만으로 제스처 프로그래밍에 대한 대략적인 흐름을 손쉽게 파악하셨을 거라고 생각합니다.
     
    이대로 끝내기엔 좀 아쉬운 감이 있어서… 예제 코드에 대한 추가 설명을 드리고자 합니다. 사실 글 중간중간에도 제가 추가로 적은 글이 제법 있었지만… 마땅히 말씀드릴 타이밍을 못 찾았던 요것 한 가지만 더 말씀 드리도록 할게요 ㅎㅎ
     
    일단 예제를 조작해 보면 제일 먼저 거슬리는 부분이 바로 panning 제스처에 대한 조작감(?) – 게임개발만 오래 하다 보니 이런 표현이…^^; – 인데요. 아이폰에서 느껴지는 부드러운 반응성에 비해 사각형 움직임이 무척이나 둔하고 불만족스럽다는 걸 느끼실 겁니다. 사각형이 한 번 움직이고 나면 괜찮은데, panning 제스처를 인식해 움직여지기 시작할 때 까지가 뭔가 걸리는 느낌입니다. x, y 한 방향으로만 움직이고, 다른 방향으로는 안 움직이는 경우도 아마 겪으셨을 거예요. 왜 이럴까요? 기기의 한계? 아님 OS 자체의 한계?
     
    이런 현상은 하드웨어의 문제나 OS 기능 자체의 문제가 아닙니다. 이것도 역시 panning 제스처의 옵션에서 GC_PAN_WITH_GUTTER 플래그가 기본적으로 켜져 있기 때문에 나타나는 현상입니다. 저도 처음에 제스처(gesture)를 이용한 멀티터치 예제들을 실행해 보다가 panning의 무딘 반응성이 너무 눈에 거슬려서, 부드러운 움직임을 보려면 결국 WM_GESTURE에서는 한계가 있고, WM_TOUCH를 써야 하는가 보다 했었는데, 간단히 플래그만 조절해주면 좀 더 부드러운 움직임을 느낄 수가 있게 됩니다.

    GC_PAN_WITH_GUTTER에서 gutter는 홈통이나 배수로 등의 뜻으로, panning 제스처가 발생했을 때 가장 메인이 되는 방향으로의 움직임 이외의 방향성에 대한 반응은 특정 임계값을 넘기지 않는 이상 무시하게 하는 효과를 줍니다. 이 때문에 손가락에서의 미세한 움직임들이 무시되고, 반응성이 안좋다는 느낌을 받게 되죠. 이를 해결하기 위해서는 rotate 제스처의 활성화를 위해 오버라이딩 했었던 OnCreate 함수에서 panning에 대한 아래의 설정도 함께 처리해 주면 됩니다.

     
    config.EnablePan( TRUE, 
        GC_PAN_WITH_SINGLE_FINGER_VERTICALLY | 
        GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY |
        GC_PAN_WITH_INERTIA );
     
    pan 제스처를 활성화 하되, GC_PAN_WITH_GUTTER 플래그만을 제외한 나머지 플래그들을 활성화 해주는 코드입니다. 위 코드를 넣고 나서 빌드하고 다시 실행해보면 이전보다 훨씬 더 부드럽게 사각형이 이동되는 것을 느낄 수 있으실 겁니다.

     
     

    Outro

    이것으로 MFC를 이용해서 제스처를 이용한 멀티터치 프로그래밍 방법의 첫 번째 예제를 소개와 설명을 모두 마쳤습니다. 제스처의 설정을 컨트롤 하는 방법, 각각의 제스처에 대한 핸들링 코드를 넣어주는 방법 등을 알아보았는데, 대부분 예전 MFC 프로그래밍의 마우스/키보드 이벤트 처리 등과 비슷한 방식이었기 때문에 그리 어렵지는 않은 난이도였습니다.

    다음 포스팅 에서는 이제 가장 자유롭고 확장성 있는 (… 하지만 대신 좀 더 까다로운 ) WM_TOUCH를 이용한 멀티터치 프로그래밍 방법을 알아 보도록 하겠습니다. 혹시 그 전에 질문 사항들이나, 제스처 프로그래밍 방법들에 대한 추가 학습사항이 있다면 다시 한 번 정리하는 기회를 갖도록 하겠습니다.

    그럼 다음 포스팅에서 다시 인사 드리도록 하겠습니다. 그 때까지 더운 날씨에 모두들 건강하시고 공부 열심히 하세요 ~ ^^*

    감사합니다 ~ 꾸벅 ~

    Reference

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Intro

    안녕하세요. MFC 카테고리의 꽃집총각입니다.

    한동안 여러 가지 다른 행사와 게으름으로(;;) 포스팅이 많이 늦어졌습니다 ^^; 지난 글에서는 ( [윈도우 7 멀티터치] #1 : 멀티터치 UX를 적용하는 3단계 전략 ) 멀티터치 프로그래밍을 내 애플리케이션에 적용하는 방법을 3단계로 나누어 설명하였습니다. 이 중 첫 번째 단계인 ‘추가 코딩 없이도 기본적으로 얻을 수 있는 기능들’에 대해 자세히 알아보았고, 나머지 두 단계는 기본적인 내용만 소개해 드렸습니다. 이번엔 이중에서 두 번째 단계인 제스처(gesture) 이용한 프로그래밍 방법에 대해 자세히 알아보도록 하겠습니다.

    note : 지난번 까지는 프로그래밍 이야기 보다는 개념적인 설명이 위주였기 때문에 ‘Windows 7 Development’ 카테고리에 글을 올렸습니다만… 이제부터는 본격적으로 구현에 대한 이야기가 시작되니까 ‘MFC’ 카테고리에서 연재하도록 하겠습니다. 앞부분은 어느 언어를 사용하든 멀티터치 프로그래밍을 위해 알아두어야 할 공통적인 내용들이었고요, 제가 앞으로 말씀드릴 부분은 주로 MFC를 위한 구현방법이니까, 이게 맞는 거겠지요. :)

     

    제스처를 인식하는 첫 번째 예제 프로그램

    이제까지는 제가 주로 개념설명이나 이론적인 부분들을 모두 말씀 드리고 나서 예제 코드를 알아봤었는데, 이런 쌍팔년도식 학습법은 요즘은 별로 어울리지 않는 듯 합니다 ^^;… 일단은 돌아가는 예제 프로그램을 간단하게 한 번 작성해 보기로 하지요. 그게 자질구레한 설명도 줄여주고, 공부하기도 좀 더 쉬울 것 같네요 ㅎㅎ

    첫 번째로 구현해볼 예제는 view 영역에 그려진 box를 터치로 조작, 변환하는 프로그램 입니다.

    view 영역에 box를 하나 표시하고, 손가락으로 사각형을 이동, 회전, 확대/축소 할 수도 있고, Press  & Tap 제스처를 통해 색상을 변경할 수도 있고, Twi Finger Tap 제스처로 X표시를 하거나 지울 수도 있는 프로그램입니다. 예제를 따라서 작성해 보면 MFC 애플리케이션에서 어떻게 제스처를 다룰 수 있는지 쉽게 이해하실 수 있습니다. 해당 예제는 Windows 7 SDK에 들어있는 MFC 샘플 중에 하나입니다. 영어에 부담이 없으신 분들은 직접 Windows 7 SDK를 확인해 보셔도 좋을 것 같군요 ^^ ( http://channel9.msdn.com/learn/courses/Windows7/Multitouch/Win7MultiTouchGestureMFC/Exercise-1-Build-a-Multitouch-Gesture-Application/ )

     

    Task 1 : MFC Application 프로젝트를 만들자

    우선 Visual Studio 2010에서 MFC Application 프로젝트를 하나 만드세요. Application Wizard에서는 고전 MFC 스타일의 SDI 타입을 지정해 줍니다. Wizard의 설정 화면을 활자 설명 대신 스크린샷으로 대신하겠습니다.

     

    Task 2 : 하드웨어 상태를 알아봅시다

    본격적인 멀티터치 UX를 구현하기에 앞서, 현재 구동중인 실행환경이 멀티터치가 가능한 상태인지를 확인해야 합니다. 멀티터치를 인식 가능한 하드웨어가 연결되어 있는지, 그리고 사용 가능한 상태인지를 알 수 있어야겠지요. 이런 정보를 얻고자 할 때는 예전부터 익히 사용해오던  ::GetSystemMetrics(…) 함수를 이용하면 됩니다.

    int WINAPI GetSystemMetrics( __in  int nIndex );
    http://msdn.microsoft.com/en-us/library/ms724385(VS.85).aspx

    GetSystemMetrics는 운영체제의 여러 가지 설정 정보들을 얻어올 수 있는 API로 예전부터 자주 사용되던 함수입니다. 윈도우 캡션 영역, 메뉴 영역 등의 너비를 얻을 수 있기 때문에 UI 작업할 때 많이 사용해 보셨을 겁니다. MFC/Win32 API에 새롭게 추가되는 기능들은 상당 부분 이렇게 이전 인터페이스를 그대로 사용하고 있기 때문에 공부하기가 특히 어렵지 않은 느낌이어서 마음에 듭니다 ㅎㅎ 이 함수로 터치인식 정보를 얻고 싶을 땐 아래의 새로운 flag들을 사용합니다.

    • SM_DIGITIGER : 입력장치 상태 확인
    • SM_TABLETPC : 타블렛 pc 여부 확인
    • SM_MAXIMUMTOUCHES : 최대 동시입력 가능한 터치 개수

    이번에 공부하면서 보니, 윈도우 XP가 타블렛 PC edition이란게 따로 있었더군요… SM_TABLETPC는 이런 정보를 확인할 때 쓰는 값이고, 나머지 두 flag는 Vista 이하 OS에서는 인식하지 않습니다. 각 항목의 자세한 값은 MSDN을 참고하시면 되고요, 기본적인 경우라면 아래의 코드를 가져다 사용하시면 되겠습니다. 현재 멀티터치 입력 가능 여부와 동시 입력 개수를 알아보는 코드입니다.

       1: BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
       2: if ((digitizerStatus & (NID_READY | NID_MULTI_INPUT)) == 0) //Stack Ready + MultiTouch
       3: {
       4:     AfxMessageBox(L"현재 터치 입력이 불가능한 상태입니다.");
       5:     return FALSE;
       6: }
       7:  
       8: BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);
       9:  
      10: CString str;
      11: str.Format(L"현재 %d개의 터치를 동시 인식할 수 있습니다.", nInputs);
      12: AfxMessageBox(str);

    그리고… 멀티터치 프로그래밍 공부가 하고 싶은데 하드웨어가 마땅치 않을 때… 제가 예전에 말씀 드렸던 시뮬레이션 환경 설정 ( http://multitouchvista.codeplex.com/ ) 기억하고 계시죠? 기억이 안 나시는 분은 ( [멀티터치]멀티터치 프로그래밍 환경 구축하기 ) 링크를 참고해 주세요 ^^

    위에 있는 코드를 그대로 복사해서 CTouchGestureDemoApp::InitInstance() 함수에 넣어주세요. 그리고 애플리케이션을 실행하면 아래와 같은 창이 뜹니다.

    아따, 많기도 하여라 @.@… 동시에 255개의 터치를 인식할 수 있군요. 저도 터치 입력 환경을 시뮬레이션 해서 공부하고 있는데, 이런 경우 동시 입력 개수가 255개로 나오게 됩니다 ㅎㅎ

     

    Task 3 : View 영역에 사각형을 그리자!

    샘플을 따라 해보기 위해서 화면에 box를 그려주는 코드를 우리가 일일이 직접 작성할 필요는 없겠죠. 지금은 제스처의 인식 방법을 알아보고 싶은 거니까요. Windows 7 SDK에서 box 드로잉을 해주는 코드를 제공하고 있으니, 이 코드를 사용하도록 하겠습니다. 그리고 우리는 멀티터치 제스처를 이용하는 방법에만 집중하기로 합시다. box를 그려주는 CDrawingObject라는 클래스의 소스는 아래에서 다운 받으실 수 있습니다.

    위의 파일을 다운 받으셔서 프로젝트에 포함하고, stdafx.h 파일의 아래쪽에 헤더파일 인클루드 구문을 넣어줍니다. 기본적으로 작성되어 있는 상태에서 #include <afxcontrolbars.h> 아래에 넣어주면 됩니다.

       1: #include "DrawingObject.h"

    그리고 ChildView.h를 열고 View 클래스의 멤버 변수로 CDrawingObject 객체를 하나 선언해 줍니다.

       1: // The drawing object
       2: CDrawingObject m_drawingObject;

    마찬가지로 View 클래스의 멤버 변수로, box의 확대/축소와 이동을 처리하기 위해 필요한 변수들을 몇 가지 더 추가해 줍니다.

       1: // Needed for drawing object position and size calculations
       2: double m_dblZoomRatioStart;
       3: double m_dblZoomRatioTotal;
       4: CPoint m_ptCenter;

    그리고 box를 그려주기 위해서, CChildView.cpp에 있는 CChildView::OnPaint() 함수의 제일 아래쪽에 box drawing 코드를 넣어줍니다.

       1: m_drawingObject.Paint(&dc);

    그다음, 윈도우 크기를 변경할 때 box의 크기와 위치를 기본값으로 초기화 해주는 코드를 넣어줍니다. box가 화면영역을 벗어나거나 한 경우 초기화 코드가 있으면 좋겠지요. 그러기 위해서 먼저 WM_SIZE 메세지 핸들러를 추가합니다.

    Ctrl + Shift + X 키를 눌러 MFC 클래스 위자드(MFC Class Wizard)를 띄웁니다. MFC 클래스 위자드는 VC++ 6.0에 있던 툴인데, Visual Studio .NET 2002 통합 환경으로 변경되면서 사라졌지만 이번에 Vusial Studio 2010에서 새롭게 부활했습니다. 거의 10년 만이라고 할 수 있군요. 예전에 사용해 보신 분들이라면 아주 익숙한 인터페이스 일겁니다. 여기에서 CChildView의 WM_SIZE 메세지를 선택해서 핸들러를 추가해 줍니다. 아래 스크린샷을 참고하세요.

    MFC 클래스 마법사의 부활은 VS2010의 핵심적인 변화 요소중에 하나입니다. 클래스 마법사의 활용 부분은 다음 기회에 별도의 포스팅으로 자세하게 설명하도록 하겠습니다.

    생성된 CChildView::OnSize 함수에 아래의 코드를 넣어줍니다.

       1: m_drawingObject.ResetObject(cx, cy);

    여기까지 진행했다면 이제 drawing 처리의 완성입니다. 프로젝트를 빌드하고 애플리케이션을 실행하면 아래와 같이 빨간 box가 drawing 되는 모습을 확인하실 수 있습니다.

     

    Outro

    한 번의 포스팅에서 예제의 완성과 추가설명까지 모두 다루려고 했지만 그러기엔 글이 너무 길어질 것 같네요. 이쯤에서 한 번 호흡을 끊고, 다음 포스팅에서 실질적인 제스처 인식 코딩 방법과 추가설명을 정리하도록 하겠습니다. (下편은 바로 올릴 예정이니 조금만 기다려 주세요)

    이번 글에서는 제스처를 인식하는 예제 프로그램의 작성 방법을 단계적으로 소개하고, 하드웨어의 멀티터치 인식 여부 확인 방법을 알아보았고, 아주 짧게나마 VS2010에서 새롭게 부활한 MFC 클래스 마법사에 대한 언급이 있었고, 첫 번째 멀티터치 예제의 작성 방법을 절차적으로 알아보았습니다. 제스처를 이용한 멀티터치 구현 하(下)편에서는 제스처를 인식해 box를 이동, 변환 시키는 코드의 작성과 샘플을 통해 알아볼 제스처 인식 프로그래밍의 부가 정보 및 팁들을 소개해 드리도록 하겠습니다.

    그럼 곧바로 하(下)편으로 찾아 뵙겠습니다.
    감사합니다 ^^*

    Reference

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Intro

    안녕하세요. MFC 카테고리의 꽃집총각 입니다.

    지난 포스팅에서는 2회에 걸쳐 멀티터치 인터페이스의 비전과(사람이 기계와 만나는 진정한 방법 – 멀티터치), 멀티터치 프로그래밍을 하기 위한 환경 구축([멀티터치]멀티터치 프로그래밍 환경 구축하기)에 대해서 각각 알아보았습니다. 오늘부터는 실제로 터치 UX 프로그래밍을 하기 위한 내용들을 살펴보고자 합니다. 이제부터 점점 내용이 재미있어 지겠군요! 자 이제 시작하겠습니다!!

    MSDN 매거진이나 channel9에 올라오는 article들을 보면, 응용 프로그램에 멀티터치 UX를 적용하는 방법을 3단계로 나누어 설명하고 있습니다.

    1. ‘Good’ step : 코딩하지 않아도 공짜로 얻을 수 있는 기능들.
    2. ‘Better’ step : 제스처(Gesture)를 이용해 멀티터치 프로그래밍 하기.
    3. ‘Best’ step : 원시 데이터(raw-data)를 직접 제어해, 가장 디테일한 터치 UX 구현하기.

    위와 같이 터치 적용 수준에 따라 세 단계로 나누고, 각각의 단계에 Good / Better / Best 라는 이름을 붙이고 있습니다. 멀티터치 UX를 적용하는 좋은 방법, 더 좋은 방법, 가장 좋은 방법 정도가 되겠네요 ㅎ 단계성을 표현하려는 의도는 이해합니다만 조금 우습군요 (피식). 각 단계가 어떤 내용을 담고 있는지 하나씩 좀 더 자세하게 이야기해보도록 하겠습니다.

     

    1. ‘Good’ step : 코딩하지 않아도 공짜로 얻는 터치 UX

    우선 기본적으로 첫 번째 단계는, 소스코드를 손대지 않고도 얻을 수 있는 기본적인 터치 UX에 대한 내용입니다. 기본으로 제공되는 컨트롤들이 윈도우 7의 터치 인터페이스 환경에 맞게 버전업 되면서, 자체적으로 멀티터치 입력에 반응하는 기능들을 제공하게 되었습니다. 이런 기능들은 기존에 작성된 다수의 레거시(legacy) 프로그램에도 그대로 적용됩니다. 다시 말해 예전에 제작했던 응용 프로그램이 별다른 호환성의 문제 없이 WIndows7에서 실행되기만 한다면 바로 얻을 수 있는 기능들입니다.

    스크롤바의 스크롤 기능, 에디트 컨트롤의 화상키보드 호출기능, 윈도우 탐색기나 인터넷 익스플로러와 같은 프로그램의 네비게이션 기능 (쉽게 말해, ‘앞으로’, ‘뒤로’ 버튼 기능. WM_APPCOMMAND 메세지) 등이 그것입니다. 이런 것들은 직접 한 번 조작해 보시는 게 훨씬 나을 겁니다. 이전 글에서 소개해 드린 Multi-Touch Vista의 터치 인식 디바이스 드라이버를 구동하면 터치 입력을 시뮬레이션 해 볼 수 있으니까요 :) 윈도우 탐색기의 파일 리스트 영역에서 손가락을 좌/우로 빠르게 드래그 해주면 – 아이폰에서 사진을 넘기는 동작입니다. 뒷부분에 다시 정리하겠지만 Flicks 라는 이름의 제스쳐 입니다 – 이전 / 다음 버튼을 누른 것처럼 네비게이션 히스토리를 따라 경로가 변경됩니다.

    윈도우 탐색기에서 왼쪽 –> 오른쪽으로 문지르는 Filcks 제스처를 실행한 모습.

    윈도우 탐색기에서 오른쪽 -> 왼쪽으로 문지르는 Flicks 제스처를 실행한 모습.

    스크린샷을 보시면 일반 아이콘 크기 정도의 ‘앞으로’, ‘뒤로’ 이미지가 손가락을 갖다 댄 곳에 잠시 나타나는 것을 보실 수 있습니다. 이렇게 되면서 윈도우 탐색기의 탐색 경로가 앞/뒤로 변경됩니다. 이 시점을 Spy++로 조회해 보니 WM_APPCOMMAND 메세지가 넘어오는군요. 이전에 작성된 응용 프로그램이 WM_APPCOMMAND 를 받아서 네비게이션 기능을 처리하고 있었다면, 윈도우 7에서 실행하는 것 만으로도 Flicks 터치 제스처를 얻게 되는 겁니다.

    손가락 터치로 스크롤바의 스크롤이 가능하다는 사실은 제가 이미지 스크린샷을 찍어서 보여드릴 방법이 딱히 없군요 ^^; 이 부분은 직접 실행해서 확인해 주시길 부탁 드리면서 나머지 한가지만 더 같이 보기로 하죠. 메모장을 열어서 View 영역을 손가락으로 탭 해주면 위 스크린샷에서 보이는 것 같이 키보드 그림이 그려진 작은 윈도우가 잠깐 동안 노출됩니다. 이 녀석을 다시 탭 하면 터치로 텍스트를 입력할 수 있는 화상키보드가 화면에 나타납니다.

     

    오… 요즘 인기를 얻고 있는 블록키보드 타입이군요. 먼지의 유입이 적고 간격이 넓어 오타가 줄어든다는 바로 그 방식 +_+ 아… 근데 어차피 화상 키보드라 아무 상관 없겠습니다…;;

    이 화상키보드 호출 기능은 텍스트를 입력하는 방식의 컨트롤들이면 어느 곳에나 기본 구현되어 있습니다. 기본 에디트 컨트롤, 리치 에디트 컨트롤, IP Address 입력 컨트롤 등이 이에 해당됩니다. 텍스트를 입력할 수 있도록 캐럿이 깜박이는 방식의 인터페이스를 가진 컨트롤들이면 이미 화상키보드 호출 기능도 공짜로 얻게 된 겁니다. 윈도우 7에서 실행하기만 한다면 말이지요 :)

    이런 기능들은 그저 공짜로 얻게 된 추가기능 치고는 꽤나 근사합니다. 하지만 모든 응용 프로그램에 공통 적용되는 사항이기 때문에 내가 만든 응용프로그램만의 고유한 기능이라기보다는 윈도우 7 OS의 새로운 기능이라고 보는 편이 더 적합합니다. 프로그램의 실제 사용자에게도 해당 기능들은 아주 당연한 인터페이스로 인식될 것이고, 금새 적응하게 될 것입니다.

    그렇다면 좀 더 터치 인터페이스에 친화적인 응용 프로그램을 만들기 위해서는 어떻게 해야 할까요?

    터치를 인식하도록 프로그래밍 하는 방법은 두 가지가 있습니다. 하나는 적은 수정비용으로 기본적인 동작들을 재빠르고 심플하게 구현하는 방법이고, 또 다른 한가지는 보다 강력하고 디테일한 움직임을 다루는 방법입니다. 이 두 가지 방법들을 각각 2. ‘Better’ step과 3. ‘Best’ step에서 이야기해 보도록 하겠습니다.

     

    2. ‘Better’ step : 제스처(Gesture) 이용하기.

    터치 UX를 응용 프로그램에 적용하는 두 번째 방법은 제스처(Gesture)를 이용하는 방법입니다. 여기에서 ‘제스처’란 터치 인터페이스를 통해 손으로 입력할 수 있는 한가지의 입력 패턴을 말합니다. 예를 들어 멀티터치라고 하면 가장 대표적으로 떠오르는 입력방식 중에 하나인 사진 확대/축소 방법은 두 손가락을 스크린에 갖다 대고 간격을 벌리거나 좁히는 식으로 주로 쓰입니다. MSDN에서는 이런 입력 방식을 Zoom이라는 이름의 제스처로 부르고 있습니다. 멀티터치는 앞으로도 다양한 제스처가 적절한 기능들과 보다 직관적으로 연결되어 확장해 나갈 수 있는 무한한 가능성을 가지고 있지요.

    MSDN에 보면 (http://msdn.microsoft.com/en-us/library/dd940543(VS.85).aspx) 여러 가지 제스처들 중에서 가장 흔하고 쉽게 사용되는 대표적인 9가지 제스처에 대해 이름을 정의하고 있습니다.

     

    이미지가 작아서 잘 안보이실 텐데요. MSDN 링크를 열고 보다 큰 이미지로 내용을 확인하시는 게 좋겠습니다. 비록 도표는 영어로 되어있지만 그다지 내용도 없을 뿐더러 Action 그림과 제스처 이름만 보면 대충 어떤 방식인지 직관적으로 이해하실 수 있습니다. 앞으로 제스처를 이용한 터치 프로그래밍을 구현하고자 하면 각 제스처의 이름을 자주 확인하게 될 테니 위의 도표는 꽤나 유용하게 쓰일겁니다. 한 번 정도는 눈여겨봐두시기 바랍니다.

    제스처를 이용한 터치 구현 방법은 9가지 제스처가 입력된 경우 이에 따른 기능들이 실행되게 하는 것입니다. Windows 7은 기본적으로 터치 입력이 감지되면 위의 9가지 제스처에 해당하는지 여부를 자체적으로 판단합니다. 이에 해당하는 적절한 Gesture가 있을 경우 WM_GESTURE라는 메세지를 만들어 응용 프로그램에 전달해줍니다. 해당 메세지에는 각각의 제스처에 대한 좌표정보 등의 추가정보들이 적절하게 가공되어 제공됩니다. 이미 OS 자체에서 터치 인터페이스를 인식해서 한번 가공해둔 상태이기 때문에 프로그래머는 가벼운 마음으로 OS가 해석해준 좌표 정보나 수치 등을 활용해 기능을 연동하기만 하면 그만입니다. 그렇기 때문에 적은 수정 비용으로, 기본적인 제스처에 반응하는 기능들을 빠르게 구현할 수가 있는 점이 장점입니다.

    만약 Win32 API만을 이용하여 프로그램을 만든다면 WndProc 에서 WM_GESTURE 메세지를 잡아서 이에 맞는 코드를 넣어주면 되고, MFC는 한층 더 간단하게 적절한 가상함수를 오버라이딩해서 코드를 넣어주면 되는 식이지요. 윈도우 메세지와 MFC 클래스의 가상함수를 재정의하는 구현 방식은 이미 우리가 오래도록 작업해 왔던 기존의 윈도우 프로그래밍 방법과 크게 다르지 않기 때문에 익숙하고 거부감이 없습니다. 보다 자세한 구현 방법은 다음 포스팅에서 계속 이어 나가도록 하겠습니다.

    제스처를 이용한 구현 방법은 ‘적은 수정 비용으로 기본적인 동작의 구현을 빠르게 만드는 방향’에 초점을 둔 방식입니다. 저수준 단위의 터치 입력 데이터를 프로그래머가 일일이 분석해서 제스처의 패턴을 파악하지 않아도 됩니다.

    하지만 이런 일반적인 제스처 말고 색다른 입력 방식을 구현해야 할 경우도 있을 겁니다. 기존에 없었던 독특한 방식의 제스처를 새롭게 만들고 싶을 수도 있습니다. 혹은 마치 실제로 손가락을 이용해 도화지에 그림을 그리듯 매우 디테일한 레벨의 컨트롤을 구현하고 싶을 수도 있습니다. TED 강연에서 제프한 박사가 보여준 독특한 형태의 멀티터치 데모라던가, Microsoft Surface에서 처리하듯이 화면의 Widget을 직접 손으로 만지는 것 같은 직관적인 움직임을 구현하고 싶을 수도 있습니다. 이런 경우에는 제스처를 이용한 구현방법을 적용하기에는 무리가 있습니다.

    그렇다면 그러한 고급 수준의 터치 인터페이스를 구현하려면 어떻게 해야 할까요?

    그것을 다음 단계인 3 ‘Best’ step이 설명하고 있습니다 :)

     

    3. ‘Best’ step : 가장 디테일한 터치 UX를 구현하는 방법.

    마지막 단계인 3. ‘Best’ step에서는 가장 세밀하고 유연한 터치 인터페이스를 만들 수 있는 방법을 제시하고 있습니다. 2단계에서처럼 윈도우가 자체적으로 터치 메세지를 가공하는 방식이 아니라, 아무 처리도 거치지 않은 원시 터치 데이터(Row Touch Data)를 응용 프로그램으로 직접 넘겨주게 설정하고(WM_TOUCH 메세지), 개발자 스스로가 이를 이용해 저레벨 수준의 구현 부터 직접 담당하는 방식입니다. 이런 식이라면 아무래도 제스처를 이용한 방법 보다는 신경써야 할 부분이 많아지고 구현이 좀 더 까다로워지는 점이 있습니다. 제스처를 이용한 구현방식이 적은 수정비용으로 빠른 구현을 꾀한 방향이었다면, 원시 데이터를 직접 활용하는 방법은 조금 까다롭더라도 고수준의 인터페이스를 구현하는 것에 초점을 두고 있습니다. Microsoft Surface 처럼 말이지요.

    Microsoft Surface ® (이미지 출처 : http://cyberimpacts.com/)

    이러한 low-level의 터치 데이터는 WM_TOUCH라는 새로운 윈도우 메세지를 통해서 넘겨받을 수 있습니다. 역시 기존의 다른 윈도우 메세지를 처리해오던 것처럼 WndProc에서 메세지를 받아서 코드를 넣어주면 됩니다. WM_TOUCH는 WM_MOUSE… 계열의 마우스 메세지와 유사합니다. 이를 활용하는 방법 역시 차례대로 다음 포스팅에서 보다 자세히 알아 보도록 하겠습니다.

     

    Outro

    이번 포스팅에서는 드디어 멀티터치 프로그래밍을 하기 위한 단계적 방법들과 몇 가지 개념들, 용어들에 대해서 정리해 보았습니다. 아직까지도 소스코드나 api들의 직접적인 설명은 없었지만 오늘 알아본 내용들을 통해 터치 UX를 구현하기 위한 전반적인 방향들은 어느 정도 체계가 잡히셨을 거라고 생각합니다.

    다음 포스팅에서는 WM_GESTURE를 이용한 방법부터 시작해서 WM_TOUCH를 이용하는 방법까지 순서대로 멀티터치 관련 API들과 활용 방법들을 정리해 보도록 하겠습니다. Win32 API를 이용하는 방법과 MFC를 이용하는 방법들을 소개할 예정이니 참고해 주세요.

    그럼 빠른 시간 안에 다음 내용을 가지고 다시 인사 드리도록 하겠습니다. 화창한 봄날인데 너무 모니터 앞에만 앉아있지 마시고 봄기운 가득 느끼시면서 재충전 하는 시간들 가져보시기 바랍니다 ^^

    감사합니다 ^^*

     

    reference

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    Intro

    안녕하세요. MFC 카테고리의 꽃집총각입니다.

    앞으로 한동안은 멀티터치에 관한 포스팅을 올리려고 합니다. 지난주에 올렸던 사람이 기계와 만나는 진정한 방법 – 멀티터치 편에서는 멀티터치에 대한 개념적인 부분과 비전에 대해 설명 드린바 있습니다. 오늘은 본격적인 프로그래밍 방법에 대해 알아보기 전에 마지막으로 한가지 더 이야기해야 할 내용이 남았습니다. 바로 멀티터치 개발환경 구축에 관한 이야기 입니다 :)

    일단은 당연한 말이지만 윈도우 7 운영체제가 필요합니다. 그리고 개발환경인 Visual Studio와, 멀티터치 입력이 가능한 하드웨어가 필요합니다. 오늘은 이 세 개의 준비물에 대해 각각 알아보도록 하겠습니다.

     

    첫 번째 준비물 : Microsoft WIndows 7 ®

    가장 당연한 첫 번째 준비물은 윈도우 7 운영체제 입니다. 윈도우 7은 기본적으로 멀티터치 입력에 대한 준비를 모두 마치고 태어난 녀석입니다. 터치 입력을 통해서 가벼운 엔터테인 수준의 컴퓨팅은 키보드와 마우스 없이도 모두 해결할 수 있습니다. 하지만 우리 블로그는 윈도우 7을 소개하는 자리가 아니니 여기서 길게 설명하지는 않겠습니다 ^^; 검색을 통해 손쉽게 윈도우 7의 터치 인식 환경에 대한 정보를 얻으실 수 있습니다.

    위에 보이는 스크린샷은 윈도우7의 제어판에 기본적으로 들어있는 ‘펜 및 터치’ 항목입니다. 터치 입력에 대한 설정들을 제어하는 곳이지요. 근데, 윈도우 7을 여태 몇 달이나 사용 중이지만 저런 항목은 제어판에서 보신적이 없으신가요? 그렇다면 아마 현재 사용하시는 PC 혹은 노트북이 터치 인식을 지원하지 않기 때문 일겁니다 ㅜㅠ… 이 부분은 좀 있다 하드웨어를 이야기 할 때 다시 말씀 드리도록 하겠습니다.

    다만 윈도우7의 터치 인식 지원에 대해 혼동하기 쉬운 점은 ‘멀티터치’의 인식이라는 점입니다. 비스타나 윈도우 XP도 타블렛 PC에 설치하면 터치로 조작할 수 있습니다. 하지만 그저 마우스 커서를 손으로 조작할 뿐이지요(싱글터치). 나중에 이야기 되겠지만 윈도우7의 멀티터치는 WM_MOUSE… 계열의 마우스 메세지를 처리하는 게 아니라 WM_GESTURE / WM_TOUCH라는 새로운 윈도우 메세지를 통해 처리됩니다. 이들 메세지는 윈도우7에서 새롭게 제공되는 메세지이며, 이전 OS에서는 당연한 말이지만 발생하지 않습니다. 저런… 안타까워라!

     

    두 번째 준비물 : VIsual Studio 2010

    두 번째 준비물은 통합 개발환경인 Visual Studio 입니다. 며칠 뒤 정식 출시될 vs2010이 있다면 추가적인 다른 준비 없이 바로 터치 프로그래밍을 할 수 있습니다. 하지만 꼭 vs2010에서만 가능한 것은 아닙니다. vs2005 / 2008 등 이전 버전에서도 터치 프로그래밍을 해 볼 수 있습니다. 단, 이 때는 별도로 Windows 7 SDK를 다운받아서 설치해 주어야 합니다.
    그러니 사실 멀티터치 프로그래밍을 하는 데에는 어느 버전의 비주얼 스튜디오라도 굳이 상관은 없는 셈이지요. 하지만 vs2010 블로그인 만큼 vs2010을 기준으로 강의를 진행하겠습니다. 여러분들도 vs2010을 사용하시겠다고 한다면 Windows 7 SDK를 반드시 깔아야 할 필요는 없습니다. 하지만 이 SDK 에는 제공되는 API들을 이용한 프로그래밍 샘플 코드들도 함께 포함되어 있는데, 이중에는 몇 개의 멀티터치 예제도 들어있습니다. 샘플을 먼저 구동해 확인해 보고 싶으신 분들은 SDK를 설치해 보시는 것도 좋을 거라는 생각이 듭니다.

     

    세 번째 준비물 : 멀티터치 인식 하드웨어

    자, 아마도 제 생각엔 가장 많은 이들의 원성을 살 세 번째 준비물, 하드웨어에 대해 이야기해 볼 시간입니다. 터치 디바이스는 점차 꾸준히 보급되고 결국은 일반화 될 테지만(아… 혹시 뭐 안될 수도 있겠지만요! ㅎ), 아직은 초기단계이기 때문에 이 글을 보시는 대다수의 프로그래머 분들은 터치 디바이스를 보유하고 있지 않으실 거라는 생각이 듭니다. 사실은 당장 저만 같아도 터치를 인식하는 시스템을 아직 장만하지 못했답니다 ㅜㅠ …

    하지만 우선 아쉬운 대로 이를 보완할 차선책이 있습니다. 마우스를 가지고 터치 입력을 시뮬레이션 해주는 방법이 있거든요. 재미있는 점은 마우스를 여러 개 연결하면 연결하는 만큼 독립적인 터치로 인식하게 해주기 때문에, 멀티터치 입력도 시뮬레이션 해볼 수가 있답니다. 책상 서랍 안에 잠자고 있는 마우스가 있다면 지금 얼른 꺼내세요! 그리고 이제 양손으로 마우스를 붙잡고 신기한 체험을 해 볼 시간입니다! “내 PC는 안될거야…”라는 패배의식을 한 방에 날려버릴 방법을 지금 소개해 드립니다 ^^

    codeplex에 가보면 Multi-Touch Vista라는 프로젝트가 있습니다. ( http://multitouchvista.codeplex.com/ ) 윈도우 7이 아닌 비스타에서도 멀티터치 인식이 되게 만드는 프로젝트 입니다. 유트브에 보면 이 프로젝트의 데모를 보실 수 있는데, 동작이 꽤나 신기합니다. 그냥 기존의 일반적인 윈도우가  자연스럽게 회전이 되네요.

    아… 잠시 이야기가 샐 뻔 했네요. 아무튼 이 프로젝트에서는 마우스 입력을 터치로 대신 인식하게 하는 디바이스도 함께 제공하고 있습니다. 오픈소스이기 때문에 사용에 별다른 제한도 없습니다. 터치 UX는 직관성 혹은 감성적인 측면에서도 의미하는 바가 크기 때문에 직접 느껴보는 것이 중요하겠지만, 개발을 하면서 아쉬운 대로 시뮬레이션 해보기엔 꽤나 적절한 방법입니다.

     
    멀티터치를 시뮬레이션 해주는 ‘Multi-Touch Vista’ 프로젝트의 device 설치 중 팝업.

    디바이스를 설치하고, 활성화하는 방법은 아래 동영상에 잘 나와있습니다. 프로젝트 주인장님이 참 깔끔히도 정리를 잘 해놓으셨네요 :) 그대로 따라 하시면 어렵지 않게 설치를 끝내실 수 있습니다.

    터치 인식 장치가 준비되지 않은 윈도우 7 시스템에서는 컴퓨터의 시스템 정보 확인 창에 아래와 같이 표시될 겁니다.

    아아 너무나도 슬픈 소식입니다. ‘디스플레이에 사용할 수 있는 펜 및 터치식 입력이 없다’라고 너무나도 분명하게 확인시켜 주는군요. 자기가 하나 사줄 것도 아니면서 말입니다. 터치식 입력을 가진 장치를 사용하는 경우, 시스템에서 인식된 터치식 입력의 개수를 확인해 보고 싶으면 시스템 정보창의 해당 부분을 보면 알 수 있습니다.

    그런데 Multi-Touch Vista에서 제공하는 인터페이스 디바이스를 설치한 후 시스템 정보를 확인해 볼까요?

     

    이번엔 너무나도 훈훈한 메세지가 떴습니다. 무려 255개의 터치 포인트를 사용할 수 있다고 하는군요!! 장치를 활성화하면 갖다 붙인 마우스 수만큼 터치를 인식하게 되는데, 최대 255개 까지 인식을 하나 봅니다. 마우스를 255개 꽂으려면 usb 허브가 몇 개나 필요할까… 하는 생각은 저 혼자만 해 본건가요… +_+ 아무튼 이제부턴 최신식 터치 노트북을 장만한 옆집 철수를 부러워 하지 않아도 되는 순간입니다.

     

    Outro

    자, 이젠 정말 터치 프로그래밍을 하기 위한 모든 준비가 끝났습니다. 하드웨어를 마련할 길이 막막해 보여서 공부를 시작해야 할지 말지 망설이고 있으셨다면 우선은 마우스로 터치를 시뮬레이션하면서 공부를 먼저 시작하시면 됩니다. 네이버 검색을 잠시 해보았더니 오늘 소개해 드린 터치 인식 드라이버는 윈도우 모바일 7의 개발환경에도 유용하게 쓰이는 듯 하군요. ( 실버라이트 코리아 라는 네이버 카페 - http://slkorea.net/ – 에서 인디(inde83)님이 작성하신 동일한 디바이스 안내 및 설정법 글을 보았습니다. ) 윈모바일7의 개발에 관심 있으신 분도 눈 여겨 보시는 게 좋을 듯 합니다.

    다음 글부터는 본격적인 개발 이야기를 해보도록 하겠습니다. 재미있는 내용을 가지고 돌아올 테니 조금만 기다려 주세요 ^^
    그럼 다음 글에서 뵙겠습니다. 오늘도 좋은 하루 보내세요 ^^*
    감사합니다 @

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    안녕하세요. MFC 카테고리의 꽃집총각 입니다.
    오늘은 Technical Article에 앞서서 멀티터치에 대한 개념적인 글을 하나 준비해 보았습니다.

    출처 : http://farm3.static.flickr.com/2152/2547399057_f91154cb87_o.jpg

    이미지 출처 : http://farm3.static.flickr.com/2152/2547399057_f91154cb87_o.jpg

    이제는 ‘멀티터치’라는 기술은 신기술이라고 부르기도 어색할 만큼 많이 일반화 되었습니다.
    애플의 아이폰은 멀티터치의 대중화에 아주 본격적인 역할을 했습니다. 많은 사람들이 손바닥에 올려놓은 아이폰에서 사진을 멀티 터치로 zoom하고 slidng 합니다.

    멀티터치 기술에서 우리가 기대할 수 있는 효과를 아래의 두 가지로 정리할 수 있다고 생각합니다.

    1. 이전에 없었던 새로운 형태의 인터페이스 구현
    2. 컴퓨팅 능력이 부족한 저연령/고연령 세대들의 학습 문턱 제거.

    멀티터치는 좀 더 본능적이고, 직관적인 컴퓨팅을 가능하게 합니다. 굳이 하드웨어나 소프트웨어의 학습과정 없이, 매뉴얼도 없이 생각대로 조작하는 것을 가능하게 해주는 새로운 개념의 인터페이스 입니다.

    멀티터치 기술의 상용화는 이제 겨우 걸음마 단계라고 볼 수 있습니다.
    아직 우리가 상상하지 못한 놀라운 제스처들이 우리의 스크린에서 가능해 질 지도 모르는 일입니다.
    제가 개인적으로 멀티터치라는 기술을 처음 접했던 것은 2006년 제프한 박사의 TED 강연을 통해서 였습니다. 저는 그가 말하는 내용 중, ‘멀티 터치는 사람이 기계와 만나는 진정한 방법이다’ 라는 말이 꽤나 인상적이군요. 강연에서 제프한 박사는 앞으로 멀티터치를 활용한 보다 다양한 분야의 응용이 이루어 질 것이고, 인터페이스의 큰 변화를 가져올 것이라는 메세지를 던지고 있습니다.

    (한글 자막을 켜고 보세요)http://www.ted.com/talks/lang/eng/jeff_han_demos_his_breakthrough_touchscreen.html

    애플은 이런 멀티터치 기술을 아주 발 빠르게 자사의 product에 적용해 나가고 있습니다. 아이팟 터치와 아이폰, 매직마우스 등에서 멀티터치 제스처를 인식하고 있고, 얼마 전에 새롭게 발표된 맥북에서도 터치패드가 멀티터치를 인식하도록 개선되었습니다.

    마이크로소프트의 새로운 OS인 윈도우 7 역시도 멀티터치 인식 기능이 구현되어 있습니다. 하지만 생각 외로 많은 분들이 윈도우 7의 멀티터치 인식에 대해 잘 모르고 계시더군요. 아마도 애플의 제품군은 하드웨어와 소프트웨어가 함께 공급되어 바로 멀티터치를 체험해 볼 수 있는 것과는 달리, 윈도우 7은 멀티터치 인식 하드웨어가 없으면 직접 체험해 볼 수 없기 때문에 비교적 알려지지 않게 된 것이라고 추측해 봅니다.

    저는 얼마 전 3월 9일에 마이크로소프트에서 주최한 Windows 7 Technical Briefing 이라는 기술 세미나에서 ‘Win32 API를 이용해 멀티터치 프로그래밍하기’라는 주제로 세션을 맡아 발표한 적이 있습니다. 그 때를 계기로 윈도우 7에서 동작하는 멀티터치 프로그램의 개발에 대해 많은 관심을 갖게 되었습니다. 발표에 앞서 ① 윈도우 7을 터치로 조작해 보신 분이 있느냐는 질문과 ② 윈도우 7이 멀티터치를 인식한다는 사실을 알고 계셨느냐는 두 가지 질문을 드려보았는데, 개발자 분들을 대상으로 하는 기술 세션이었는데도 불구하고 생각 외로 많은 분들이 처음 듣는다는 반응을 보이셔서 의외라고 생각했습니다.

    아래의 동영상은 PDC 2008에서 윈도우 7의 멀티터치 인식 기능을 시연하는 장면입니다.

    보시다시피 이미 2년 전인 PDC 2008에서 윈도우 7의 멀티터치 기능은 대중들에게 활짝 공개 되었습니다. 말 그대로 ‘새로운 기능이라고 부르기도 어색할 만한’, 새기능 아닌 새기능 – 윈도우 7 터치 인식 인터페이스 입니다.

    앞서 잠깐 말씀 드렸던 것과 같이 윈도우 7 멀티터치의 가장 큰 장애요소는 ‘멀티터치 인식 하드웨어’가 아닌가 생각합니다. 하지만 이미 컴퓨터 시장에는 올인원 PC, 타블렛 PC, 터치 인식 노트북 등 다양한 멀티터치 인식 장치들이 쏟아져 나오고 있습니다. 유명한 d모 가격비교 사이트의 스샷을 올려드리고 싶지만, 특정 브랜드명이 다수 노출되어 그럴 수가 없네요 ^^;… 직접 한 번 검색해 보시기 바랍니다. 분명하게 말씀 드릴 수 있는 것은 향후 적지 않은 시간 동안 ‘멀티터치’ 분야는 하드웨어와 소프트웨어를 막론하고 컴퓨터 산업의 커다란 이슈가 될 것이라는 점 입니다. 아니, 이미 멀티터치는 커다란 이슈가 되었습니다.

    앞으로 팀블로그나 여러가지 활동을 통해서, 윈도우 7의 멀티터치 기능을 소개하는 기회를 가지려고 합니다. 그리고 이러한 무궁한 가능성을 가진 멀티터치를 Visual Studio 2010을 통해 Windows 7의 응용프로그램에 적용하는 방법을 시리즈로 연재하고자 합니다. WPF와 같은 Managed 언어로도 구현 가능하지만 제가 진행할 시리즈들은 MFC와 Win32를 이용한 터치 프로그래밍 방법이 주요 내용이 될 것입니다.

    그럼 앞으로 연재할 멀티터치 구현 포스팅에 많은 관심 부탁 드리면서
    다음 포스팅에서 뵙도록 하겠습니다.

    모두 오늘 좋은 하루 보내세요 ^^*

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    안녕하세요. MFC 카테고리의 꽃집총각입니다.

    꽤나 오랜만에 글을 올리는군요 ^^;
    그동안 팀 블로그에 올려 두었던 MFC의 기능 소개 글들을 한 번에 정리할 기회를 갖게 되어 예전 글들을 다시 살펴보며 정리하다가 CTaskDialog 클래스 사용법을 알아볼 수 있는 공개용 예제코드를 작성해 보았습니다. 포스팅을 할 때 사용했던 코드이기 때문에 주요코드들은 모두 블로그에 조각조각 올라와 있기는 하지만 다운받아서 직접 빌드하고 실행해보실 수 있도록 프로젝트 파일과 솔루션 파일 모두 함께 올립니다.


    소스코드는 당연히 Visual Studio 2010 RC 버전에서 제작되었습니다 ^^;...
    아래는 예제 코드를 켭쳐한 이미지 입니다.

    프로그램을 빌드해서 실행하면 대화상자 중앙에 두 개의 버튼이 있습니다.
    왼쪽 버튼을 누르면 간단한 출력 방식인 CTaskDialog::ShowDialog() 함수의 사용법을,
    오른쪽 버튼을 누르면 좀 더 디테일한 방법인 CTaskDialog::DoModal() 함수의 사용법을
    확인해 보실 수 있습니다.

    아래 두 장의 이미지는 각각의 방법들로 띄운 예제 Task Dialog 의 모습입니다.


     CTaskDialog::ShowDialog()를 이용해 출력한 Task Dialog.


    CTaskDialog::DoModal()을 이용해 출력한 Task Dialog.

    학습하시는 데 도움이 되었으면 하는 마음으로 올립니다.

    곧 MFC의 새롭고 재밌는 기능들을 주제로 다시 포스팅을 이어나가도록 하겠습니다.
    조금만 기다려 주세요 ~

    그럼 다음 포스팅에서 뵙겠습니다 ~
    감사합니다 ^^*

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    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

    저작자 표시
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    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
    저작자 표시
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    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

    저작자 표시
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    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

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License
    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)

     

    저작자 표시
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License
    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)

    저작자 표시
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License


     

    티스토리 툴바