Поделиться через


Асинхронный вызов методов службы

Приложение WpdServiceApiSample содержит код, демонстрирующий, как приложение может асинхронно вызывать методы службы. В этом примере используются следующие интерфейсы.

Интерфейс Описание
IPortableDeviceService Используется для получения интерфейса IPortableDeviceServiceMethods для вызова методов в данной службе.
IPortableDeviceServiceMethods Используется для вызова метода службы.
IPortableDeviceValues Используется для хранения параметров исходящего метода и входящих результатов метода. Это значение может иметь значение NULL , если метод не требует никаких параметров или не возвращает результаты.
IPortableDeviceServiceMethodCallback Реализуется приложением для получения результатов метода после завершения метода.

 

Когда пользователь выбирает параметр "10" в командной строке, приложение вызывает метод InvokeMethodsAsync , который находится в модуле ServiceMethods.cpp. Обратите внимание, что перед вызовом методов пример приложения открывает службу контактов на подключенном устройстве.

Методы службы инкапсулируют функциональные возможности, которые каждая служба определяет и реализует. Они уникальны для каждого типа службы и представлены идентификатором GUID. Например, служба "Контакты" определяет метод BeginSync , вызываемый приложениями для подготовки устройства к синхронизации объектов Contact, и метод EndSync для уведомления устройства о завершении синхронизации. Приложения выполняют метод службы переносимого устройства, вызывая метод IPortableDeviceServiceMethods::Invoke.

Методы службы не следует путать с командами WPD. Команды WPD являются частью стандартного интерфейса драйвера устройства WPD (DDI) и являются механизмом взаимодействия между приложением WPD и драйвером. Команды предварительно определены, сгруппированы по категориям (например, WPD_CATEGORY_COMMON) и представлены структурой PROPERTYKEY . Приложение отправляет команды драйверу устройства, вызывая IPortableDeviceService::SendCommand. Дополнительные сведения см. в разделе Команды.

Метод InvokeMethodsAsync вызывает IPortableDeviceService::Methods для получения интерфейса IPortableDeviceServiceMethods . Используя этот интерфейс, он вызывает вспомогательную функцию InvokeMethodAsync дважды; один раз для метода BeginSync и один раз для метода EndSync . В этом примере 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 выполняет следующие действия для каждого вызываемого метода:

  • Создает глобальный дескриптор событий, отслеживаемый для определения завершения метода.
  • Создает объект CMethodCallback , который предоставляется в качестве аргумента для IPortableDeviceServiceMethods:InvokeAsync.
  • Вызывает метод IPortableDeviceServiceMethods::InvokeAsync для вызова заданного метода.
  • Отслеживает глобальный дескриптор событий для завершения.
  • Выполняет очистку.

Класс CMethodCallback демонстрирует, как приложение может реализовать IPortableDeviceServiceMethodCallback. Реализация OnComplete в этом классе сигнализирует о событии для уведомления приложения о завершении метода службы. В дополнение к методу OnComplete этот класс реализует AddRef, QueryInterface и Release, которые используются для поддержания количества ссылок объекта и интерфейсов, которые он реализует.

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