다음을 통해 공유


관리되지 않는 코드에서 조작 지원 추가

이 섹션에서는 _IManipulationEvents 인터페이스에 대한 이벤트 싱크를 구현하여 관리되지 않는 코드에 조작 지원을 추가하는 방법을 설명합니다.

다음 이미지는 조작 아키텍처를 간략하게 설명합니다.

-imanipulationevents 인터페이스를 사용하여 이벤트를 처리하는 개체의 조작 프로세서에 전달되는 Windows 터치 메시지를 보여 주는 그림

WM_TOUCH 메시지에서 받은 터치 데이터는 터치 메시지의 연락처 ID와 함께 IManipulationProcessor에 전달됩니다. 메시지 시퀀스에 따라 IManipulationProcessor 인터페이스는 수행 중인 변환의 종류와 이 변환과 관련된 값을 계산합니다. 그런 다음 IManipulationProcessor 는 이벤트 싱크에서 처리하는 _IManipulationEvents 생성합니다. 그런 다음 이벤트 싱크는 이러한 값을 사용하여 변환되는 개체에 대한 사용자 지정 작업을 수행할 수 있습니다.

애플리케이션에 조작 지원을 추가하려면 다음 단계를 수행해야 합니다.

  1. _IManipulationEvents 인터페이스에 대한 이벤트 싱크를 구현합니다.
  2. IManipulationProcessor 인터페이스의 instance 만듭니다.
  3. 이벤트 싱크의 instance 만들고 터치 이벤트를 설정합니다.
  4. 조작 프로세서에 터치 이벤트 데이터를 보냅니다.

이 섹션에서는 애플리케이션에 조작 지원을 추가하기 위해 수행해야 하는 단계를 설명합니다. 시작하려면 각 단계에서 코드가 제공됩니다.

참고

제스처 및 터치 메시지는 상호 배타적이므로 조작 및 제스처를 동시에 사용할 수 없습니다.

_IManipualtionEvents 인터페이스에 대한 이벤트 싱크 구현

이벤트 싱크의 instance 만들려면 먼저 이벤트에 대한 _IManipulationEvents 인터페이스를 구현하는 클래스를 만들어야 합니다. 이벤트 싱크입니다. IManipulationProcessor 인터페이스에서 생성된 이벤트는 이벤트 싱크에서 처리됩니다. 다음 코드에서는 _IManipulationEvents 인터페이스를 상속하는 클래스에 대한 예제 헤더를 보여 줍니다.

// Manipulation Header Files
#include <comdef.h>
#include <manipulations.h>
#include <ocidl.h>

class CManipulationEventSink : _IManipulationEvents
{
public:
    CManipulationEventSink(IManipulationProcessor *manip, HWND hWnd);

    int GetStartedEventCount();
    int GetDeltaEventCount();
    int GetCompletedEventCount();
    double CManipulationEventSink::GetX();
    double CManipulationEventSink::GetY();        

    ~CManipulationEventSink();

    //////////////////////////////
    // IManipulationEvents methods
    //////////////////////////////
    virtual HRESULT STDMETHODCALLTYPE ManipulationStarted( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y);
    
    virtual HRESULT STDMETHODCALLTYPE ManipulationDelta( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y,
        /* [in] */ FLOAT translationDeltaX,
        /* [in] */ FLOAT translationDeltaY,
        /* [in] */ FLOAT scaleDelta,
        /* [in] */ FLOAT expansionDelta,
        /* [in] */ FLOAT rotationDelta,
        /* [in] */ FLOAT cumulativeTranslationX,
        /* [in] */ FLOAT cumulativeTranslationY,
        /* [in] */ FLOAT cumulativeScale,
        /* [in] */ FLOAT cumulativeExpansion,
        /* [in] */ FLOAT cumulativeRotation);
    
    virtual HRESULT STDMETHODCALLTYPE ManipulationCompleted( 
        /* [in] */ FLOAT x,
        /* [in] */ FLOAT y,
        /* [in] */ FLOAT cumulativeTranslationX,
        /* [in] */ FLOAT cumulativeTranslationY,
        /* [in] */ FLOAT cumulativeScale,
        /* [in] */ FLOAT cumulativeExpansion,
        /* [in] */ FLOAT cumulativeRotation);

    ////////////////////////////////////////////////////////////
    // IUnknown methods
    ////////////////////////////////////////////////////////////
    STDMETHOD_(ULONG, AddRef)(void);
    STDMETHOD_(ULONG, Release)(void);
    STDMETHOD(QueryInterface)(REFIID riid, LPVOID *ppvObj);

private:
    double m_fX;
    double m_fY;

    int m_cRefCount;
    int m_cStartedEventCount;
    int m_cDeltaEventCount;
    int m_cCompletedEventCount;

    IManipulationProcessor* m_pManip;

    IConnectionPointContainer* m_pConPointContainer;
    IConnectionPoint* m_pConnPoint;
    
    HWND m_hWnd;
};     

헤더가 지정된 경우 클래스가 조작 프로세서가 수행할 작업을 수행하도록 이벤트 인터페이스의 구현을 만들어야 합니다. 다음 코드는 _IManipulationEvents 인터페이스에 대한 이벤트 싱크의 최소 기능을 구현하는 템플릿입니다.

#include "stdafx.h"
#include "cmanipulationeventsink.h"

CManipulationEventSink::CManipulationEventSink(IManipulationProcessor *manip, HWND hWnd)
{
    m_hWnd = hWnd;

    //Set initial ref count to 1.
    m_cRefCount = 1;

    m_pManip = manip;
    m_pManip->put_PivotRadius(-1);

    m_cStartedEventCount = 0;
    m_cDeltaEventCount = 0;
    m_cCompletedEventCount = 0;

    HRESULT hr = S_OK;

    //Get the container with the connection points.
    IConnectionPointContainer* spConnectionContainer;
    
    hr = manip->QueryInterface(
      IID_IConnectionPointContainer, 
          (LPVOID*) &spConnectionContainer
        );
    //hr = manip->QueryInterface(&spConnectionContainer);
    if (spConnectionContainer == NULL){
        // something went wrong, try to gracefully quit
        
    }

    //Get a connection point.
    hr = spConnectionContainer->FindConnectionPoint(__uuidof(_IManipulationEvents), &m_pConnPoint);
    if (m_pConnPoint == NULL){
        // something went wrong, try to gracefully quit
    }

    DWORD dwCookie;

    //Advise.
    hr = m_pConnPoint->Advise(this, &dwCookie);
}

int CManipulationEventSink::GetStartedEventCount()
{
    return m_cStartedEventCount;
}

int CManipulationEventSink::GetDeltaEventCount()
{
    return m_cDeltaEventCount;
}

int CManipulationEventSink::GetCompletedEventCount()
{
    return m_cCompletedEventCount;
}

double CManipulationEventSink::GetX()
{
    return m_fX;
}

double CManipulationEventSink::GetY()
{
    return m_fY;
}

CManipulationEventSink::~CManipulationEventSink()
{
    //Cleanup.
}

///////////////////////////////////
//Implement IManipulationEvents
///////////////////////////////////

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationStarted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y)
{
    m_cStartedEventCount ++;
    
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationDelta( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT translationDeltaX,
    /* [in] */ FLOAT translationDeltaY,
    /* [in] */ FLOAT scaleDelta,
    /* [in] */ FLOAT expansionDelta,
    /* [in] */ FLOAT rotationDelta,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cDeltaEventCount ++;
    
    RECT rect;
        
    GetWindowRect(m_hWnd, &rect);
    
    int oldWidth =  rect.right-rect.left;
    int oldHeight = rect.bottom-rect.top;            

    // scale and translate the window size / position    
    MoveWindow(m_hWnd,                                                     // the window to move
               static_cast<int>(rect.left + (translationDeltaX / 100.0f)), // the x position
               static_cast<int>(rect.top + (translationDeltaY/100.0f)),    // the y position
               static_cast<int>(oldWidth * scaleDelta),                    // width
               static_cast<int>(oldHeight * scaleDelta),                   // height
               TRUE);                                                      // redraw
                     
    return S_OK;
}

HRESULT STDMETHODCALLTYPE CManipulationEventSink::ManipulationCompleted( 
    /* [in] */ FLOAT x,
    /* [in] */ FLOAT y,
    /* [in] */ FLOAT cumulativeTranslationX,
    /* [in] */ FLOAT cumulativeTranslationY,
    /* [in] */ FLOAT cumulativeScale,
    /* [in] */ FLOAT cumulativeExpansion,
    /* [in] */ FLOAT cumulativeRotation)
{
    m_cCompletedEventCount ++;

    m_fX = x;
    m_fY = y;

    // place your code handler here to do any operations based on the manipulation   

    return S_OK;
}


/////////////////////////////////
//Implement IUnknown
/////////////////////////////////

ULONG CManipulationEventSink::AddRef(void) 
{
    return ++m_cRefCount;
}

ULONG CManipulationEventSink::Release(void)
{ 
    m_cRefCount --;

    if(0 == m_cRefCount) {
        delete this;
        return 0;
    }

    return m_cRefCount;
}

HRESULT CManipulationEventSink::QueryInterface(REFIID riid, LPVOID *ppvObj) 
{
    if (IID__IManipulationEvents == riid) {
        *ppvObj = (_IManipulationEvents *)(this); AddRef(); return S_OK;
    } else if (IID_IUnknown == riid) {
        *ppvObj = (IUnknown *)(this); AddRef(); return S_OK;
    } else {
        return E_NOINTERFACE;
    }
}         

클래스에서 ManipulationStarted, ManipulationDeltaManipulationCompleted 메서드의 구현에 주의하세요. 이러한 메서드는 인터페이스에서 가장 가능성이 높은 메서드로, 이벤트에서 전달되는 조작 정보를 기반으로 작업을 수행해야 합니다. 또한 생성자의 두 번째 매개 변수는 이벤트 조작에 사용되는 개체입니다. 샘플을 생성하는 데 사용되는 코드에서 애플리케이션의 hWnd가 생성자로 전송되어 위치가 변경되고 크기가 조정될 수 있습니다.

IManipulationProcessor 인터페이스의 instance 만들기

조작을 사용할 코드에서 IManipulationProcessor 인터페이스의 instance 만들어야 합니다. 먼저 조작 클래스에 대한 지원을 추가해야 합니다. 다음 코드에서는 클래스에서 이 작업을 수행하는 방법을 보여줍니다.

//Include windows.h for touch events
#include "windows.h"  

// Manipulation implementation file
#include <manipulations_i.c>
    
// Smart Pointer to a global reference of a manipulation processor, event sink
IManipulationProcessor* g_pIManipProc;     

조작 프로세서에 대한 변수가 있고 조작용 헤더를 포함하면 IManipulationProcessor 인터페이스의 instance 만들어야 합니다. COM 개체입니다. 따라서 CoCreateInstance를 호출한 다음 IManipulationProcessor에 대한 참조의 instance 만들어야 합니다. 다음 코드에서는 이 인터페이스의 instance 만드는 방법을 보여줍니다.

   HRESULT hr = CoInitialize(0);
        
   hr = CoCreateInstance(CLSID_ManipulationProcessor,
       NULL,
       CLSCTX_INPROC_SERVER,
       IID_IUnknown,
       (VOID**)(&g_pIManipProc)
   );

이벤트 싱크의 instance 만들고 터치 이벤트 설정

코드에 이벤트 싱크 클래스에 대한 정의를 포함하고 조작 이벤트 싱크 클래스에 대한 변수를 추가합니다. 다음 코드 예제에서는 클래스 구현에 대 한 헤더를 포함 하 고 이벤트 싱크를 저장 하는 전역 변수를 설정 합니다.

//Include your definition of the event sink, CManipulationEventSink.h in this case
#include "CManipulationEventSink.h"    
     
// Set up a variable to point to the manipulation event sink implementation class    
CManipulationEventSink* g_pManipulationEventSink;   

변수가 있고 새 이벤트 싱크 클래스에 대한 정의를 포함하면 이전 단계에서 설정한 조작 프로세서를 사용하여 클래스를 생성할 수 있습니다. 다음 코드는 OnInitDialog에서 이 클래스의 instance 만드는 방법을 보여줍니다.

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

참고

이벤트 싱크의 instance 만드는 방법은 조작 데이터를 사용하여 수행하는 작업에 따라 달라집니다. 대부분의 경우 이 예제와 동일한 생성자가 없는 조작 프로세서 이벤트 싱크를 만듭니다.

조작 프로세서에 터치 이벤트 데이터 보내기

이제 조작 프로세서 및 이벤트 싱크를 설정했으므로 조작 이벤트를 트리거하기 위해 터치 데이터를 조작 프로세서에 피드해야 합니다.

참고

이는 Windows Touch 메시지와 시작 설명한 것과 동일한 절차입니다.

먼저 WM_TOUCH 메시지를 디코딩하고 IManipulationProcessor 인터페이스로 보내 이벤트를 발생시킬 코드를 만듭니다. 다음 코드는 WndProc 메서드에서 호출되고 메시징을 위해 LRESULT 를 반환하는 예제 구현을 보여줍니다.

LRESULT OnTouch(HWND hWnd, WPARAM wParam, LPARAM lParam )
{
  UINT cInputs = LOWORD(wParam);
  PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
  
  BOOL bHandled = FALSE;
  
  if (NULL != pInputs) {
    if (GetTouchInputInfo((HTOUCHINPUT)lParam,
      cInputs,
      pInputs,
      sizeof(TOUCHINPUT))) {      
      for (UINT i=0; i<cInputs; i++){
        if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN){
            g_pIManipProc->ProcessDown(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
        if (pInputs[i].dwFlags & TOUCHEVENTF_UP){
            g_pIManipProc->ProcessUp(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
        if (pInputs[i].dwFlags & TOUCHEVENTF_MOVE){
            g_pIManipProc->ProcessMove(pInputs[i].dwID, static_cast<FLOAT>(pInputs[i].x), static_cast<FLOAT>(pInputs[i].y));
            bHandled = TRUE;
        }
      }      
    } else {
      // GetLastError() and error handling
    }
    delete [] pInputs;
  } else {
    // error handling, presumably out of memory
  }
  if (bHandled){
    // if you don't want to pass to DefWindowProc, close the touch input handle
    if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) {
        // error handling
    }
    return 0;
  }else{
    return DefWindowProc(hWnd, WM_TOUCH, wParam, lParam);
  }
}

이제 WM_TOUCH 메시지를 디코딩하는 유틸리티 메서드가 있으므로 WndProc 메서드에서 유틸리티 함수에 WM_TOUCH 메시지를 전달해야 합니다. 다음 코드에서는 이 작업을 수행하는 방법을 보여줍니다.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message)
    {
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        // Parse the menu selections:
        switch (wmId)
        {
        case IDM_ABOUT:
            DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;
    case WM_TOUCH:
        return OnTouch(hWnd, wParam, lParam);
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        // TODO: Add any drawing code here...
        EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

이제 이벤트 싱크에서 구현한 사용자 지정 메서드가 작동합니다. 이 예제에서는 창을 터치하면 창이 이동합니다.

조작