관리되지 않는 코드에서 조작 지원 추가
이 섹션에서는 _IManipulationEvents 인터페이스에 대한 이벤트 싱크를 구현하여 관리되지 않는 코드에 조작 지원을 추가하는 방법을 설명합니다.
다음 이미지는 조작 아키텍처를 간략하게 설명합니다.
WM_TOUCH 메시지에서 받은 터치 데이터는 터치 메시지의 연락처 ID와 함께 IManipulationProcessor에 전달됩니다. 메시지 시퀀스에 따라 IManipulationProcessor 인터페이스는 수행 중인 변환의 종류와 이 변환과 관련된 값을 계산합니다. 그런 다음 IManipulationProcessor 는 이벤트 싱크에서 처리하는 _IManipulationEvents 생성합니다. 그런 다음 이벤트 싱크는 이러한 값을 사용하여 변환되는 개체에 대한 사용자 지정 작업을 수행할 수 있습니다.
애플리케이션에 조작 지원을 추가하려면 다음 단계를 수행해야 합니다.
- _IManipulationEvents 인터페이스에 대한 이벤트 싱크를 구현합니다.
- IManipulationProcessor 인터페이스의 instance 만듭니다.
- 이벤트 싱크의 instance 만들고 터치 이벤트를 설정합니다.
- 조작 프로세서에 터치 이벤트 데이터를 보냅니다.
이 섹션에서는 애플리케이션에 조작 지원을 추가하기 위해 수행해야 하는 단계를 설명합니다. 시작하려면 각 단계에서 코드가 제공됩니다.
참고
제스처 및 터치 메시지는 상호 배타적이므로 조작 및 제스처를 동시에 사용할 수 없습니다.
_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, ManipulationDelta 및 ManipulationCompleted 메서드의 구현에 주의하세요. 이러한 메서드는 인터페이스에서 가장 가능성이 높은 메서드로, 이벤트에서 전달되는 조작 정보를 기반으로 작업을 수행해야 합니다. 또한 생성자의 두 번째 매개 변수는 이벤트 조작에 사용되는 개체입니다. 샘플을 생성하는 데 사용되는 코드에서 애플리케이션의 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;
}
이제 이벤트 싱크에서 구현한 사용자 지정 메서드가 작동합니다. 이 예제에서는 창을 터치하면 창이 이동합니다.
관련 항목