summaryrefslogtreecommitdiff
path: root/wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp')
-rw-r--r--wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp526
1 files changed, 526 insertions, 0 deletions
diff --git a/wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp b/wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp
new file mode 100644
index 00000000..42b362f2
--- /dev/null
+++ b/wpd/WpdServiceSampleDriver/WpdServiceMethods.cpp
@@ -0,0 +1,526 @@
+#include "stdafx.h"
+
+#include "WpdServiceMethods.tmh"
+
+CMethodTask::CMethodTask(_In_ ServiceMethodContext* pContext) :
+ m_hThread(NULL),
+ m_pContext(pContext)
+{
+ m_pContext->AddRef();
+}
+
+CMethodTask::~CMethodTask()
+{
+ if (m_hThread != NULL)
+ {
+ CloseHandle(m_hThread);
+ m_hThread = NULL;
+ }
+ SAFE_RELEASE(m_pContext);
+}
+
+HRESULT CMethodTask::Run()
+{
+ HRESULT hr = S_OK;
+
+ // Create the thread
+ m_hThread = CreateThread(NULL, 0, ThreadProc, m_pContext, 0, NULL);
+ if (m_hThread == NULL)
+ {
+ DWORD dwError = GetLastError();
+ hr = HRESULT_FROM_WIN32(dwError);
+ }
+
+ return hr;
+}
+
+ServiceMethodContext::ServiceMethodContext() :
+ m_cRef(1),
+ m_pServiceMethods(NULL)
+{
+ m_pTask = NULL;
+}
+
+ServiceMethodContext::~ServiceMethodContext()
+{
+ if (m_pTask)
+ {
+ delete m_pTask;
+ m_pTask = NULL;
+ }
+}
+
+HRESULT ServiceMethodContext::Initialize(
+ _In_ WpdServiceMethods* pServiceMethods,
+ _In_ IPortableDeviceValues* pStartParams,
+ _In_ LPCWSTR pwszContext)
+{
+ HRESULT hr = S_OK;
+
+ m_pTask = new CMethodTask(this);
+ if (m_pTask != NULL)
+ {
+ m_pServiceMethods = pServiceMethods;
+ m_pStartParameters = pStartParams;
+ m_strContext = pwszContext;
+
+ hr = m_pTask->Run();
+ CHECK_HR(hr, "Failed to run method task");
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate method task");
+ }
+ return hr;
+}
+
+VOID ServiceMethodContext::InvokeMethod()
+{
+ if (m_pServiceMethods != NULL &&
+ m_pStartParameters != NULL &&
+ m_strContext.GetLength() > 0)
+ {
+ m_hrStatus = m_pServiceMethods->DispatchMethod(m_strContext, m_pStartParameters, &m_pResults);
+ }
+ CHECK_HR(m_hrStatus, "Failed to Dispatch method");
+}
+
+
+WpdServiceMethods::WpdServiceMethods()
+ : m_pContactsService(NULL)
+{
+
+}
+
+WpdServiceMethods::~WpdServiceMethods()
+{
+
+}
+
+HRESULT WpdServiceMethods::Initialize(
+ _In_ FakeContactsService* pContactsService)
+{
+ if (pContactsService == NULL)
+ {
+ return E_POINTER;
+ }
+ m_pContactsService = pContactsService;
+ return S_OK;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_SERVICE_METHODS_START_INVOKE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_SERVICE_METHOD: Indicates the method to invoke.
+ * This must be from the list returned by WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS
+ * or WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_ METHODS_BY_FORMAT.
+ *
+ * - WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES: IPortableDeviceValues containing the method parameters.
+ * Each parameter must be set in the ordering specified by WPD_PARAMETER_ATTRIBUTE_ORDER, with all parameters present.
+ * This must be an empty set if the method does not have any parameters.
+ *
+ * The driver should:
+ * - Return immediately with the method invocation context in WPD_PROPERTY_SERVICE_METHOD_CONTEXT.
+ * - When this method invocation completes, the driver must send a WPD_EVENT_SERVICE_METHOD_COMPLETE event
+ * with the WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT parameter set as this method context.
+ * - Lastly, the driver should wait for the WPD_COMMAND_SERVICE_METHODS_END_INVOKE command
+ * before cleaning up associated resources with this context
+ */
+HRESULT WpdServiceMethods::OnStartInvoke(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pwszContext = NULL;
+
+ // Create a new method context
+ hr = StartMethod(pParams, &pwszContext);
+ CHECK_HR(hr, "Failed to create a new method context");
+
+ // Return the method context in the results
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetStringValue(WPD_PROPERTY_SERVICE_METHOD_CONTEXT, pwszContext);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_METHOD_CONTEXT");
+ }
+
+ CoTaskMemFree(pwszContext);
+ return hr;
+}
+
+
+/**
+ * This method is called when we receive a WPD_COMMAND_SERVICE_METHODS_END_INVOKE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_SERVICE_METHOD_CONTEXT: Context of the method invocation being ended.
+ * This must be returned from WPD_COMMAND_SERVICE_METHODS_START_INVOKE
+ *
+ * The driver should:
+ * - Return the method results in WPD_PROPERTY_SERVICE_METHOD_RESULT_VALUES
+ * - Return the overall method status code in WPD_PROPERTY_SERVICE_METHOD_HRESULT
+ * - Destroy any resources associated with this context.
+ */
+HRESULT WpdServiceMethods::OnEndInvoke(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ HRESULT hrStatus = S_OK;
+ LPWSTR pwszContext = NULL;
+ ContextMap* pContextMap = NULL;
+
+ CComPtr<IPortableDeviceValues> pMethodResults;
+
+ hr = pParams->GetStringValue(WPD_PROPERTY_SERVICE_METHOD_CONTEXT, &pwszContext);
+ CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD_CONTEXT from IPortableDeviceValues");
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = EndMethod(pContextMap, pwszContext, &pMethodResults, &hrStatus);
+ CHECK_HR(hr, "Failed to destroy method context %ws", pwszContext);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetErrorValue(WPD_PROPERTY_SERVICE_METHOD_HRESULT, hrStatus);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_METHOD_HRESULT for method context %ws", pwszContext);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_RESULT_VALUES, pMethodResults);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_METHOD_RESULT_VALUES for method context %ws", pwszContext);
+ }
+
+ CoTaskMemFree(pwszContext);
+ SAFE_RELEASE(pContextMap);
+ return hr;
+}
+
+
+/**
+ * This method is called when we receive a WPD_COMMAND_SERVICE_METHODS_CANCEL_INVOKE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_SERVICE_METHOD_CONTEXT: Context of the method invocation being cancelled.
+ * This must be returned from WPD_COMMAND_SERVICE_METHODS_START_INVOKE
+ *
+ * The driver should:
+ * - Destroy any resources associated with this context.
+ *
+ */
+HRESULT WpdServiceMethods::OnCancelInvoke(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pwszContext = NULL;
+ ContextMap* pContextMap = NULL;
+ UNREFERENCED_PARAMETER(pResults);
+
+ hr = pParams->GetStringValue(WPD_PROPERTY_SERVICE_METHOD_CONTEXT, &pwszContext);
+ CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD_CONTEXT from IPortableDeviceValues");
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CancelMethod(pContextMap, pwszContext);
+ CHECK_HR(hr, "Failed to cancel the method");
+ }
+
+ CoTaskMemFree(pwszContext);
+ SAFE_RELEASE(pContextMap);
+ return hr;
+}
+
+HRESULT WpdServiceMethods::StartMethod(
+ _In_ IPortableDeviceValues* pParams,
+ _Outptr_result_nullonfailure_ LPWSTR* ppwszMethodContext)
+{
+ HRESULT hr = S_OK;
+ ContextMap* pContextMap = NULL;
+ ServiceMethodContext* pContext = NULL;
+ GUID Method = GUID_NULL;
+
+ CAtlStringW strKey;
+ CComPtr<IPortableDeviceValues> pMethodParams;
+
+ if((pParams == NULL) ||
+ (ppwszMethodContext == NULL))
+ {
+ hr = E_POINTER;
+ CHECK_HR(hr, "Cannot have NULL parameter");
+ return hr;
+ }
+
+ *ppwszMethodContext = NULL;
+
+ // Check if the method is supported
+ hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method);
+ CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD");
+
+ if (SUCCEEDED(hr) && !m_pContactsService->IsMethodSupported(Method))
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+ CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Get the context map which the driver stored in pParams for convenience
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pContext = new ServiceMethodContext();
+ if(pContext == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate new method context");
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pContextMap->Add(pContext, strKey);
+ CHECK_HR(hr, "Failed to insert method context into our context Map");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pContext->Initialize(this, pParams, strKey);
+ CHECK_HR(hr, "Failed to initialize the method context");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppwszMethodContext = AtlAllocTaskWideString(strKey);
+ if (*ppwszMethodContext == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate method context string");
+ }
+ }
+
+ SAFE_RELEASE(pContextMap);
+ SAFE_RELEASE(pContext);
+
+ return hr;
+}
+
+HRESULT WpdServiceMethods::EndMethod(
+ _In_ ContextMap* pContextMap,
+ _In_ LPCWSTR pwszMethodContext,
+ _COM_Outptr_result_maybenull_ IPortableDeviceValues** ppResults,
+ _Out_ HRESULT* phrStatus)
+{
+ HRESULT hr = S_OK;
+ ServiceMethodContext* pContext = NULL;
+ CAtlStringW strKey = pwszMethodContext;
+
+ *ppResults = NULL;
+ *phrStatus = S_OK;
+ pContext = (ServiceMethodContext*) pContextMap->GetContext(strKey);
+
+ if (pContext != NULL)
+ {
+ if (pContext->m_pResults)
+ {
+ hr = pContext->m_pResults->QueryInterface(IID_IPortableDeviceValues, (void**)ppResults);
+ CHECK_HR(hr, "Failed to QueryInterface IPortableDeviceValues for results");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *phrStatus = pContext->m_hrStatus;
+ }
+ pContextMap->Remove(strKey);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ CHECK_HR(hr, "Failed to get the context for %ws", pwszMethodContext);
+ }
+
+ if (FAILED(hr))
+ {
+ *phrStatus = hr;
+ }
+
+ SAFE_RELEASE(pContext);
+ return hr;
+}
+
+HRESULT WpdServiceMethods::CancelMethod(
+ _In_ ContextMap* pContextMap,
+ _In_ LPCWSTR pwszMethodContext)
+{
+ HRESULT hr = S_OK;
+ ServiceMethodContext* pContext = NULL;
+ CAtlStringW strKey = pwszMethodContext;
+
+ pContext = (ServiceMethodContext*) pContextMap->GetContext(strKey);
+
+ if (pContext != NULL)
+ {
+ //
+ // This is where we will cancel the method invocation associated
+ // with this context
+ //
+
+ // ....
+
+ // When done ... clean up associated resources
+ pContextMap->Remove(strKey);
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ CHECK_HR(hr, "Failed to get the context for %ws", pwszMethodContext);
+ }
+
+ SAFE_RELEASE(pContext);
+ return hr;
+}
+
+HRESULT WpdServiceMethods::DispatchMethod(
+ _In_ LPCWSTR pwszContext,
+ _In_ IPortableDeviceValues* pStartParams,
+ _COM_Outptr_ IPortableDeviceValues** ppResults)
+{
+ HRESULT hr = S_OK;
+ HRESULT hrStatus = S_OK;
+ GUID Method = GUID_NULL;
+ CComPtr<IPortableDeviceValues> pMethodParams;
+ CComPtr<IPortableDeviceValues> pMethodResults;
+
+ *ppResults = NULL;
+
+ // Get the method GUID
+ hr = pStartParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method);
+ CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD");
+
+ // Get the method parameters. These can be optional if the methods don't require parameters
+ if (SUCCEEDED(hr))
+ {
+ HRESULT hrTemp = pStartParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES, &pMethodParams);
+ CHECK_HR(hrTemp, "Failed to get WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES (ok if method does not require parameters)");
+ }
+
+ // Prepare the results collection
+ if (SUCCEEDED(hr))
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pMethodResults);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Invoke the method
+ if (IsEqualGUID(METHOD_FullEnumSyncSvc_BeginSync, Method))
+ {
+ hrStatus = m_pContactsService->OnBeginSync(pMethodParams, *ppResults);
+ CHECK_HR(hrStatus, "BeginSync method failed");
+ }
+ else if (IsEqualGUID(METHOD_FullEnumSyncSvc_EndSync, Method))
+ {
+ hrStatus = m_pContactsService->OnEndSync(pMethodParams, *ppResults);
+ CHECK_HR(hrStatus, "EndSync method failed");
+ }
+ else if (IsEqualGUID(MyCustomMethod, Method))
+ {
+ CComPtr<IPortableDeviceValues> pCustomEventParams;
+
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pCustomEventParams);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+
+ if (SUCCEEDED(hr))
+ {
+ hrStatus = m_pContactsService->OnMyCustomMethod(pMethodParams, pMethodResults, pCustomEventParams);
+ CHECK_HR(hrStatus, "MyCustomMethod method failed");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // In addition to a method complete event, we can also send a custom event,
+ // for example, to indicate progress of the method
+ hr = PostWpdEvent(pStartParams, pCustomEventParams);
+ CHECK_HR(hr, "Failed to post custom event");
+ }
+ }
+ else
+ {
+ hrStatus = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+ CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method));
+ }
+ }
+
+ // We always want to post a method completion event
+ // Even if the method has failed
+ {
+ CComPtr<IPortableDeviceValues> pEventParams;
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pEventParams);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_SERVICE_METHOD_COMPLETE);
+ CHECK_HR(hr, "Failed to set the event id to WPD_EVENT_SERVICE_METHOD_COMPLETE");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT, pwszContext);
+ CHECK_HR(hr, "Failed to set the method context for WPD_EVENT_SERVICE_METHOD_COMPLETE");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = PostWpdEvent(pStartParams, pEventParams);
+ CHECK_HR(hr, "Failed to post WPD_EVENT_SERVICE_METHOD_COMPLETE");
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = hrStatus;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppResults = pMethodResults.Detach();
+ }
+
+ return hr;
+}