サービス メソッドの非同期呼び出し

WpdServiceApiSample アプリケーションには、アプリケーションがサービス メソッドを非同期的に呼び出す方法を示すコードが含まれています。 このサンプルでは、次のインターフェイスを使用します。

インターフェイス 説明
IPortableDeviceService 特定のサービスでメソッドを呼び出すために IPortableDeviceServiceMethods インターフェイスを取得するために使用されます。
IPortableDeviceServiceMethods サービス メソッドを呼び出すために使用されます。
IPortableDeviceValues 送信メソッドパラメーターと受信メソッドの結果を保持するために使用されます。 メソッドがパラメーターを必要としない場合や結果を返す場合は 、NULL を 指定できます。
IPortableDeviceServiceMethodCallback メソッドが完了したときにメソッドの結果を受け取るためにアプリケーションによって実装されます。

 

ユーザーがコマンド ラインでオプション "10" を選択すると、アプリケーションは ServiceMethods.cpp モジュールにある InvokeMethodsAsync メソッドを呼び出します。 メソッドを呼び出す前に、サンプル アプリケーションは接続されているデバイスで Contacts サービスを開きます。

サービス メソッドは、各サービスが定義して実装する機能をカプセル化します。 これらはサービスの種類ごとに一意であり、GUID で表されます。 たとえば、Contacts サービスは、アプリケーションが Contact オブジェクトを同期するためにデバイスを準備するために呼び出す BeginSync メソッドと、同期が完了したことをデバイスに通知する EndSync メソッドを定義します。 アプリケーションは、 IPortableDeviceServiceMethods::Invoke を呼び出してポータブル デバイス サービス メソッドを実行します。

サービス メソッドを WPD コマンドと混同しないでください。 WPD コマンドは、標準の WPD デバイス ドライバー インターフェイス (DDI) の一部であり、WPD アプリケーションとドライバー間の通信のメカニズムです。 コマンドは定義済みで、カテゴリ ( たとえば、WPD_CATEGORY_COMMON) でグループ化され、 PROPERTYKEY 構造体で表されます。 アプリケーションは 、IPortableDeviceService::SendCommand を呼び出して、デバイス ドライバーにコマンドを送信します。 詳細については、「コマンド」トピックを参照してください。

InvokeMethodsAsync メソッドは、IPortableDeviceService::Methods を呼び出して IPortableDeviceServiceMethods インターフェイスを取得します。 このインターフェイスを使用して、 InvokeMethodAsync ヘルパー関数を 2 回呼び出します。 BeginSync メソッドの場合は 1 回、 EndSync メソッドの場合は 1 回です。 この例では、 InvokeMethodAsync、IPortableDeviceServiceMethodCallback::OnComplete が呼び出されたときに、グローバル イベントが通知されるまで無期限に待機します。

次のコードでは 、InvokeMethodsAsync メソッドを 使用します。

// Invoke methods on the Contacts Service asynchornously.
// BeginSync and EndSync are methods defined by the FullEnumerationSync Device Service.
void InvokeMethodsAsync(IPortableDeviceService* pService)
{
    HRESULT hr = S_OK;
    CComPtr<IPortableDeviceServiceMethods> pMethods;

    if (pService == NULL)
    {
        printf("! A NULL IPortableDeviceService interface pointer was received\n");
        return;
    }

    // Get an IPortableDeviceServiceMethods interface from the IPortableDeviceService interface to
    // invoke methods.
    hr = pService->Methods(&pMethods);
    if (FAILED(hr))
    {
        printf("! Failed to get IPortableDeviceServiceMethods from IPortableDeviceService, hr = 0x%lx\n",hr);
    }

    // Invoke the BeginSync method asynchronously
    if (SUCCEEDED(hr))
    {
        printf("Invoking %ws asynchronously...\n",NAME_FullEnumSyncSvc_BeginSync);

        // This method does not take any parameters, so we pass in NULL
        hr = InvokeMethodAsync(pMethods, METHOD_FullEnumSyncSvc_BeginSync, NULL);
        if (FAILED(hr))
        {
            printf("! Failed to invoke %ws asynchronously, hr = 0x%lx\n",NAME_FullEnumSyncSvc_BeginSync, hr);
        }
    }

    // Invoke the EndSync method asynchronously
    if (SUCCEEDED(hr))
    {
        printf("Invoking %ws asynchronously...\n",NAME_FullEnumSyncSvc_EndSync);

        hr = InvokeMethodAsync(pMethods, METHOD_FullEnumSyncSvc_EndSync, NULL);
        if (FAILED(hr))
        {
            printf("! Failed to invoke %ws asynchronously, hr = 0x%lx\n",NAME_FullEnumSyncSvc_EndSync, hr);
        }
    }
}

InvokeMethodAsync ヘルパー関数は、呼び出すメソッドごとに次の処理を行います。

  • メソッドの完了を決定するために監視するグローバル イベント ハンドルを作成します。
  • IPortableDeviceServiceMethods:InvokeAsync の引数として指定される CMethodCallback オブジェクトを作成します。
  • IPortableDeviceServiceMethods::InvokeAsync メソッドを呼び出して、指定されたメソッドを呼び出します。
  • グローバル イベント ハンドルの完了を監視します。
  • クリーンアップを実行します。

CMethodCallback クラスは、アプリケーションで IPortableDeviceServiceMethodCallback を実装する方法を示しています。 このクラスの OnComplete の実装は、サービス メソッドが完了したことをアプリケーションに通知するイベントを通知します。 OnComplete メソッドに加えて、このクラスは AddRefQueryInterfaceRelease を実装します。これは、オブジェクトの参照カウントと実装するインターフェイスを維持するために使用されます。

class CMethodCallback : public IPortableDeviceServiceMethodCallback
{
public:
   CMethodCallback () : m_cRef(1)
   {
   }

   ~CMethodCallback ()
   {
   }

public:
    // IPortableDeviceServiceMethodCallback::QueryInterface
    virtual HRESULT STDMETHODCALLTYPE OnComplete(
        HRESULT                 hrStatus,
        IPortableDeviceValues*  /*pResults*/) // We are ignoring results as our methods will not return any results
    {
        printf("** Method completed, status HRESULT = 0x%lx **\n", hrStatus);

        if (g_hMethodCompleteEvent != NULL)
        {
            SetEvent(g_hMethodCompleteEvent);
        }          
        return S_OK;
    }

    // IUnknown::AddRef
    virtual ULONG STDMETHODCALLTYPE AddRef(void)
    {
        InterlockedIncrement((long*) &m_cRef);
        return m_cRef;
    }

    // IUnknown::QueryInterface
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
        REFIID  riid,
        LPVOID* ppvObj)
    {
        HRESULT hr = S_OK;
        if (ppvObj == NULL)
        {
            hr = E_INVALIDARG;
            return hr;
        }

        if ((riid == IID_IUnknown) ||
            (riid == IID_IPortableDeviceServiceMethodCallback))
        {
            AddRef();
            *ppvObj = this;
        }
        else
        {
            *ppvObj = NULL;
            hr = E_NOINTERFACE;
        }
        return hr;
    }

    // IUnknown::Release
    virtual ULONG STDMETHODCALLTYPE Release(void)
    {
        ULONG ulRefCount = m_cRef - 1;

        if (InterlockedDecrement((long*) &m_cRef) == 0)
        {
            delete this;
            return 0;
        }
        return ulRefCount;
    }

private:
    DWORD   m_cRef;
};

IPortableDeviceService

IPortableDeviceServiceMethodCallback

IPortableDeviceServiceMethods

WpdServicesApiSample