アンマネージド コードでの操作のサポートの追加

このセクションでは、 _IManipulationEvents インターフェイスのイベント シンクを実装して、アンマネージ コードに操作のサポートを追加する方法について説明します。

次の図は、操作アーキテクチャの概要を示しています。

-imanipulationevents インターフェイスを使用してイベントを処理する オブジェクトの操作プロセッサに渡される Windows タッチ メッセージを示す図

WM_TOUCHメッセージから受信したタッチ データは、タッチ メッセージからの連絡先 ID と共に IManipulationProcessor に渡されます。 メッセージ シーケンスに基づいて、 IManipulationProcessor インターフェイスは、実行されている変換の種類と、この変換に関連付けられている値を計算します。 IManipulationProcessor は、イベント シンクによって処理される_IManipulationEventsを生成します。 その後、イベント シンクはこれらの値を使用して、変換されるオブジェクトに対してカスタム操作を実行できます。

アプリケーションに操作のサポートを追加するには、次の手順に従う必要があります。

  1. _IManipulationEvents インターフェイスのイベント シンクを実装します。
  2. IManipulationProcessor インターフェイスのインスタンスを作成します。
  3. イベント シンクのインスタンスを作成し、タッチ イベントを設定します。
  4. タッチ イベント データを操作プロセッサに送信します。

このセクションでは、操作のサポートをアプリケーションに追加するために従う必要がある手順について説明します。 作業を開始するために、各手順でコードが提供されます。

Note

ジェスチャ メッセージとタッチ メッセージは相互に排他的であるため、操作とジェスチャを同時に使用することはできません。

_IManipualtionEvents インターフェイスのイベント シンクを実装する

イベント シンクのインスタンスを作成する前に、イベント処理用の _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;
    }
}         

クラスの ManipulationStartedManipulationDeltaおよび ManipulationCompleted メソッドの実装に特に注意してください。 これらは、イベントで渡される操作情報に基づいて操作を実行する必要がある、インターフェイス内の最も可能性の高いメソッドです。 また、コンストラクターの 2 番目のパラメーターは、イベント操作で使用される オブジェクトであることにも注意してください。 サンプルの生成に使用されるコードでは、再配置およびサイズ変更できるように、アプリケーションの hWnd がコンストラクターに送信されます。

IManipulationProcessor インターフェイスのインスタンスを作成する

操作を使用するコードでは、 IManipulationProcessor インターフェイスのインスタンスを作成する必要があります。 まず、操作クラスのサポートを追加する必要があります。 次のコードは、クラスでこれを行う方法を示しています。

//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 インターフェイスのインスタンスを作成する必要があります。 これは COM オブジェクトです。 したがって、 CoCreateInstance を呼び出し、 IManipulationProcessor への参照のインスタンスを作成する必要があります。 次のコードは、このインターフェイスのインスタンスを作成する方法を示しています。

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

イベント シンクのインスタンスを作成し、タッチ イベントを設定する

イベント シンク クラスの定義をコードに含め、操作イベント シンク クラスの変数を追加します。 次のコード例では、 クラス実装の ヘッダーを含め、イベント シンクを格納するグローバル変数を設定します。

//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 から作成する方法を示しています。

   g_pManipulationEventSink = new CManipulationEventSink(g_pIManipProc, hWnd);


   RegisterTouchWindow(hWnd, 0);  

Note

イベント シンクのインスタンスを作成する方法は、操作データで何をしているかによって異なります。 ほとんどの場合、この例と同じコンストラクターを持たない操作プロセッサ イベント シンクを作成します。

タッチ イベント データを操作プロセッサに送信する

操作プロセッサとイベント シンクを設定したら、操作イベントをトリガーするためにタッチ データを操作プロセッサにフィードする必要があります。

Note

これは、「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 メッセージをデコードするためのユーティリティ メソッドが用意されたので、WM_TOUCH メッセージを WndProc メソッドからユーティリティ関数に渡す必要があります。 次のコードは、これを行う方法を示しています。

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;
}

イベント シンクに実装したカスタム メソッドが機能するようになりました。 この例では、ウィンドウをタッチすると移動します。

操作