diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /wpd/WpdServiceSampleDriver | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'wpd/WpdServiceSampleDriver')
57 files changed, 14135 insertions, 0 deletions
diff --git a/wpd/WpdServiceSampleDriver/Device.cpp b/wpd/WpdServiceSampleDriver/Device.cpp new file mode 100644 index 00000000..b5f4f278 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Device.cpp @@ -0,0 +1,367 @@ +#include "stdafx.h" +#include "Device.h" +#include "WpdServiceSampleDriver_i.c" + +#include "Device.tmh" + +STDMETHODIMP_(HRESULT) +CDevice::OnD0Entry(_In_ IWDFDevice* /*pDevice*/, WDF_POWER_DEVICE_STATE /*previousState*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnD0Exit(_In_ IWDFDevice* /*pDevice*/, WDF_POWER_DEVICE_STATE /*newState*/) +{ + return S_OK; +} + +STDMETHODIMP_(VOID) +CDevice::OnSurpriseRemoval(_In_ IWDFDevice* /*pDevice*/) +{ + return; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnQueryRemove(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnQueryStop(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(VOID) +CDevice::OnSelfManagedIoCleanup(_In_ IWDFDevice* /*pDevice*/) +{ + return; +} + +STDMETHODIMP_(VOID) +CDevice::OnSelfManagedIoFlush(_In_ IWDFDevice* /*pDevice*/) +{ + return; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoInit(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoSuspend(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoRestart(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnSelfManagedIoStop(_In_ IWDFDevice* /*pDevice*/) +{ + return S_OK; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnPrepareHardware(_In_ IWDFDevice* pDevice) +{ + HRESULT hr = S_OK; + + if (m_pWpdBaseDriver != NULL) + { + hr = m_pWpdBaseDriver->Initialize(); + CHECK_HR(hr, "Failed to Initialize the driver class"); + } + + // Initialize the WPD Class Extension. This will enable the appropriate WPD interface GUID, + // as well as do any additional initialization (e.g. enabling Legacy Compatibility layers for those drivers + // which requested support in their INF). + if (hr == S_OK && m_pPortableDeviceClassExtension == NULL) + { + CComPtr<IPortableDeviceValues> pOptions; + CComPtr<IPortableDevicePropVariantCollection> pContentTypes; + + hr = CoCreateInstance(CLSID_PortableDeviceClassExtension, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceClassExtension, + (VOID**)&m_pPortableDeviceClassExtension); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceClassExtension"); + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pOptions); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if (hr == S_OK) + { + // Get supported content types + if (hr == S_OK) + { + hr = GetSupportedContentTypes(&pContentTypes); + CHECK_HR(hr, "Failed to get supported content types"); + } + + // Add the supported types to the options + if (hr == S_OK) + { + hr = pOptions->SetIPortableDevicePropVariantCollectionValue(WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES, pContentTypes); + CHECK_HR(hr, "Failed to set WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES"); + } + + // Initialize the PortableDeviceClassExtension with a list of supported content types for the + // connected device. This will ensure that the correct application compatibility settings will + // be applied for your device. + if (hr == S_OK) + { + hr = m_pPortableDeviceClassExtension->Initialize(pDevice, pOptions); + CHECK_HR(hr, "Failed to Initialize portable device class extension object"); + } + + // Register the services as Plug and Play interfaces + if (hr == S_OK) + { + hr = RegisterServices(m_pPortableDeviceClassExtension, false /*bUnregister*/); + CHECK_HR(hr, "Failed to register services"); + } + + // Since users commonly have the abiltity to customize their device even when it is not + // connected to the PC, we need to make sure the PC is current when the driver loads. + // + // Send the latest device friendly name to the PortableDeviceClassExtension component + // so the system is always updated with the current device name. + // + // This call should also be made after a successful property set operation of + // WPD_DEVICE_FRIENDLY_NAME. + + LPWSTR wszDeviceFriendlyName = NULL; + + if (hr == S_OK) + { + hr = GetDeviceFriendlyName(&wszDeviceFriendlyName); + CHECK_HR(hr, "Failed to get device's friendly name"); + } + + if (hr == S_OK) + { + hr = UpdateDeviceFriendlyName(m_pPortableDeviceClassExtension, wszDeviceFriendlyName); + CHECK_HR(hr, "Failed to update device's friendly name"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszDeviceFriendlyName); + } + } + } + + return hr; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnReleaseHardware(_In_ IWDFDevice* /*pDevice*/) +{ + // Unregister the services as Plug and Play interfaces (errors are ignored). + HRESULT hr = RegisterServices(m_pPortableDeviceClassExtension, true /*bUnregister*/); + CHECK_HR(hr, "Failed to unregister services"); + + if (m_pWpdBaseDriver != NULL) + { + m_pWpdBaseDriver->Uninitialize(); + } + + if (m_pPortableDeviceClassExtension != NULL) + { + m_pPortableDeviceClassExtension = NULL; + } + + return S_OK; +} + +HRESULT CDevice::GetSupportedContentTypes( + _Outptr_ IPortableDevicePropVariantCollection** ppContentTypes) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + + if (ppContentTypes == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + *ppContentTypes = NULL; + + // CoCreate a collection to store the WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES command parameters. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES command results. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Set the params + if (SUCCEEDED(hr)) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + if (SUCCEEDED(hr)) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + if (SUCCEEDED(hr)) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, WPD_FUNCTIONAL_CATEGORY_ALL); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY")); + } + + // Make the call + if (SUCCEEDED(hr)) + { + hr = m_pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to dispatch message to get supported content types")); + } + + // Get the results + if (SUCCEEDED(hr)) + { + hr = pResults->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, ppContentTypes); + CHECK_HR(hr, ("Failed to get WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES")); + } + + return hr; +} + +HRESULT CDevice::GetDeviceFriendlyName( + _Outptr_ LPWSTR* pwszDeviceFriendlyName) +{ + HRESULT hr = S_OK; + + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + CComPtr<IPortableDeviceKeyCollection> pKeys; + CComPtr<IPortableDeviceValues> pValues; + + if (pwszDeviceFriendlyName == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + *pwszDeviceFriendlyName = NULL; + + // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command parameters. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command results. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the requested property keys. In our case, we are requesting just the device friendly name + // (WPD_DEVICE_FRIENDLY_NAME) + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**)&pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection for results"); + } + + // Set the params + if (SUCCEEDED(hr)) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_OBJECT_PROPERTIES_GET.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_OBJECT_PROPERTIES_GET.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, WPD_DEVICE_OBJECT_ID); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID")); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_DEVICE_FRIENDLY_NAME); + CHECK_HR(hr, ("Failed to add WPD_DEVICE_FRIENDLY_NAME to key collection")); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS")); + } + + // Make the call + if (SUCCEEDED(hr)) + { + hr = m_pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to dispatch message to get supported content types")); + } + + // Get the results + if (SUCCEEDED(hr)) + { + hr = pResults->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); + CHECK_HR(hr, ("Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + } + + if (SUCCEEDED(hr)) + { + hr = pValues->GetStringValue(WPD_DEVICE_FRIENDLY_NAME, pwszDeviceFriendlyName); + CHECK_HR(hr, ("Failed to get WPD_DEVICE_FRIENDLY_NAME")); + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/Device.h b/wpd/WpdServiceSampleDriver/Device.h new file mode 100644 index 00000000..cf7fdc03 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Device.h @@ -0,0 +1,91 @@ +#pragma once + +#include "resource.h" +#include "WpdServiceSampleDriver.h" + +class ATL_NO_VTABLE CDevice : + public CComObjectRootEx<CComMultiThreadModel>, + public IPnpCallback, + public IPnpCallbackSelfManagedIo, + public IPnpCallbackHardware +{ +public: + CDevice() : + m_pWpdBaseDriver(NULL) + { + } + + DECLARE_NOT_AGGREGATABLE(CDevice) + + BEGIN_COM_MAP(CDevice) + COM_INTERFACE_ENTRY(IPnpCallback) + COM_INTERFACE_ENTRY(IPnpCallbackSelfManagedIo) + COM_INTERFACE_ENTRY(IPnpCallbackHardware) + END_COM_MAP() + +public: + static HRESULT + CreateInstance( + _In_ IWDFDeviceInitialize* pDeviceInit, + _In_ WpdBaseDriver* pWpdBaseDriver, + _COM_Outptr_ IUnknown** ppUnkwn) + { + *ppUnkwn = NULL; + + // + // Set device properties. + // + pDeviceInit->SetLockingConstraint(None); + + CComObject< CDevice> *pMyDevice = NULL; + HRESULT hr = CComObject<CDevice>::CreateInstance( &pMyDevice ); + if( SUCCEEDED (hr) ) + { + pMyDevice->AddRef(); + hr = pMyDevice->QueryInterface( __uuidof(IUnknown),(void **) ppUnkwn); + if (hr == S_OK) + { + pMyDevice->m_pWpdBaseDriver = pWpdBaseDriver; + } + pMyDevice->Release(); + pMyDevice = NULL; + } + + return hr; + } + + // IPnpCallback + // + STDMETHOD_(HRESULT, OnD0Entry) (_In_ IWDFDevice* pDevice, WDF_POWER_DEVICE_STATE previousState); + STDMETHOD_(HRESULT, OnD0Exit) (_In_ IWDFDevice* pDevice, WDF_POWER_DEVICE_STATE newState); + STDMETHOD_(VOID, OnSurpriseRemoval)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnQueryRemove) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnQueryStop) (_In_ IWDFDevice* pDevice); + + // IPnpCallbackSelfManagedIo + // + STDMETHOD_(VOID, OnSelfManagedIoCleanup)(_In_ IWDFDevice* pDevice); + STDMETHOD_(VOID, OnSelfManagedIoFlush) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoInit) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoSuspend) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoRestart) (_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnSelfManagedIoStop) (_In_ IWDFDevice* pDevice); + + // IPnpCallbackHardware + // + STDMETHOD_(HRESULT, OnPrepareHardware)(_In_ IWDFDevice* pDevice); + STDMETHOD_(HRESULT, OnReleaseHardware)(_In_ IWDFDevice* pDevice); + +private: + HRESULT GetSupportedContentTypes( + _Outptr_ IPortableDevicePropVariantCollection** ppContentTypes); + + HRESULT GetDeviceFriendlyName( + _Outptr_ LPWSTR* pwszDeviceFriendlyName); + +private: + + WpdBaseDriver* m_pWpdBaseDriver; + CComPtr<IPortableDeviceClassExtension> m_pPortableDeviceClassExtension; +}; + diff --git a/wpd/WpdServiceSampleDriver/Driver.cpp b/wpd/WpdServiceSampleDriver/Driver.cpp new file mode 100644 index 00000000..c6f90c53 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Driver.cpp @@ -0,0 +1,200 @@ +#include "stdafx.h" + +#include "Driver.h" +#include "Device.h" +#include "Queue.h" + +#include "Driver.tmh" + +CDriver::CDriver() +{ + + +} + +HRESULT +CDriver::OnDeviceAdd( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit + ) +/*++ + +Routine Description: + + The framework calls this function when a device is being added to + the driver stack. + +Arguments: + + IWDFDriver - Framework interface. The driver uses this + interface to create device objects. + IWDFDeviceInitialize - Framework interface. The driver uses this + interface to set device parameters before + creating the device obeject. + +Return Value: + + HRESULT S_OK - Device added successfully + +--*/ +{ + HRESULT hr = S_OK; + CComPtr<IUnknown> pDeviceCallback; + + if (hr == S_OK) + { + WpdBaseDriver *pWpdBaseDriver = NULL; + + // + // Create the WPD driver object that handles all WPD messages for this device + // + pWpdBaseDriver = new WpdBaseDriver(); + if(pWpdBaseDriver == NULL) + { + hr = E_OUTOFMEMORY; + } + + if(SUCCEEDED(hr)) + { + // + // Create device callback object + // + hr = CDevice::CreateInstance(pDeviceInit, pWpdBaseDriver, &pDeviceCallback); + } + + // + // This driver has no special power management requirements and so + // we set power policy ownership to UMDF to indicate that UMDF should + // handle powermanagement for us. + // + pDeviceInit->SetPowerPolicyOwnership(FALSE); + + // + // Create WDFDevice. + // + CComPtr<IWDFDevice> pIWDFDevice; + if(SUCCEEDED(hr)) + { + hr = pDriver->CreateDevice( + pDeviceInit, + pDeviceCallback, + &pIWDFDevice); + } + + // + // Assign pWpdBaseDriver to the device object. Each UMDF device requires its own instance of + // a WpdBaseDriver to handle WPD messages. + // + if(SUCCEEDED(hr)) + { + hr = pIWDFDevice->AssignContext(this, (void*)pWpdBaseDriver); + if(SUCCEEDED(hr)) + { + // AddRef the WpdBaseDriver object since it is not stored with the + // device context. + pWpdBaseDriver->AddRef(); + } + } + + // + // Create queue callback object + // + CComPtr<IUnknown> pIUnknown; + if(S_OK == hr) + { + hr = CQueue::CreateInstance(&pIUnknown); + } + + // + // Configure the default queue. + // + if(S_OK == hr) + { + CComPtr<IWDFIoQueue> pDefaultQueue; + hr = pIWDFDevice->CreateIoQueue( + pIUnknown, + TRUE, // bDefaultQueue + WdfIoQueueDispatchSequential, + TRUE, // bPowerManaged + FALSE, // bAllowZeroLengthRequests + &pDefaultQueue); + } + + pDeviceCallback = NULL; + pIWDFDevice = NULL; + + // + // It is fine to release the interface on the callback object. + // The framework has its own refcount on this object and will + // provide an interface when calling into the driver. + // + pIUnknown = NULL; + + // Release the WpdBaseDriver object. If it was successfully added to the device context, + // it was already addref'd above. Releasing it here ensures it will be destroyed if + // an error occured and it could not be added to the device context. + SAFE_RELEASE(pWpdBaseDriver); + } + + return hr; +} + +void +CDriver::OnDeinitialize( + _In_ IWDFDriver* pDriver + ) +/*++ + +Routine Description: + + The framework calls this function just before de-initializing itself. All + WDF framework resources should be released by driver before returning from this call. + +Arguments: + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER(pDriver); + return; +} + +HRESULT +CDriver::OnInitialize( + _In_ IWDFDriver* pDriver + ) +/*++ + +Routine Description: + + The framework calls this function just after loading the driver. The driver can + perform any global, device independent intialization in this routine. + +Arguments: + +Return Value: + +--*/ +{ + UNREFERENCED_PARAMETER(pDriver); + return S_OK; +} + +STDMETHODIMP_ (void) +CDriver::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +{ + // Release the base driver object + HRESULT hr = S_OK; + WpdBaseDriver* pWpdBaseDriver = NULL; + + hr = pWdfObject->RetrieveContext((void**)&pWpdBaseDriver); + if((hr == S_OK) && (pWpdBaseDriver != NULL)) + { + pWpdBaseDriver->Release(); + pWpdBaseDriver = NULL; + } +} + diff --git a/wpd/WpdServiceSampleDriver/Driver.h b/wpd/WpdServiceSampleDriver/Driver.h new file mode 100644 index 00000000..7f826154 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Driver.h @@ -0,0 +1,47 @@ +#pragma once + +#include "resource.h" +#include "WpdServiceSampleDriver.h" + +class ATL_NO_VTABLE CDriver : + public CComObjectRootEx<CComMultiThreadModel>, + public CComCoClass<CDriver, &CLSID_WpdServiceSampleDriver>, + public IDriverEntry, + public IObjectCleanup +{ +public: + CDriver(); + + DECLARE_REGISTRY_RESOURCEID(IDR_WpdServiceSampleDriver) + + DECLARE_NOT_AGGREGATABLE(CDriver) + + BEGIN_COM_MAP(CDriver) + COM_INTERFACE_ENTRY(IDriverEntry) + END_COM_MAP() + +public: + // + // IDriverEntry + // + STDMETHOD (OnInitialize)( + _In_ IWDFDriver* pDriver + ); + STDMETHOD (OnDeviceAdd)( + _In_ IWDFDriver* pDriver, + _In_ IWDFDeviceInitialize* pDeviceInit + ); + STDMETHOD_ (void, OnDeinitialize)( + _In_ IWDFDriver* pDriver + ); + + // + // IObjectCleanup + // + STDMETHOD_ (void, OnCleanup)( + _In_ IWDFObject* pWdfObject + ); +}; + +OBJECT_ENTRY_AUTO(__uuidof(WpdServiceSampleDriver), CDriver) + diff --git a/wpd/WpdServiceSampleDriver/FakeContactContent.cpp b/wpd/WpdServiceSampleDriver/FakeContactContent.cpp new file mode 100644 index 00000000..f5807756 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactContent.cpp @@ -0,0 +1,268 @@ +#include "stdafx.h" + +#include "FakeContactContent.tmh" + +// Properties supported by a contact +const PropertyAttributeInfo g_SupportedContactProperties[] = +{ + // Standard WPD properties. + {&WPD_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_GenericObj_ObjectID}, + {&WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_GenericObj_PersistentUID}, + {&WPD_OBJECT_PARENT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_GenericObj_ParentID}, + {&WPD_OBJECT_NAME, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_GenericObj_Name}, + {&WPD_OBJECT_FORMAT, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_GenericObj_ObjectFormat}, + {&WPD_OBJECT_CONTENT_TYPE, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, L"ObjectContentType"}, + {&WPD_OBJECT_CAN_DELETE, VT_BOOL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, L"ObjectCanDelete"}, + {&WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, L"StorageID"}, + + // Contact Service extension properties + {&PKEY_ContactObj_GivenName, VT_LPWSTR, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NAME_ContactObj_GivenName}, + {&PKEY_ContactObj_FamilyName, VT_LPWSTR, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NAME_ContactObj_FamilyName}, + + // Custom property used to store the version of this object + {&MyContactVersionIdentifier, VT_UI4, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, L"ContactVersionIdentifier"}, +}; + +HRESULT GetSupportedContactProperties( + _In_ IPortableDeviceKeyCollection *pKeys) +{ + HRESULT hr = S_OK; + + if(pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedContactProperties); dwIndex++) + { + hr = pKeys->Add(*g_SupportedContactProperties[dwIndex].pKey); + CHECK_HR(hr, "Failed to add custom contacts property"); + } + + return hr; +} + +HRESULT GetContactPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = SetPropertyAttributes(Key, &g_SupportedContactProperties[0], ARRAYSIZE(g_SupportedContactProperties), pAttributes); + + return hr; +} + + +// For this object, the supported properties are the same as the supported +// format properties. +// This is where customization for supported properties per object can happen +HRESULT FakeContactContent::GetSupportedProperties( + _In_ IPortableDeviceKeyCollection *pKeys) +{ + return GetSupportedContactProperties(pKeys); +} + +HRESULT FakeContactContent::GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + // Add WPD_OBJECT_ID + pvValue = ObjectID; + hr = pStore->SetValue(WPD_OBJECT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + // Add WPD_OBJECT_PERSISTENT_UNIQUE_ID + pvValue = this->PersistentUniqueID; + hr = pStore->SetValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + // Add WPD_OBJECT_PARENT_ID + pvValue = ParentID; + hr = pStore->SetValue(WPD_OBJECT_PARENT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PARENT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + // Add WPD_OBJECT_NAME + pvValue = Name; + hr = pStore->SetValue(WPD_OBJECT_NAME, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_NAME")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + // Add WPD_OBJECT_CONTENT_TYPE + hr = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CONTENT_TYPE")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + // Add WPD_OBJECT_FORMAT + hr = pStore->SetGuidValue(WPD_OBJECT_FORMAT, Format); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_FORMAT")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + // Add WPD_OBJECT_CAN_DELETE + hr = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CAN_DELETE")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID)) + { + // Add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID + hr = pStore->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, ContainerFunctionalObjectID); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID")); + } + else if (IsEqualPropertyKey(Key, PKEY_ContactObj_GivenName)) + { + // Add PKEY_ContactObj_GivenName + pvValue = GivenName; + hr = pStore->SetValue(PKEY_ContactObj_GivenName, &pvValue); + CHECK_HR(hr, ("Failed to set PKEY_ContactObj_GivenName")); + } + else if (IsEqualPropertyKey(Key, PKEY_ContactObj_FamilyName)) + { + // Add PKEY_ContactObj_FamilyName + pvValue = FamilyName; + hr = pStore->SetValue(PKEY_ContactObj_FamilyName, &pvValue); + CHECK_HR(hr, ("Failed to set PKEY_ContactObj_FamilyName")); + } + else if (IsEqualPropertyKey(Key, MyContactVersionIdentifier)) + { + // Add MyContactVersionIdentifier + pvValue = VersionIdentifier; + hr = pStore->SetValue(MyContactVersionIdentifier, &pvValue); + CHECK_HR(hr, ("Failed to set MyContactVersionIdentifier")); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Property {%ws}.%d is not supported", CComBSTR(Key.fmtid), Key.pid); + } + + return hr; +} + + +HRESULT FakeContactContent::WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value) +{ + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(Key, PKEY_ContactObj_FamilyName)) + { + if(Value.vt == VT_LPWSTR) + { + FamilyName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set PKEY_ContactObj_FamilyName because type was not VT_LPWSTR"); + } + } + else if(IsEqualPropertyKey(Key, PKEY_ContactObj_GivenName)) + { + if(Value.vt == VT_LPWSTR) + { + GivenName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set PKEY_ContactObj_GivenName because type was not VT_LPWSTR"); + } + } + else + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(Key.fmtid), Key.pid, ObjectID); + } + + return hr; +} + +HRESULT FakeContactContent::GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + hr = GetContactPropertyAttributes(Key, pAttributes); + CHECK_HR(hr, "Failed to add property attributes for %ws.%d", CComBSTR(Key.fmtid), Key.pid); + + // Some of our properties have extra attributes on top of the ones that are common to all + if(IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + CAtlStringW strDefaultName; + + strDefaultName.Format(L"%ws%ws", L"Name", ObjectID.GetString()); + + hr = pAttributes->SetStringValue(WPD_PROPERTY_ATTRIBUTE_DEFAULT_VALUE, strDefaultName.GetString());; + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_DEFAULT_VALUE"); + } + + return hr; +} + +HRESULT FakeContactContent::WriteValues( + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _Out_ bool* pbObjectChanged) +{ + HRESULT hr = FakeContent::WriteValues(pValues, pResults, pbObjectChanged); + + if (SUCCEEDED(hr) && (*pbObjectChanged == true)) + { + UpdateVersion(); + } + + return hr; +} + +void FakeContactContent::UpdateVersion() +{ + if (VersionIdentifier < ULONG_MAX) + { + VersionIdentifier++; + } + else + { + VersionIdentifier = 0; + } +} diff --git a/wpd/WpdServiceSampleDriver/FakeContactContent.h b/wpd/WpdServiceSampleDriver/FakeContactContent.h new file mode 100644 index 00000000..1b2d8dfb --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactContent.h @@ -0,0 +1,77 @@ +#pragma once + +/** + * This class represents an abstraction of a contact content object + * Driver implementors should replace this with their own + * device I/O classes/libraries. + */ + +class FakeContactContent : public FakeContent +{ +public: + FakeContactContent() + { + Format = FORMAT_AbstractContact; + ContentType = WPD_CONTENT_TYPE_CONTACT; + RequiredScope = CONTACTS_SERVICE_ACCESS; + CanDelete = true; + VersionIdentifier = 0; + } + + FakeContactContent(const FakeContactContent& src) + { + *this = src; + } + + ~FakeContactContent() + { + } + + FakeContactContent& operator= (const FakeContactContent& src) + { + FamilyName = src.FamilyName; + GivenName = src.GivenName; + VersionIdentifier = src.VersionIdentifier; + + return *this; + } + + HRESULT GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore); + + HRESULT WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value); + + HRESULT GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetSupportedProperties( + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT WriteValues( + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _Out_ bool* pbObjectChanged); + +private: + void UpdateVersion(); + +public: + // Custom properties defined by the contacts service + CAtlStringW FamilyName; + CAtlStringW GivenName; + +private: + // Indicates whether the object has been updated + DWORD VersionIdentifier; +}; + +HRESULT GetSupportedContactProperties( + _In_ IPortableDeviceKeyCollection* pKeys); + +HRESULT GetContactPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); diff --git a/wpd/WpdServiceSampleDriver/FakeContactsService.cpp b/wpd/WpdServiceSampleDriver/FakeContactsService.cpp new file mode 100644 index 00000000..405279a9 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactsService.cpp @@ -0,0 +1,753 @@ +#include "stdafx.h" + +#include "FakeContactsService.tmh" + +const FormatAttributeInfo g_SupportedContactFormats[] = +{ + {&FORMAT_AbstractContact, L"AbstractContact"}, + {&FORMAT_VCard2Contact, L"VCard2"} +}; + +const GUID* g_SupportedMethods[] = +{ + &METHOD_FullEnumSyncSvc_BeginSync, + &METHOD_FullEnumSyncSvc_EndSync, + &MyCustomMethod +}; + +// Method parameters +const MethodParameterAttributeInfo g_MethodParameters[] = +{ + {&MyCustomMethodResult, VT_BOOL, WPD_PARAMETER_USAGE_RETURN, WPD_PARAMETER_ATTRIBUTE_FORM_UNSPECIFIED, 0, L"Result"}, + {&MyCustomMethodParam, VT_UI4, WPD_PARAMETER_USAGE_IN, WPD_PARAMETER_ATTRIBUTE_FORM_UNSPECIFIED, 1, L"Integer_Param"}, + {&MyCustomMethodParamInOut, VT_LPWSTR, WPD_PARAMETER_USAGE_INOUT, WPD_PARAMETER_ATTRIBUTE_FORM_OBJECT_IDENTIFIER, 2, L"ObjectId_Param"}, +}; + +const GUID* g_SupportedServiceEvents[] = +{ + &WPD_EVENT_OBJECT_ADDED, + &WPD_EVENT_OBJECT_REMOVED, + &WPD_EVENT_OBJECT_UPDATED, + &MyCustomEvent, +}; + +// Event parameters +const EventParameterAttributeInfo g_ServiceEventParameters[] = +{ + {&WPD_EVENT_OBJECT_ADDED, &WPD_EVENT_PARAMETER_EVENT_ID, VT_CLSID}, + {&WPD_EVENT_OBJECT_ADDED, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_ADDED, &WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_ADDED, &WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR}, + + {&WPD_EVENT_OBJECT_REMOVED, &WPD_EVENT_PARAMETER_EVENT_ID, VT_CLSID}, + {&WPD_EVENT_OBJECT_REMOVED, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_REMOVED, &WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_REMOVED, &WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR}, + + {&WPD_EVENT_OBJECT_UPDATED, &WPD_EVENT_PARAMETER_EVENT_ID, VT_CLSID}, + {&WPD_EVENT_OBJECT_UPDATED, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_UPDATED, &WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, VT_LPWSTR}, + {&WPD_EVENT_OBJECT_UPDATED, &WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR}, + + {&MyCustomEvent, &WPD_EVENT_PARAMETER_EVENT_ID, VT_CLSID}, + {&MyCustomEvent, &WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR}, + {&MyCustomEvent, &MyCustomEventParam0, VT_BOOL}, + {&MyCustomEvent, &MyCustomEventParam1, VT_UI4}, +}; + +// Supported commands for this service +const PROPERTYKEY* g_ServiceSupportedCommands[] = +{ + // WPD_CATEGORY_OBJECT_ENUMERATION + &WPD_COMMAND_OBJECT_ENUMERATION_START_FIND, + &WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT, + &WPD_COMMAND_OBJECT_ENUMERATION_END_FIND, + + // WPD_CATEGORY_OBJECT_PROPERTIES + &WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED, + &WPD_COMMAND_OBJECT_PROPERTIES_GET, + &WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL, + &WPD_COMMAND_OBJECT_PROPERTIES_SET, + &WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES, + &WPD_COMMAND_OBJECT_PROPERTIES_DELETE, + + // WPD_CATEGORY_OBJECT_RESOURCES + &WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED, + &WPD_COMMAND_OBJECT_RESOURCES_OPEN, + &WPD_COMMAND_OBJECT_RESOURCES_READ, + &WPD_COMMAND_OBJECT_RESOURCES_CLOSE, + &WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES, + + // WPD_CATEGORY_OBJECT_MANAGEMENT + &WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY, + &WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS, + + // WPD_CATEGORY_OBJECT_PROPERTIES_BULK + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_END, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_END, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END, + + // WPD_CATEGORY_COMMON + &WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS, + + // WPD_CATEGORY_SERVICE_COMMON + &WPD_COMMAND_SERVICE_COMMON_GET_SERVICE_OBJECT_ID, + + // WPD_CATEGORY_SERVICE_CAPABILITIES + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_COMMANDS, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_COMMAND_OPTIONS, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS_BY_FORMAT, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_PARAMETER_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMATS, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_PROPERTY_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_EVENTS, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_PARAMETER_ATTRIBUTES, + &WPD_COMMAND_SERVICE_CAPABILITIES_GET_INHERITED_SERVICES +}; + + +HRESULT FakeContactsService::GetSupportedCommands( + _In_ IPortableDeviceKeyCollection* pCommands) +{ + HRESULT hr = S_OK; + + if(pCommands == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_ServiceSupportedCommands); dwIndex++) + { + PROPERTYKEY key = *(g_ServiceSupportedCommands[dwIndex]); + hr = pCommands->Add(key); + CHECK_HR(hr, "Failed to add supported command at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + return hr; +} + +HRESULT FakeContactsService::GetCommandOptions( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pOptions) +{ + HRESULT hr = S_OK; + + if(pOptions == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Check for command options + if (IsEqualPropertyKey(WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS, Command)) + { + // This driver does not support recursive deletion + hr = pOptions->SetBoolValue(WPD_OPTION_OBJECT_MANAGEMENT_RECURSIVE_DELETE_SUPPORTED, TRUE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_MANAGEMENT_RECURSIVE_DELETE_SUPPORTED"); + } + + return hr; +} + + +HRESULT FakeContactsService::GetSupportedMethods( + _In_ IPortableDevicePropVariantCollection* pMethods) +{ + HRESULT hr = S_OK; + + if (pMethods == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add the supported methods to the collection. + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedMethods); dwIndex++) + { + PROPVARIANT pv; + pv.vt = VT_CLSID; + pv.puuid = (GUID*)g_SupportedMethods[dwIndex]; // Assignment only, don't PropVariantClear this + + hr = pMethods->Add(&pv); + CHECK_HR(hr, "Failed to add supported method at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + + return hr; +} + +BOOL FakeContactsService::IsMethodSupported( + _In_ REFGUID Method) +{ + // Add the supported methods to the collection. + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedMethods); dwIndex++) + { + if (Method == *g_SupportedMethods[dwIndex]) + { + return TRUE; + } + } + + return FALSE; +} + +HRESULT FakeContactsService::GetSupportedMethodsByFormat( + _In_ REFGUID Format, + _In_ IPortableDevicePropVariantCollection* pMethods) +{ + HRESULT hr = S_OK; + + if (pMethods == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + for (DWORD i=0; i<ARRAYSIZE(g_SupportedContactFormats); i++) + { + if (Format == *g_SupportedContactFormats[i].pFormatGuid) + { + // Add the supported methods for the format to the collection, right now there are none, so we + // return an emtpy collection + hr = S_OK; + break; + } + } + CHECK_HR(hr, "Format %ws is not supported", CComBSTR(Format)); + + return hr; +} + + +HRESULT FakeContactsService::GetMethodAttributes( + _In_ REFGUID Method, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pParameters; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // CoCreate a collection for specifying the method parameters. + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pParameters); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + + // Add the method attributes to this collection + if (Method == METHOD_FullEnumSyncSvc_BeginSync) + { + if (hr == S_OK) + { + hr = pAttributes->SetStringValue(WPD_METHOD_ATTRIBUTE_NAME, L"BeginSync"); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_NAME"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_METHOD_ATTRIBUTE_ACCESS, WPD_COMMAND_ACCESS_READWRITE); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ACCESS"); + } + + if (hr == S_OK) + { + // no parameters, set empty collection + hr = pAttributes->SetIPortableDeviceKeyCollectionValue(WPD_METHOD_ATTRIBUTE_PARAMETERS, pParameters); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_PARAMETERS"); + } + + if (hr == S_OK) + { + // no associated format, set GUID_NULL + hr = pAttributes->SetGuidValue(WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT, GUID_NULL); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT"); + } + + } + else if (Method == METHOD_FullEnumSyncSvc_EndSync) + { + if (hr == S_OK) + { + hr = pAttributes->SetStringValue(WPD_METHOD_ATTRIBUTE_NAME, L"EndSync"); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_NAME"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_METHOD_ATTRIBUTE_ACCESS, WPD_COMMAND_ACCESS_READWRITE); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ACCESS"); + } + + if (hr == S_OK) + { + // no parameters, set empty collection + hr = pAttributes->SetIPortableDeviceKeyCollectionValue(WPD_METHOD_ATTRIBUTE_PARAMETERS, pParameters); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_PARAMETERS"); + } + + if (hr == S_OK) + { + // no associated format, set GUID_NULL + hr = pAttributes->SetGuidValue(WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT, GUID_NULL); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT"); + } + } + else if (Method == MyCustomMethod) + { + if (hr == S_OK) + { + hr = pAttributes->SetStringValue(WPD_METHOD_ATTRIBUTE_NAME, L"CustomMethod"); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_NAME"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_METHOD_ATTRIBUTE_ACCESS, WPD_COMMAND_ACCESS_READWRITE); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ACCESS"); + } + + if (hr == S_OK) + { + // Set the supported parameters + for (size_t i=0; i<ARRAYSIZE(g_MethodParameters); i++) + { + pParameters->Add(*(g_MethodParameters[i].pKey)); + } + + hr = pAttributes->SetIPortableDeviceKeyCollectionValue(WPD_METHOD_ATTRIBUTE_PARAMETERS, pParameters); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_PARAMETERS"); + } + + if (hr == S_OK) + { + // no associated format, set GUID_NULL + hr = pAttributes->SetGuidValue(WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT, GUID_NULL); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_ASSOCIATED_FORMAT"); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method)); + } + + return hr; +} + +HRESULT FakeContactsService::GetMethodParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = SetMethodParameterAttributes(Parameter, &g_MethodParameters[0], ARRAYSIZE(g_MethodParameters), pAttributes); + CHECK_HR(hr, "Failed to set method parameter attributes"); + + return hr; +} + +HRESULT FakeContactsService::GetSupportedFormats( + _In_ IPortableDevicePropVariantCollection* pFormats) +{ + HRESULT hr = S_OK; + + if (pFormats == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (hr == S_OK) + { + // Add the supported formats to this collection + for (DWORD i=0; i<ARRAYSIZE(g_SupportedContactFormats); i++) + { + PROPVARIANT pv = {0}; + pv.vt = VT_CLSID; + pv.puuid = (CLSID*)g_SupportedContactFormats[i].pFormatGuid; // assignment, do not call PropVariantClear + + hr = pFormats->Add(&pv); + CHECK_HR(hr, "Failed to add format to IPortableDevicePropVariantCollection"); + } + } + + return hr; +} + +HRESULT FakeContactsService::GetFormatAttributes( + _In_ REFGUID Format, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + // Add the supported formats to this collection + for (DWORD i=0; i<ARRAYSIZE(g_SupportedContactFormats); i++) + { + if (Format == *g_SupportedContactFormats[i].pFormatGuid) + { + hr = pAttributes->SetStringValue(WPD_FORMAT_ATTRIBUTE_NAME, g_SupportedContactFormats[i].wszName); + CHECK_HR(hr, "Failed to set WPD_FORMAT_ATTRIBUTE_NAME"); + break; + } + } + + if (hr == HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)) + { + CHECK_HR(hr, "Unknown format %ws received",CComBSTR(Format)); + } + + return hr; +} + +HRESULT FakeContactsService::GetSupportedFormatProperties( + _In_ REFGUID Format, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + + if (pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add the supported format properties to this collection + // The formats of this service happen to support the same set of properties + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + for (DWORD i=0; i<ARRAYSIZE(g_SupportedContactFormats); i++) + { + if (Format == (*g_SupportedContactFormats[i].pFormatGuid)) + { + hr = GetSupportedContactProperties(pKeys); + CHECK_HR(hr, "Failed to add supported contact format properties"); + break; + } + } + CHECK_HR(hr, "Format %ws is not supported", CComBSTR(Format)); + + return hr; +} + +HRESULT FakeContactsService::GetPropertyAttributes( + _In_ REFGUID Format, + _In_ REFPROPERTYKEY Property, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + for (DWORD i=0; i<ARRAYSIZE(g_SupportedContactFormats); i++) + { + if (Format == (*g_SupportedContactFormats[i].pFormatGuid)) + { + hr = GetContactPropertyAttributes(Property, pAttributes); + CHECK_HR(hr, "Failed to get property attributes"); + break; + } + } + + CHECK_HR(hr, "Failed to find supported format to retrieve property attributes"); + + return hr; +} + +HRESULT FakeContactsService::GetSupportedEvents( + _In_ IPortableDevicePropVariantCollection* pEvents) +{ + HRESULT hr = S_OK; + + if (pEvents == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + PROPVARIANT pv; + pv.vt = VT_CLSID; + + for (DWORD i=0; i<ARRAYSIZE(g_SupportedServiceEvents); i++) + { + pv.puuid = (CLSID*)g_SupportedServiceEvents[i]; // Assignment, don't PropVariantClear this + + hr = pEvents->Add(&pv); + CHECK_HR(hr, "Failed to add event to the collection"); + } + + return hr; +} + +HRESULT FakeContactsService::GetEventAttributes( + _In_ REFGUID Event, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pEventOptions; + CComPtr<IPortableDeviceKeyCollection> pEventParameters; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // CoCreate a collection to store the event options. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pEventOptions); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + if (hr == S_OK) + { + hr = pEventOptions->SetBoolValue(WPD_EVENT_OPTION_IS_BROADCAST_EVENT, TRUE); + CHECK_HR(hr, "Failed to set WPD_EVENT_OPTION_IS_BROADCAST_EVENT"); + } + + // Loop through the supported events for this service to find a match + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + for (DWORD i=0; i<ARRAYSIZE(g_SupportedServiceEvents); i++) + { + if (Event == *g_SupportedServiceEvents[i]) + { + // Set the event options. + hr = pAttributes->SetIPortableDeviceValuesValue(WPD_EVENT_ATTRIBUTE_OPTIONS, pEventOptions); + CHECK_HR(hr, "Failed to set WPD_EVENT_ATTRIBUTE_OPTIONS"); + + // Set the event parameters. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pEventParameters); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = SetEventParameters(Event, &g_ServiceEventParameters[0], ARRAYSIZE(g_ServiceEventParameters), pEventParameters); + CHECK_HR(hr, "Failed to set event parameters"); + + if (hr == S_OK) + { + hr = pAttributes->SetIPortableDeviceKeyCollectionValue(WPD_EVENT_ATTRIBUTE_PARAMETERS, pEventParameters); + CHECK_HR(hr, "Failed to set WPD_METHOD_ATTRIBUTE_PARAMETERS"); + } + } + + // Set a name for the custom event + if (hr == S_OK) + { + if (Event == MyCustomEvent) + { + hr = pAttributes->SetStringValue(WPD_EVENT_ATTRIBUTE_NAME , L"MyCustomEvent"); + CHECK_HR(hr, "Failed to set WPD_EVENT_ATTRIBUTE_NAME"); + } + } + break; + } + } + + return hr; +} + +HRESULT FakeContactsService::GetEventParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = SetEventParameterAttributes(Parameter, &g_ServiceEventParameters[0], ARRAYSIZE(g_ServiceEventParameters), pAttributes); + CHECK_HR(hr, "Failed to set event parameter attributes"); + + return hr; +} + +HRESULT FakeContactsService::GetInheritedServices( + const DWORD dwInheritanceType, + _In_ IPortableDevicePropVariantCollection* pServices) +{ + HRESULT hr = S_OK; + + if (pServices == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (dwInheritanceType == WPD_SERVICE_INHERITANCE_IMPLEMENTATION) + { + PROPVARIANT pv; + pv.vt = VT_CLSID; + pv.puuid = (CLSID*)&SERVICE_FullEnumSync; // Assignment, don't PropVariantClear this + + hr = pServices->Add(&pv); + CHECK_HR(hr, "Failed to add service GUID to the collection"); + } + + return hr; +} + +HRESULT FakeContactsService::OnBeginSync( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + UNREFERENCED_PARAMETER(pResults); + + // This is where the sync service receives a notification from the application that + // sync is about to begin so that it can lock the session + // This method does not do anything right now + + return S_OK; +} + +HRESULT FakeContactsService::OnEndSync( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + UNREFERENCED_PARAMETER(pResults); + + // This is where the sync service receives a notification from the application that + // sync is about to end so that it can unlock the session + // This method does not do anything right now + + return S_OK; +} + +// This demonstrates how a custom service method can be implemented +HRESULT FakeContactsService::OnMyCustomMethod( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults, + _In_ IPortableDeviceValues* pEventParams) +{ + HRESULT hr = S_OK; + DWORD dwParamValue = 0; + BOOL bResultValue = FALSE; + LPWSTR pszParamValue = NULL; + + if (pParams == NULL || pResults == NULL || pEventParams == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = pParams->GetUnsignedIntegerValue(MyCustomMethodParam, &dwParamValue); + CHECK_HR(hr, "Failed to get MyCustomMethodParam"); + + if (hr == S_OK) + { + hr = pParams->GetStringValue(MyCustomMethodParamInOut, &pszParamValue); + CHECK_HR(hr, "Failed to get MyCustomMethodParamInOut"); + } + + if (hr == S_OK) + { + // For demonstration purposes only, we simply return the inout parameter as is + hr = pResults->SetStringValue(MyCustomMethodParamInOut, pszParamValue); + CHECK_HR(hr, "Failed to set MyCustomMethodParamInOut"); + } + + if (hr == S_OK) + { + // This is where the device will process the method invocation + // For demonstration purposes only, we return TRUE if the input is an even number + bResultValue = (dwParamValue % 1 == 0)?TRUE:FALSE; + } + + if (hr == S_OK) + { + hr = pResults->SetBoolValue(MyCustomMethodResult, bResultValue); + CHECK_HR(hr, "Failed to set MyCustomMethodResult"); + } + + if (hr == S_OK) + { + hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, MyCustomEvent); + CHECK_HR(hr, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + if (hr == S_OK) + { + // Adding this event parameter will allow WPD to scope this event to the container functional object + hr = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, RequestFilename); + CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID"); + } + + if (hr == S_OK) + { + // set the first custom event parameter + hr = pEventParams->SetBoolValue(MyCustomEventParam0, bResultValue); + CHECK_HR(hr, "Failed to add MyCustomEvent parameter 0"); + } + + if (hr == S_OK) + { + // set the second custom event parameter + hr = pEventParams->SetUnsignedIntegerValue(MyCustomEventParam1, dwParamValue); + CHECK_HR(hr, "Failed to add MyCustomEvent parameter 1"); + } + } + + CoTaskMemFree(pszParamValue); + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/FakeContactsService.h b/wpd/WpdServiceSampleDriver/FakeContactsService.h new file mode 100644 index 00000000..23522c07 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactsService.h @@ -0,0 +1,101 @@ +#pragma once + +/** + * This class represents an abstraction of a contacts service that implements + * the full enumeration sync model. + * Driver implementors should replace this with their own + * device I/O classes/libraries. + */ + +class FakeContactsService +{ +public: + FakeContactsService() : RequestFilename(CONTACTS_SERVICE_OBJECT_ID) + { + } + + ~FakeContactsService() + { + } + + // Capabilities + HRESULT GetSupportedCommands( + _In_ IPortableDeviceKeyCollection* pCommands); + + HRESULT GetCommandOptions( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pOptions); + + HRESULT GetSupportedMethods( + _In_ IPortableDevicePropVariantCollection* pMethods); + + BOOL IsMethodSupported( + _In_ REFGUID Method); + + HRESULT GetSupportedMethodsByFormat( + _In_ REFGUID Format, + _In_ IPortableDevicePropVariantCollection* pMethods); + + HRESULT GetMethodAttributes( + _In_ REFGUID Method, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetMethodParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetSupportedFormats( + _In_ IPortableDevicePropVariantCollection* pFormats); + + HRESULT GetFormatAttributes( + _In_ REFGUID Format, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetSupportedFormatProperties( + _In_ REFGUID Format, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT GetPropertyAttributes( + _In_ REFGUID Format, + _In_ REFPROPERTYKEY Property, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetSupportedEvents( + _In_ IPortableDevicePropVariantCollection* pEvents); + + HRESULT GetEventAttributes( + _In_ REFGUID Event, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetEventParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetInheritedServices( + const DWORD dwInheritanceType, + _In_ IPortableDevicePropVariantCollection* pServices); + + // Methods + HRESULT OnBeginSync( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnEndSync( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnMyCustomMethod( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults, + _In_ IPortableDeviceValues* pEventParameters); + + LPCWSTR GetRequestFilename() + { + return RequestFilename.GetString(); + } + +private: + CAtlStringW RequestFilename; +}; + + diff --git a/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.cpp b/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.cpp new file mode 100644 index 00000000..d6135e2d --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.cpp @@ -0,0 +1,555 @@ +#include "stdafx.h" + +#include "FakeContactsServiceContent.tmh" + +// Change unit is a subset of the custom properties supported by a contact object. +// This typically contains at least one read-only property that indicates that the +// object has changed +const PROPERTYKEY* g_ContactsServiceChangeUnit[1] = +{ + &MyContactVersionIdentifier, +}; + +const PropertyAttributeInfo g_SupportedServiceProperties[] = +{ + {&WPD_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PARENT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_NAME, VT_LPWSTR, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_FORMAT, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTENT_TYPE, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CAN_DELETE, VT_BOOL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_FUNCTIONAL_OBJECT_CATEGORY, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_SERVICE_VERSION, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&PKEY_Services_ServiceDisplayName, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_Services_ServiceDisplayName}, + {&PKEY_Services_ServiceIcon, VT_VECTOR | VT_UI1, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_Services_ServiceIcon}, + {&PKEY_FullEnumSyncSvc_SyncFormat, VT_VECTOR | VT_UI1, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_FullEnumSyncSvc_SyncFormat}, + {&PKEY_FullEnumSyncSvc_VersionProps, VT_VECTOR | VT_UI1, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NAME_FullEnumSyncSvc_VersionProps}, + {&PKEY_FullEnumSyncSvc_LocalOnlyDelete, VT_UI1, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NAME_FullEnumSyncSvc_LocalOnlyDelete}, + {&PKEY_FullEnumSyncSvc_FilterType, VT_UI1, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NAME_FullEnumSyncSvc_FilterType}, + {&PKEY_FullEnumSyncSvc_ReplicaID, VT_VECTOR | VT_UI1, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NAME_FullEnumSyncSvc_ReplicaID}, +}; + +HRESULT FakeContactsServiceContent::InitializeContent( + _Inout_ DWORD *pdwLastObjectID) +{ + HRESULT hr = S_OK; + + if (pdwLastObjectID == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add contact objects to the contact service + for(DWORD dwContactIndex = 1; dwContactIndex <= NUM_CONTACT_OBJECTS; dwContactIndex++) + { + (*pdwLastObjectID)++; + + CAutoPtr<FakeContactContent> pContact(new FakeContactContent()); + if (pContact) + { + pContact->ParentID = ObjectID; + pContact->ContainerFunctionalObjectID = ObjectID; + pContact->ParentPersistentUniqueID = PersistentUniqueID; + pContact->RequiredScope = CONTACTS_SERVICE_ACCESS; + pContact->Name.Format(L"Contact%d", *pdwLastObjectID); + pContact->ObjectID.Format(L"%d", *pdwLastObjectID); + pContact->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContact->ObjectID.GetString()); + pContact->GivenName.Format(L"GivenName%d", dwContactIndex); + pContact->FamilyName.Format(L"FamilyName%d", dwContactIndex); + + _ATLTRY + { + m_Children.Add(pContact); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeContactContent"); + } + + if (SUCCEEDED(hr)) + { + pContact.Detach(); + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate contact content at index %d", dwContactIndex); + return hr; + } + } + + return hr; +} + +HRESULT FakeContactsServiceContent::CreatePropertiesOnlyObject( + _In_ IPortableDeviceValues* pObjectProperties, + _Out_ DWORD* pdwLastObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppNewObject) +{ + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + GUID guidContentType = WPD_CONTENT_TYPE_UNSPECIFIED; + GUID guidFormat = WPD_OBJECT_FORMAT_UNSPECIFIED; + + if (pObjectProperties == NULL || pdwLastObjectID == NULL || ppNewObject == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + *pdwLastObjectID = NULL; + *ppNewObject = NULL; + + // Get WPD_OBJECT_FORMAT + if (SUCCEEDED(hr)) + { + hr = pObjectProperties->GetGuidValue(WPD_OBJECT_FORMAT, &guidFormat); + CHECK_HR(hr, "Failed to get WPD_OBJECT_FORMAT"); + } + + if (SUCCEEDED(hr) && (guidFormat != WPD_OBJECT_FORMAT_VCARD2) && (guidFormat != WPD_OBJECT_FORMAT_ABSTRACT_CONTACT)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid Format [%ws]", CComBSTR(guidFormat)); + } + + if (SUCCEEDED(hr)) + { + // Create the object + CAutoPtr<FakeContactContent> pContent(new FakeContactContent()); + if (pContent) + { + (*pdwLastObjectID)++; + pContent->ParentID = ObjectID; + pContent->ParentPersistentUniqueID = PersistentUniqueID; + pContent->Name.Format(L"Contact%d", *pdwLastObjectID); + pContent->ObjectID.Format(L"%d", (*pdwLastObjectID)); + pContent->ContentType = guidContentType; + pContent->Format = guidFormat; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + pContent->ContainerFunctionalObjectID = ObjectID; + pContent->RequiredScope = CONTACTS_SERVICE_ACCESS; + + // Get the other optional contact properties. + LPWSTR pszTempString = NULL; + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &pszTempString); + if(hrTemp == S_OK) + { + pContent->Name = pszTempString; + CoTaskMemFree(pszTempString); + } + + hrTemp = pObjectProperties->GetStringValue(PKEY_ContactObj_FamilyName, &pszTempString); + if(hrTemp == S_OK) + { + pContent->FamilyName = pszTempString; + CoTaskMemFree(pszTempString); + } + + hrTemp = pObjectProperties->GetStringValue(PKEY_ContactObj_GivenName, &pszTempString); + if(hrTemp == S_OK) + { + pContent->GivenName = pszTempString; + CoTaskMemFree(pszTempString); + } + + _ATLTRY + { + m_Children.Add(pContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeContactContent"); + } + + if (SUCCEEDED(hr)) + { + *ppNewObject = pContent.Detach(); + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeContactContent object"); + } + } + return hr; +} + +HRESULT FakeContactsServiceContent::GetSupportedProperties( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + + if (pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex = 0; (dwIndex < ARRAYSIZE(g_SupportedServiceProperties)) && (hr == S_OK); dwIndex++) + { + // Common WPD service properties + hr = pKeys->Add(*g_SupportedServiceProperties[dwIndex].pKey); + CHECK_HR(hr, "Failed to add common service property"); + } + + return hr; +} + +HRESULT FakeContactsServiceContent::GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = SetPropertyAttributes(Key, &g_SupportedServiceProperties[0], ARRAYSIZE(g_SupportedServiceProperties), pAttributes); + + return hr; +} + +HRESULT FakeContactsServiceContent::GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + + PropVariantWrapper pvValue; + + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + // Add WPD_OBJECT_ID + pvValue = ObjectID; + hr = pStore->SetValue(WPD_OBJECT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + // Add WPD_OBJECT_NAME + pvValue = Name; + hr = pStore->SetValue(WPD_OBJECT_NAME, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_NAME")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + // Add WPD_OBJECT_PERSISTENT_UNIQUE_ID + pvValue = PersistentUniqueID; + hr = pStore->SetValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + // Add WPD_OBJECT_PARENT_ID + pvValue = ParentID; + hr = pStore->SetValue(WPD_OBJECT_PARENT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PARENT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + // Add WPD_OBJECT_FORMAT + hr = pStore->SetGuidValue(WPD_OBJECT_FORMAT, Format); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_FORMAT")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + // Add WPD_OBJECT_CONTENT_TYPE + hr = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CONTENT_TYPE")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + // Add WPD_OBJECT_CAN_DELETE + hr = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CAN_DELETE")); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + // Add WPD_FUNCTIONAL_OBJECT_CATEGORY + hr = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, FunctionalCategory); + CHECK_HR(hr, ("Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID)) + { + // Add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID + hr = pStore->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, ContainerFunctionalObjectID); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_SERVICE_VERSION)) + { + // Add WPD_SERVICE_VERSION + hr = pStore->SetStringValue(WPD_SERVICE_VERSION, Version); + CHECK_HR(hr, ("Failed to set WPD_SERVICE_VERSION")); + } + else if (IsEqualPropertyKey(Key, PKEY_Services_ServiceDisplayName)) + { + // Add PKEY_Services_ServiceDisplayName + pvValue = HumanReadableName; + hr = pStore->SetValue(PKEY_Services_ServiceDisplayName, &pvValue); + CHECK_HR(hr, ("Failed to set PKEY_Services_ServiceDisplayName")); + } + else if (IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_SyncFormat)) + { + // Add PKEY_FullEnumSyncSvc_SyncFormat + hr = pStore->SetBufferValue(PKEY_FullEnumSyncSvc_SyncFormat, reinterpret_cast<BYTE*>(&PreferredSyncFormat), sizeof(PreferredSyncFormat)); + CHECK_HR(hr, ("Failed to set PKEY_FullEnumSyncSvc_SyncFormat")); + } + else if (IsEqualPropertyKey(Key, PKEY_Services_ServiceIcon)) + { + // Add PKEY_Services_ServiceIcon + hr = GetIconData(pStore); + CHECK_HR(hr, "Failed to set PKEY_Services_ServiceIcon"); + } + else if (IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_VersionProps)) + { + // Add PKEY_FullEnumSyncSvc_VersionProps + hr = GetVICData(pStore); + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_VersionProps"); + } + else if (IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_LocalOnlyDelete)) + { + // Add PKEY_FullEnumSyncSvc_LocalOnlyDelete + PROPVARIANT pv = {0}; + pv.vt = VT_UI1; + pv.bVal = LocalOnlyDelete; + + hr = pStore->SetValue(PKEY_FullEnumSyncSvc_LocalOnlyDelete, &pv); + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_LocalOnlyDelete"); + } + else if (IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_FilterType)) + { + // Add PKEY_FullEnumSyncSvc_FilterType + PROPVARIANT pv = {0}; + pv.vt = VT_UI1; + pv.bVal = FilterType; + + hr = pStore->SetValue(PKEY_FullEnumSyncSvc_FilterType, &pv); + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_FilterType"); + } + else if (IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_ReplicaID)) + { + // Add PKEY_FullEnumSyncSvc_ReplicaID + hr = pStore->SetBufferValue(PKEY_FullEnumSyncSvc_ReplicaID, reinterpret_cast<BYTE*>(&ReplicaId), sizeof(ReplicaId)); + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_ReplicaID"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Property {%ws}.%d is not supported", CComBSTR(Key.fmtid), Key.pid); + } + + return hr; +} + +/** + * This method is called to get the contacts service icon data. + * + * The parameters sent to us are: + * pValues - An IPortableDeviceValues to be populated with the icon data + * + * The driver should: + * Retrieve the icon data and set it in pValues for PKEY_Services_ServiceIcon + */ +HRESULT FakeContactsServiceContent::GetIconData( + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + PBYTE pIconData = NULL; + DWORD cbIconData = 0; + + if (pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + pIconData = GetResourceData(IDR_WPD_SAMPLEDRIVER_SERVICE_ICON); + cbIconData = GetResourceSize(IDR_WPD_SAMPLEDRIVER_SERVICE_ICON); + + if ((pIconData == NULL) || (cbIconData == 0)) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing the service icon data"); + } + + if (hr == S_OK) + { + hr = pStore->SetBufferValue(PKEY_Services_ServiceIcon, pIconData, cbIconData); + CHECK_HR(hr, "Failed to copy the icon data to IPortableDeviceValues"); + } + + return hr; +} + +/** + * This method is called to get the contacts service's full enumeration version properties + * + * The parameters sent to us are: + * pValues - An IPortableDeviceValues to be populated with the version property data + * + * The driver should: + * Retrieve the version property data blob and set it in pValues for SVCPROP_FullEnumVersionProps + * + * Version property data blob must adhere to the following format: + * + * Count of Change Unit Groups + * Change Unit PROPERTYKEY (group 0) + * Count of Keys (group 0) + * Key0, Key1..Keyn(group 0) + * ... + * Change Unit PROPERTYKEY (group 1) + * Count of Keys (group 1) + * Key0, Key1..Keyn(group 1) + */ +HRESULT FakeContactsServiceContent::GetVICData( + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = E_OUTOFMEMORY; + const DWORD cGroup = 1; // currently support only 1 group + DWORD cVIC = ARRAYSIZE(g_ContactsServiceChangeUnit); + + // Our VIC contains only a single change unit, identified by GUID_NULL + DWORD cbVIC = + sizeof(cGroup) + // count of groups of change units + // Currently we support only 1 group + sizeof(PROPERTYKEY) + // change unit identifier + sizeof(cVIC) + // change unit count + ARRAYSIZE(g_ContactsServiceChangeUnit) * sizeof(PROPERTYKEY); // Properties in this change unit + + BYTE *pVIC = new BYTE[cbVIC]; + + if (pVIC) + { + hr = E_FAIL; + + BYTE *pPos = pVIC; + const BYTE *pEnd = pVIC + cbVIC; + + if ((pPos + sizeof(cGroup)) <= pEnd) + { + // count of groups + memcpy(pPos, &cGroup, sizeof(cGroup)); + pPos+=sizeof(cGroup); + + if ((pPos + sizeof(PROPERTYKEY)) <= pEnd) + { + // Change Unit Identifier + memcpy(pPos, &WPD_PROPERTY_NULL, sizeof(PROPERTYKEY)); + pPos+=sizeof(PROPERTYKEY); + + if ((pPos + sizeof(cVIC)) <= pEnd) + { + // Number of items in the change unit + memcpy(pPos, &cVIC, sizeof(cVIC)); + pPos+=sizeof(cVIC); + + // Change Unit Property List + DWORD i = 0; + while ((i < cVIC) && (pPos + sizeof(PROPERTYKEY) <= pEnd)) + { + memcpy(pPos, g_ContactsServiceChangeUnit[i], sizeof(PROPERTYKEY)); + pPos+=sizeof(PROPERTYKEY); + i++; + } + + if ((pPos == pEnd) && (i == cVIC)) + { + // All done + hr = pStore->SetBufferValue(PKEY_FullEnumSyncSvc_VersionProps, pVIC, cbVIC); + } + } + } + } + delete [] pVIC; + } + + CHECK_HR(hr, "Failed to copy the PKEY_FullEnumSyncSvc_VersionProps data to IPortableDeviceValues"); + return hr; +} + +HRESULT FakeContactsServiceContent::WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value) +{ + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + Name = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because type (%d) was not VT_LPWSTR", Value.vt); + } + } + else if(IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_LocalOnlyDelete)) + { + if(Value.vt == VT_UI1) + { + LocalOnlyDelete = Value.bVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_LocalOnlyDelete because type (%d) was not VT_UI1", Value.vt); + } + } + else if(IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_FilterType)) + { + if(Value.vt == VT_UI1) + { + FilterType = Value.bVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_FilterType because type (%d) was not VT_UI1", Value.vt); + } + } + else if(IsEqualPropertyKey(Key, PKEY_FullEnumSyncSvc_ReplicaID)) + { + if(Value.vt == (VT_VECTOR | VT_UI1) && Value.caub.cElems == sizeof(ReplicaId)) + { + CopyMemory(&ReplicaId, Value.caub.pElems, sizeof(ReplicaId)); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set PKEY_FullEnumSyncSvc_FilterType because type (%d) was not VT_VECTOR | VT_UI1", Value.vt); + } + } + else + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(Key.fmtid), Key.pid, ObjectID); + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.h b/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.h new file mode 100644 index 00000000..e7dbee0e --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContactsServiceContent.h @@ -0,0 +1,92 @@ +#pragma once + +/** + * This class represents an abstraction of a contacts service content object + * Driver implementors should replace this with their own + * device I/O classes/libraries. + */ + +#define CONTACTS_SERVICE_OBJECT_ID L"789DEF" +#define CONTACTS_SERVICE_PERSISTENT_UNIQUE_ID L"{95A95EA9-9904-430E-8FF6-70851F208478}" +#define CONTACTS_SERVICE_OBJECT_NAME_VALUE NAME_ContactsSvc +#define CONTACTS_SERVICE_HUMAN_READABLE_NAME L"Hello World Phone Contacts" +#define CONTACTS_SERVICE_PREFERRED_FORMAT WPD_OBJECT_FORMAT_ABSTRACT_CONTACT +#define CONTACTS_SERVICE_VERSION L"1.0" +#define CONTACTS_SERVICE_LOCAL_ONLY_DELETE 1 + +#define NUM_CONTACT_OBJECTS 10 + +class FakeContactsServiceContent : public FakeContent +{ +public: + FakeContactsServiceContent() + { + ObjectID = CONTACTS_SERVICE_OBJECT_ID; + PersistentUniqueID = CONTACTS_SERVICE_PERSISTENT_UNIQUE_ID; + ParentID = WPD_DEVICE_OBJECT_ID; + Name = CONTACTS_SERVICE_OBJECT_NAME_VALUE; + ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + Format = WPD_OBJECT_FORMAT_UNSPECIFIED; + ParentPersistentUniqueID = WPD_DEVICE_OBJECT_ID; + ContainerFunctionalObjectID = WPD_DEVICE_OBJECT_ID; + + Version = CONTACTS_SERVICE_VERSION; + FunctionalCategory = SERVICE_Contacts; + HumanReadableName = CONTACTS_SERVICE_HUMAN_READABLE_NAME; + PreferredSyncFormat = CONTACTS_SERVICE_PREFERRED_FORMAT; + RequiredScope = CONTACTS_SERVICE_ACCESS; + LocalOnlyDelete = CONTACTS_SERVICE_LOCAL_ONLY_DELETE; + FilterType = SYNCSVC_FILTER_CONTACTS_WITH_PHONE; + CopyMemory(&ReplicaId, &MyFullEnumSyncReplicaId, sizeof(ReplicaId)); + } + + FakeContactsServiceContent(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeContactsServiceContent() + { + } + + virtual HRESULT InitializeContent( + _Inout_ DWORD *pdwLastObjectID); + + virtual HRESULT CreatePropertiesOnlyObject( + _In_ IPortableDeviceValues* pObjectProperties, + _Out_ DWORD* pdwLastObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppNewObject); + + virtual HRESULT GetSupportedProperties( + _In_ IPortableDeviceKeyCollection *pKeys); + + virtual HRESULT GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + virtual HRESULT GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore); + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value); + +private: + HRESULT GetIconData( + _In_ IPortableDeviceValues* pStore); + + HRESULT GetVICData( + _In_ IPortableDeviceValues* pStore); + +public: + CAtlStringW Version; + CAtlStringW HumanReadableName; + + GUID FunctionalCategory; + GUID PreferredSyncFormat; + GUID ReplicaId; + + BYTE LocalOnlyDelete; + BYTE FilterType; +}; diff --git a/wpd/WpdServiceSampleDriver/FakeContent.cpp b/wpd/WpdServiceSampleDriver/FakeContent.cpp new file mode 100644 index 00000000..96139791 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContent.cpp @@ -0,0 +1,635 @@ +#include "stdafx.h" +#include "FakeContent.tmh" + +HRESULT FakeContent::InitializeContent(_Inout_ DWORD *pdwLastObjectID) +{ + UNREFERENCED_PARAMETER(pdwLastObjectID); + return S_OK; +} + +HRESULT FakeContent::InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ WpdObjectEnumeratorContext* pEnumeratorContext) +{ + HRESULT hr = S_OK; + UNREFERENCED_PARAMETER(Scope); + + if (pEnumeratorContext == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + pEnumeratorContext->m_TotalChildren = static_cast<DWORD>(m_Children.GetCount()); + return hr; +} + +HRESULT FakeContent::GetSupportedProperties(_In_ IPortableDeviceKeyCollection *pKeys) +{ + HRESULT hr = S_OK; + + if(pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + hr = pKeys->Add(WPD_OBJECT_ID); + CHECK_HR(hr, "Failed to add WPD_OBJECT_ID"); + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_PERSISTENT_UNIQUE_ID); + CHECK_HR(hr, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_PARENT_ID); + CHECK_HR(hr, "Failed to add WPD_OBJECT_PARENT_ID"); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_NAME); + CHECK_HR(hr, "Failed to add WPD_OBJECT_NAME"); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_CONTENT_TYPE); + CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTENT_TYPE"); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_FORMAT); + CHECK_HR(hr, "Failed to add WPD_OBJECT_FORMAT"); + } + + if (hr == S_OK) + { + hr = pKeys->Add(WPD_OBJECT_CAN_DELETE); + CHECK_HR(hr, "Failed to add WPD_OBJECT_CAN_DELETE"); + } + + return hr; +} + +HRESULT FakeContent::GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + else + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + + return hr; +} + +HRESULT FakeContent::GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + // Add WPD_OBJECT_ID + pvValue = ObjectID; + hr = pStore->SetValue(WPD_OBJECT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + // Add WPD_OBJECT_PERSISTENT_UNIQUE_ID + pvValue = this->PersistentUniqueID; + hr = pStore->SetValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + // Add WPD_OBJECT_PARENT_ID + pvValue = ParentID; + hr = pStore->SetValue(WPD_OBJECT_PARENT_ID, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_PARENT_ID")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + // Add WPD_OBJECT_NAME + pvValue = Name; + hr = pStore->SetValue(WPD_OBJECT_NAME, &pvValue); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_NAME")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + // Add WPD_OBJECT_CONTENT_TYPE + hr = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CONTENT_TYPE")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + // Add WPD_OBJECT_FORMAT + hr = pStore->SetGuidValue(WPD_OBJECT_FORMAT, Format); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_FORMAT")); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + // Add WPD_OBJECT_CAN_DELETE + hr = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + CHECK_HR(hr, ("Failed to set WPD_OBJECT_CAN_DELETE")); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Property {%ws}.%d is not supported", CComBSTR(Key.fmtid), Key.pid); + } + return hr; +} + +HRESULT FakeContent::WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value) +{ + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + Name = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because type was not VT_LPWSTR"); + } + } + else + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(Key.fmtid), Key.pid, ObjectID); + } + + return hr; +} + +HRESULT FakeContent::CreatePropertiesOnlyObject( + _In_ IPortableDeviceValues* pObjectProperties, + _Out_ DWORD* pdwLastObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppNewObject) +{ + UNREFERENCED_PARAMETER(pObjectProperties); + UNREFERENCED_PARAMETER(pdwLastObjectID); + *ppNewObject = NULL; + return E_ACCESSDENIED; +} + +HRESULT FakeContent::GetSupportedResources( + _In_ IPortableDeviceKeyCollection* pResources) +{ + UNREFERENCED_PARAMETER(pResources); + return S_OK; +} + +HRESULT FakeContent::GetResourceAttributes( + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes) +{ + UNREFERENCED_PARAMETER(Resource); + UNREFERENCED_PARAMETER(pAttributes); + return S_OK; +} + +HRESULT FakeContent::OpenResource( + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext) +{ + UNREFERENCED_PARAMETER(Resource); + UNREFERENCED_PARAMETER(dwMode); + UNREFERENCED_PARAMETER(pResourceContext); + return S_OK; +} + +HRESULT FakeContent::ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) +{ + UNREFERENCED_PARAMETER(pResourceContext); + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(dwNumBytesToRead); + *pdwNumBytesRead = 0; + return S_OK; +} + +bool FakeContent::CanAccess( + ACCESS_SCOPE Scope) +{ + return ((Scope & RequiredScope) == RequiredScope); +} + +HRESULT FakeContent::GetAllValues( + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + DWORD cKeys = 0; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // CoCreate a collection to store the property keys we are going to use + // to request the property values of. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (hr == S_OK) + { + hr = GetSupportedProperties(pKeys); + CHECK_HR(hr, "Failed to get supported properties"); + } + + if (hr == S_OK) + { + hr = pKeys->GetCount(&cKeys); + CHECK_HR(hr, "Failed to get supported properties"); + } + + if (hr == S_OK) + { + for (DWORD i=0; i<cKeys; i++) + { + PROPERTYKEY Key = {0}; + hr = pKeys->GetAt(i, &Key); + CHECK_HR(hr, "Failed to get supported property at index %d", i); + if (hr == S_OK) + { + hr = GetValue(Key, pStore); + CHECK_HR(hr, "Failed to get property value at index %d", i); + } + } + } + + return hr; +} + +HRESULT FakeContent::WriteValues( + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _Out_ bool* pbObjectChanged) +{ + HRESULT hr = S_OK; + DWORD cValues = 0; + bool hasFailedWrite = false; + + if (pValues == NULL || pResults == NULL || pbObjectChanged == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = pValues->GetCount(&cValues); + CHECK_HR(hr, "Failed to get total number of values"); + + (*pbObjectChanged) = false; + + for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + PROPVARIANT Value; + PropVariantInit(&Value); + + hr = pValues->GetAt(dwIndex, &Key, &Value); + CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d", dwIndex); + + if (hr == S_OK) + { + HRESULT hrWrite = WriteValue(Key, Value); + if (FAILED(hrWrite)) + { + CHECK_HR(hrWrite, "Failed to write value at index %d", dwIndex); + hasFailedWrite = true; + } + else + { + (*pbObjectChanged) = true; + } + + hr = pResults->SetErrorValue(Key, hrWrite); + CHECK_HR(hr, "Failed to set error result value at index %d", dwIndex); + } + + PropVariantClear(&Value); + } + + // Since we have set failures for the property set operations we must let the application + // know by returning S_FALSE. This will instruct the application to look at the + // property set operation results for failure values. + if ((hr == S_OK) && hasFailedWrite) + { + hr = S_FALSE; + } + + return hr; +} + +_Success_(return) +bool FakeContent::FindNext( + ACCESS_SCOPE Scope, + const DWORD dwIndex, + _Outptr_result_nullonfailure_ FakeContent** ppChild) +{ + HRESULT hr = S_OK; + bool bFound = false; + + if (ppChild == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return false; + } + + *ppChild = NULL; + + if (dwIndex < m_Children.GetCount()) + { + if (m_Children[dwIndex] && (m_Children[dwIndex]->CanAccess(Scope))) + { + *ppChild = m_Children[dwIndex]; + bFound = true; + } + } + + return bFound; +} + +HRESULT FakeContent::GetContent( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppContent) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + *ppContent = NULL; + + if (CanAccess(Scope)) + { + if (ObjectID.CompareNoCase(wszObjectID) == 0) + { + hr = S_OK; + *ppContent = this; + } + else + { + DWORD dwIndex = 0; + FakeContent* pChild = NULL; + while (FindNext(Scope, dwIndex, &pChild)) + { + hr = pChild->GetContent(Scope, wszObjectID, ppContent); + if (hr == S_OK || hr == E_ACCESSDENIED) + { + break; + } + dwIndex++; + } + } + } + else + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "GetContent: '%ws' was found but falls outside scope", wszObjectID); + } + + return hr; +} + +HRESULT FakeContent::GetObjectIDsByFormat( + ACCESS_SCOPE Scope, + _In_ REFGUID guidFormat, + const DWORD dwDepth, + _In_ IPortableDevicePropVariantCollection* pObjectIDs) +{ + HRESULT hr = S_OK; + + if (CanAccess(Scope)) + { + DWORD dwIndex = 0; + FakeContent* pChild = NULL; + + if (Format == guidFormat || guidFormat == WPD_OBJECT_FORMAT_ALL) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_LPWSTR; + pv.pwszVal = ObjectID.GetBuffer(); + hr = pObjectIDs->Add(&pv); + CHECK_HR(hr, "Failed to add '%ws' to the list of object IDs by format", ObjectID); + } + + if (dwDepth > 0) + { + while ((hr == S_OK) && (FindNext(Scope, dwIndex, &pChild))) + { + hr = pChild->GetObjectIDsByFormat(Scope, guidFormat, dwDepth-1, pObjectIDs); + CHECK_HR(hr, "Failed to get object IDs by format for child at index %d", dwIndex); + dwIndex++; + } + } + } + else + { + hr = E_ACCESSDENIED; + } + + return hr; +} + +HRESULT FakeContent::GetObjectIDByPersistentID( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszPersistentID, + _In_ IPortableDevicePropVariantCollection* pObjectIDs) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + + if (CanAccess(Scope)) + { + if (PersistentUniqueID.CompareNoCase(wszPersistentID) == 0) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_LPWSTR; + pv.pwszVal = ObjectID.GetBuffer(); + + hr = pObjectIDs->Add(&pv); + CHECK_HR(hr, "Failed to add '%ws' to the list of object IDs", ObjectID); + } + else + { + DWORD dwIndex = 0; + FakeContent* pChild = NULL; + while (FindNext(Scope, dwIndex, &pChild)) + { + hr = pChild->GetObjectIDByPersistentID(Scope, wszPersistentID, pObjectIDs); + if (hr == S_OK || hr == E_ACCESSDENIED) + { + // Found the object or was denied access + break; + } + else if (hr != HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) + { + CHECK_HR(hr, "Failed to get object ID for child at index %d", dwIndex); + } + dwIndex++; + } + } + } + else + { + hr = E_ACCESSDENIED; + } + + return hr; +} + +HRESULT FakeContent::MarkForDelete( + const DWORD dwOptions) +{ + HRESULT hr = S_OK; + + if (CanDelete == false) + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Object '%ws' is not deletable", ObjectID); + return hr; + } + + if (dwOptions == PORTABLE_DEVICE_DELETE_NO_RECURSION) + { + if (m_Children.GetCount() > 0) + { + hr = HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY); + } + } + else if (dwOptions == PORTABLE_DEVICE_DELETE_WITH_RECURSION) + { + // Mark children for delete + for (size_t Index = 0; Index < m_Children.GetCount(); Index++) + { + if (m_Children[Index]) + { + hr = m_Children[Index]->MarkForDelete(dwOptions); + CHECK_HR(hr, "Failed to mark child '%ws' for deletion", m_Children[Index]->ObjectID); + } + } + } + + if (hr == S_OK) + { + // All successful. Mark self for delete + MarkedForDeletion = true; + } + + return hr; +} + +HRESULT FakeContent::RemoveObjectsMarkedForDeletion( + ACCESS_SCOPE Scope) +{ + HRESULT hr = S_OK; + DWORD dwIndex = 0; + FakeContent* pChild = NULL; + + while (FindNext(Scope, dwIndex, &pChild)) + { + if (pChild != NULL) + { + hr = pChild->RemoveObjectsMarkedForDeletion(Scope); + CHECK_HR(hr, "Failed to remove children marked for deletion for object '%ws'", pChild->ObjectID); + + if ((hr == S_OK) && (pChild->MarkedForDeletion == true)) + { + m_Children.RemoveAt(dwIndex); + delete pChild; + pChild = NULL; + } + } + dwIndex++; + } + + m_Children.FreeExtra(); + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/FakeContent.h b/wpd/WpdServiceSampleDriver/FakeContent.h new file mode 100644 index 00000000..3f1f7103 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeContent.h @@ -0,0 +1,151 @@ +#pragma once + +class FakeContent +{ +public: + FakeContent() : + CanDelete(false), + RequiredScope(FULL_DEVICE_ACCESS), + MarkedForDeletion(false) + { + Format = WPD_OBJECT_FORMAT_UNSPECIFIED; + ContentType = WPD_CONTENT_TYPE_UNSPECIFIED; + } + + FakeContent(const FakeContent& src) : + CanDelete(false) + { + *this = src; + } + + virtual ~FakeContent() + { + for(size_t index = 0; index < m_Children.GetCount(); index++) + { + if (m_Children[index]) + { + delete(m_Children[index]); + m_Children[index] = NULL; + } + } + m_Children.RemoveAll(); + } + + virtual FakeContent& operator= (const FakeContent& src) + { + ObjectID = src.ObjectID; + PersistentUniqueID = src.PersistentUniqueID; + ParentID = src.ParentID; + Name = src.Name; + ContentType = src.ContentType; + Format = src.Format; + CanDelete = src.CanDelete; + RequiredScope = src.RequiredScope; + ParentPersistentUniqueID = src.ParentPersistentUniqueID; + ContainerFunctionalObjectID = src.ContainerFunctionalObjectID; + + return *this; + } + + virtual HRESULT InitializeContent( + _Inout_ DWORD *pdwLastObjectID); + + virtual HRESULT InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ WpdObjectEnumeratorContext* pEnumeratorContext); + + virtual HRESULT GetSupportedProperties( + _In_ IPortableDeviceKeyCollection *pKeys); + + virtual HRESULT GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + virtual HRESULT GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore); + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value); + + virtual HRESULT CreatePropertiesOnlyObject( + _In_ IPortableDeviceValues* pObjectProperties, + _Out_ DWORD* pdwLastObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppNewObject); + + virtual HRESULT GetSupportedResources( + _In_ IPortableDeviceKeyCollection* pResources); + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes); + + virtual HRESULT OpenResource( + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext); + + virtual HRESULT ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead); + + virtual HRESULT WriteValues( + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _Out_ bool* pbObjectChanged); + +public: + bool CanAccess( + ACCESS_SCOPE Scope); + + HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore); + + _Success_(return) + bool FindNext( + ACCESS_SCOPE Scope, + const DWORD dwIndex, + _Outptr_result_nullonfailure_ FakeContent** ppChild); + + HRESULT GetContent( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppContent); + + HRESULT GetObjectIDsByFormat( + ACCESS_SCOPE Scope, + _In_ REFGUID Format, + const DWORD dwDepth, + _In_ IPortableDevicePropVariantCollection* pObjectIDs); + + HRESULT GetObjectIDByPersistentID( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszPersistentID, + _In_ IPortableDevicePropVariantCollection* pObjectIDs); + + HRESULT MarkForDelete( + const DWORD dwOptions); + + HRESULT RemoveObjectsMarkedForDeletion( + ACCESS_SCOPE Scope); + +public: + CAtlStringW ObjectID; + CAtlStringW PersistentUniqueID; + CAtlStringW ParentID; + CAtlStringW Name; + CAtlStringW ParentPersistentUniqueID; + CAtlStringW ContainerFunctionalObjectID; + GUID ContentType; + GUID Format; + bool CanDelete; + bool MarkedForDeletion; + + // A bitmask of all the required scopes in order to access this object + ACCESS_SCOPE RequiredScope; + + CAtlArray<FakeContent*> m_Children; +}; diff --git a/wpd/WpdServiceSampleDriver/FakeDevice.cpp b/wpd/WpdServiceSampleDriver/FakeDevice.cpp new file mode 100644 index 00000000..d5f8d4d4 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeDevice.cpp @@ -0,0 +1,1122 @@ +#include "stdafx.h" + +#include "FakeDevice.tmh" + +const PROPERTYKEY* g_SupportedCommands[] = +{ + // WPD_CATEGORY_OBJECT_ENUMERATION + &WPD_COMMAND_OBJECT_ENUMERATION_START_FIND, + &WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT, + &WPD_COMMAND_OBJECT_ENUMERATION_END_FIND, + + // WPD_CATEGORY_OBJECT_PROPERTIES + &WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED, + &WPD_COMMAND_OBJECT_PROPERTIES_GET, + &WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL, + &WPD_COMMAND_OBJECT_PROPERTIES_SET, + &WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES, + &WPD_COMMAND_OBJECT_PROPERTIES_DELETE, + + // WPD_CATEGORY_OBJECT_RESOURCES + &WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED, + &WPD_COMMAND_OBJECT_RESOURCES_OPEN, + &WPD_COMMAND_OBJECT_RESOURCES_READ, + &WPD_COMMAND_OBJECT_RESOURCES_CLOSE, + &WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES, + + // WPD_CATEGORY_CAPABILITIES + &WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS, + &WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS, + &WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES, + &WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS, + &WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES, + &WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS, + &WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES, + &WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES, + + // WPD_CATEGORY_OBJECT_MANAGEMENT + &WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY, + &WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS, + + // WPD_CATEGORY_OBJECT_PROPERTIES_BULK + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_END, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_END, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_START, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT, + &WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END, + + // WPD_CATEGORY_COMMON + &WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS, +}; + + +const GUID* g_SupportedFunctionalCategories[] = +{ + &WPD_FUNCTIONAL_CATEGORY_DEVICE, + &WPD_FUNCTIONAL_CATEGORY_STORAGE, + &SERVICE_Contacts, +}; + +const PROPERTYKEY* g_SupportedCommonProperties[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, +}; + + +HRESULT FakeDevice::InitializeContent() +{ + HRESULT hr = m_DeviceContent.InitializeContent(&m_dwLastObjectID); + CHECK_HR(hr, "Failed to initialize device content"); + return hr; +} + +FakeContactsService* FakeDevice::GetContactsService() +{ + return &m_ContactsService; +} + +ACCESS_SCOPE FakeDevice::GetAccessScope( + _In_ IPortableDeviceValues* pParams) +{ + ACCESS_SCOPE Scope = FULL_DEVICE_ACCESS; + LPWSTR pszFileName = NULL; + + // For simplicity, our request filename is the same as the the service object ID + if (pParams && (pParams->GetStringValue(PRIVATE_SAMPLE_DRIVER_REQUEST_FILENAME, &pszFileName) == S_OK)) + { + CAtlStringW strRequestFilename = pszFileName; + // For simplicity, our request filename is the same as the the service object ID + // Case-insensitive comparison is required + if (strRequestFilename.CompareNoCase(m_ContactsService.GetRequestFilename()) == 0) + { + Scope = CONTACTS_SERVICE_ACCESS; + } + } + + CoTaskMemFree(pszFileName); + return Scope; +} + +HRESULT FakeDevice::GetSupportedCommands( + _In_ IPortableDeviceKeyCollection* pCommands) +{ + HRESULT hr = S_OK; + + if(pCommands == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommands); dwIndex++) + { + PROPERTYKEY key = *(g_SupportedCommands[dwIndex]); + hr = pCommands->Add(key); + CHECK_HR(hr, "Failed to add supported command at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + return hr; +} + +HRESULT FakeDevice::GetCommandOptions( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pOptions) +{ + HRESULT hr = S_OK; + + if(pOptions == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Check for command options + if (IsEqualPropertyKey(WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS, Command)) + { + // This driver does not support recursive deletion + hr = pOptions->SetBoolValue(WPD_OPTION_OBJECT_MANAGEMENT_RECURSIVE_DELETE_SUPPORTED, TRUE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_MANAGEMENT_RECURSIVE_DELETE_SUPPORTED"); + } + if (IsEqualPropertyKey(WPD_COMMAND_OBJECT_RESOURCES_READ, Command)) + { + // For better read performance, tell the API not to provide the input buffer parameter + // when issuing a WPD_COMMAND_OBJECT_RESOURCES_READ command. + hr = pOptions->SetBoolValue(WPD_OPTION_OBJECT_RESOURCES_NO_INPUT_BUFFER_ON_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_RESOURCES_NO_INPUT_BUFFER_ON_READ"); + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedFunctionalCategories( + _In_ IPortableDevicePropVariantCollection* pFunctionalCategories) +{ + HRESULT hr = S_OK; + + if(pFunctionalCategories == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Device-wide command + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedFunctionalCategories); dwIndex++) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + + pv.vt = VT_CLSID; + pv.puuid = (CLSID*)g_SupportedFunctionalCategories[dwIndex]; + + hr = pFunctionalCategories->Add(&pv); + CHECK_HR(hr, "Failed to add supported functional category at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + + return hr; +} + +HRESULT FakeDevice::GetFunctionalObjects( + _In_ REFGUID guidFunctionalCategory, + _In_ IPortableDevicePropVariantCollection* pFunctionalObjects) +{ + HRESULT hr = S_OK; + PROPVARIANT pv = {0}; + + if(pFunctionalObjects == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these object identifiers + + // Add WPD_DEVICE_OBJECT_ID to the functional object identifiers collection + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_DEVICE) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = WPD_DEVICE_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add device object ID"); + } + + // Add CONTACTS_SERVICE_OBJECT_ID to the functional object identifiers collection + if (hr == S_OK) + { + if ((guidFunctionalCategory == SERVICE_Contacts) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = CONTACTS_SERVICE_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add contacts service object ID"); + } + } + + // Add STORAGE_OBJECT_ID to the functional object identifiers collection + // if request is not scoped by the contacts service + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = STORAGE_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add storage object ID"); + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedContentTypes( + _In_ REFGUID guidFunctionalCategory, + _In_ IPortableDevicePropVariantCollection* pContentTypes) +{ + HRESULT hr = S_OK; + + if(pContentTypes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + + // Add supported content types for known functional categories + if (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE) + { + // Add WPD_CONTENT_TYPE_DOCUMENT to the supported content type collection + pv.vt = VT_CLSID; + pv.puuid = (CLSID*)&WPD_CONTENT_TYPE_DOCUMENT; + hr = pContentTypes->Add(&pv); + CHECK_HR(hr, "Failed to add WPD_CONTENT_TYPE_DOCUMENT"); + + if (hr == S_OK) + { + // Add WPD_CONTENT_TYPE_FOLDER to the supported content type collection + pv.vt = VT_CLSID; + pv.puuid = (CLSID*)&WPD_CONTENT_TYPE_FOLDER; + hr = pContentTypes->Add(&pv); + CHECK_HR(hr, "Failed to add WPD_CONTENT_TYPE_FOLDER"); + } + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedFormats( + _In_ REFGUID guidContentType, + _In_ IPortableDevicePropVariantCollection* pFormats) +{ + HRESULT hr = S_OK; + + if(pFormats == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_CLSID; + + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + + if ((guidContentType == WPD_CONTENT_TYPE_CONTACT) || + (guidContentType == WPD_CONTENT_TYPE_ALL)) + { + pv.puuid = (CLSID*)&FORMAT_AbstractContact; + hr = pFormats->Add(&pv); + CHECK_HR(hr, "Failed to add FORMAT_AbstractContact"); + + pv.puuid = (CLSID*)&FORMAT_VCard2Contact; + hr = pFormats->Add(&pv); + CHECK_HR(hr, "Failed to add FORMAT_VCard2Contact"); + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedFormatProperties( + _In_ REFGUID guidObjectFormat, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + + if(pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (guidObjectFormat == WPD_OBJECT_FORMAT_ALL) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++) + { + PROPERTYKEY key = *g_SupportedCommonProperties[dwIndex]; + hr = pKeys->Add(key); + CHECK_HR(hr, "Failed to add common property"); + } + } + + return hr; +} + +HRESULT FakeDevice::GetFixedPropertyAttributes( + _In_ REFGUID guidObjectFormat, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + UNREFERENCED_PARAMETER(guidObjectFormat); + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // + // Since ALL of our properties have the same attributes, we are ignoring the + // passed in guidObjectFormat and Key parameters. These parameters allow you to + // customize fixed property attributes for properties for specific formats. + // + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedEvents( + _In_ IPortableDevicePropVariantCollection* pEvents) +{ + UNREFERENCED_PARAMETER(pEvents); + + HRESULT hr = S_OK; + + if(pEvents == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + return hr; +} + +HRESULT FakeDevice::GetEventOptions( + _In_ IPortableDeviceValues* pOptions) +{ + UNREFERENCED_PARAMETER(pOptions); + + HRESULT hr = S_OK; + + if(pOptions == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + return hr; +} + +void FakeDevice::InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszParentID, + _In_ WpdObjectEnumeratorContext* pEnumContext) +{ + if (pEnumContext == NULL) + { + return; + } + + pEnumContext->m_Scope = Scope; + pEnumContext->m_strParentObjectID = wszParentID; + + if (pEnumContext->m_strParentObjectID.GetLength() == 0) + { + // Clients passing an 'empty' string for the parent are asking for the + // 'DEVICE' object. We should return 1 child in this case. + pEnumContext->m_TotalChildren = 1; + } + else + { + FakeContent* pContent = NULL; + HRESULT hr = GetContent(Scope, wszParentID, &pContent); + if (hr == S_OK) + { + hr = pContent->InitializeEnumerationContext(Scope, pEnumContext); + CHECK_HR(hr, "Failed to initialize enuemration context for '%ws'", wszParentID); + } + + if (hr != S_OK) + { + // Invalid, or non-existing objects contain no children. + pEnumContext->m_TotalChildren = 0; + } + } +} + +HRESULT FakeDevice::FindNext( + const DWORD dwNumObjectsRequested, + _In_ WpdObjectEnumeratorContext* pEnumContext, + _In_ IPortableDevicePropVariantCollection* pObjectIDCollection, + _Out_opt_ DWORD* pdwNumObjectsEnumerated) +{ + HRESULT hr = S_OK; + DWORD NumObjectsEnumerated = 0; + + if ((pEnumContext == NULL) || + (pObjectIDCollection == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (pdwNumObjectsEnumerated) + { + *pdwNumObjectsEnumerated = 0; + } + + // If the enumeration context reports that their are more objects to return, then continue, if not, + // return an empty results set. + if (pEnumContext->HasMoreChildrenToEnumerate()) + { + if (pEnumContext->m_strParentObjectID.CompareNoCase(L"") == 0) + { + // We are being asked for the device + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, m_DeviceContent.ObjectID); + CHECK_HR(hr, "Failed to add 'DEVICE' object ID to enumeration collection"); + + // Update the the number of children we are returning for this enumeration call + NumObjectsEnumerated++; + } + else + { + FakeContent* pContent = NULL; + HRESULT hrGet = GetContent(pEnumContext->m_Scope, pEnumContext->m_strParentObjectID, &pContent); + CHECK_HR(hrGet, "Failed to get content '%ws'", pEnumContext->m_strParentObjectID); + + if (hrGet == S_OK) + { + DWORD dwStartIndex = pEnumContext->m_ChildrenEnumerated; + for (DWORD i=0; i<dwNumObjectsRequested; i++) + { + FakeContent* pChild = NULL; + if (pContent->FindNext(pEnumContext->m_Scope, dwStartIndex, &pChild)) + { + hr = AddStringValueToPropVariantCollection(pObjectIDCollection, pChild->ObjectID); + CHECK_HR(hr, "Failed to add object [%ws]", pChild->ObjectID); + + if (hr == S_OK) + { + // Update the the number of children we are returning for this enumeration call + dwStartIndex++; + NumObjectsEnumerated++; + } + } + else + { + // no more children + break; + } + } + } + } + } + + if (hr == S_OK && pdwNumObjectsEnumerated) + { + *pdwNumObjectsEnumerated = NumObjectsEnumerated; + } + + return hr; +} + +HRESULT FakeDevice::GetObjectIDsByFormat( + ACCESS_SCOPE Scope, + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR wszParentObjectID, + const DWORD dwDepth, + _In_ IPortableDevicePropVariantCollection* pObjectIDs) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if(pObjectIDs == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + hr = GetContent(Scope, wszParentObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszParentObjectID); + + if (hr == S_OK) + { + hr = pContent->GetObjectIDsByFormat(Scope, guidObjectFormat, dwDepth, pObjectIDs); + CHECK_HR(hr, "Failed to get object IDs by format"); + } + + return hr; +} + +HRESULT FakeDevice::GetObjectIDsFromPersistentUniqueIDs( + ACCESS_SCOPE Scope, + _In_ IPortableDevicePropVariantCollection* pPersistentIDs, + _In_ IPortableDevicePropVariantCollection* pObjectIDs) +{ + HRESULT hr = S_OK; + DWORD dwCount = 0; + + if ((pPersistentIDs == NULL) || + (pObjectIDs == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + // Iterate through the persistent ID list and add the equivalent object ID for each element. + hr = pPersistentIDs->GetCount(&dwCount); + CHECK_HR(hr, "Failed to get count from persistent ID collection"); + + if (hr == S_OK) + { + PROPVARIANT pvPersistentID = {0}; + + for(DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++) + { + PropVariantInit(&pvPersistentID); + + hr = pPersistentIDs->GetAt(dwIndex, &pvPersistentID); + CHECK_HR(hr, "Failed to get persistent ID at index %d", dwIndex); + + if (hr == S_OK) + { + hr = m_DeviceContent.GetObjectIDByPersistentID(Scope, pvPersistentID.pwszVal, pObjectIDs); + CHECK_HR(hr, "Failed to get object ID from persistent unique ID '%ws'", pvPersistentID.pwszVal); + } + + if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND)) + { + PROPVARIANT pvEmptyObjectID = {0}; + pvEmptyObjectID.vt = VT_LPWSTR; + pvEmptyObjectID.pwszVal = L""; + + // Insert empty string when object cannot be found + hr = pObjectIDs->Add(&pvEmptyObjectID); + CHECK_HR(hr, "Failed to set empty string for persistent unique ID '%ws' when object cannot be found", pvPersistentID.pwszVal); + } + + PropVariantClear(&pvPersistentID); + + if(FAILED(hr)) + { + break; + } + } + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedProperties( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pKeys == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->GetSupportedProperties(pKeys); + CHECK_HR(hr, "Failed to get supported properties for '%ws'", wszObjectID); + } + + return hr; +} + +HRESULT FakeDevice::GetAllPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pValues) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pValues == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->GetAllValues(pValues); + CHECK_HR(hr, "Failed to get all property values for '%ws'", wszObjectID); + } + return hr; +} + + +HRESULT FakeDevice::GetPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues) +{ + HRESULT hrReturn = S_OK; + HRESULT hr = S_OK; + DWORD cKeys = 0; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pKeys == NULL) || + (pValues == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pKeys->GetCount(&cKeys); + CHECK_HR(hr, "Failed to number of PROPERTYKEYs in collection"); + } + + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + PROPERTYKEY Key = WPD_PROPERTY_NULL; + hr = pKeys->GetAt(dwIndex, &Key); + CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex); + + if (hr == S_OK) + { + hr = pContent->GetValue(Key, pValues); + CHECK_HR(hr, "Failed to get property at index %d", dwIndex); + if (FAILED(hr)) + { + // Mark the property as failed by setting the error value + // hrReturn is marked as S_FALSE indicating that at least one property has failed. + hr = pValues->SetErrorValue(Key, hr); + hrReturn = S_FALSE; + } + } + } + } + + if (FAILED(hr)) + { + // A general error has occurred (rather than failure to set one or more properties) + hrReturn = hr; + } + + return hrReturn; +} + +HRESULT FakeDevice::SetPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _In_ IPortableDeviceValues* pEventParams, + _Out_ bool* pbObjectChanged) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pValues == NULL) || + (pResults == NULL) || + (pEventParams == NULL) || + (pbObjectChanged == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pbObjectChanged = false; + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->WriteValues(pValues, pResults, pbObjectChanged); + CHECK_HR(hr, "Failed to write value for '%ws'", wszObjectID); + + if (SUCCEEDED(hr) && (*pbObjectChanged)) // hr can be S_OK or S_FALSE (if one or more property writes failed) + { + HRESULT hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, pContent->PersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pContent->ParentPersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + // Adding this event parameter will allow WPD to scope this event to the container functional object + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, pContent->ContainerFunctionalObjectID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID"); + } + } + + } + + return hr; +} + +HRESULT FakeDevice::GetPropertyAtributes( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pAttributes == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->GetPropertyAttributes(Key, pAttributes); + CHECK_HR(hr, "Failed to get property attributes for '%ws'", wszObjectID); + } + + return hr; +} + +HRESULT FakeDevice::CreatePropertiesOnlyObject( + ACCESS_SCOPE Scope, + _In_ IPortableDeviceValues* pObjectProperties, + _In_ IPortableDeviceValues* pEventParams, + _Outptr_result_nullonfailure_ LPWSTR* ppszNewObjectID) +{ + HRESULT hr; + LPWSTR pszParentID = NULL; + FakeContent* pParent = NULL; + FakeContent* pNewObject = NULL; + + if ((pObjectProperties == NULL) || + (pEventParams == NULL) || + (ppszNewObjectID == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszNewObjectID = NULL; + + // Get WPD_OBJECT_PARENT_ID + hr = pObjectProperties->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID); + CHECK_HR(hr, "Failed to get WPD_OBJECT_PARENT_ID"); + + // Check if it is within our current access scope + if (SUCCEEDED(hr)) + { + hr = GetContent(Scope, pszParentID, &pParent); + CHECK_HR(hr, "Failed to get content '%ws'", pszParentID); + } + + if (SUCCEEDED(hr)) + { + hr = pParent->CreatePropertiesOnlyObject(pObjectProperties, &m_dwLastObjectID, &pNewObject); + CHECK_HR(hr, "Failed to create properties only object with parent '%ws'", pszParentID); + } + + if (SUCCEEDED(hr)) + { + *ppszNewObjectID = AtlAllocTaskWideString(pNewObject->ObjectID); + if (*ppszNewObjectID == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate memory for created object ID"); + } + + HRESULT hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, pNewObject->PersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pNewObject->ParentPersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + // Adding this event parameter will allow WPD to scope this event to the container functional object + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, pNewObject->ContainerFunctionalObjectID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID"); + } + } + + CoTaskMemFree(pszParentID); + return hr; +} + +HRESULT FakeDevice::DeleteObject( + ACCESS_SCOPE Scope, + const DWORD dwDeleteOptions, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pEventParams) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pEventParams == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->MarkForDelete(dwDeleteOptions); + CHECK_HR(hr, "Failed to mark '%ws' for delete with option %d", wszObjectID, dwDeleteOptions); + } + + if (hr == S_OK) + { + HRESULT hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_REMOVED); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, pContent->PersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + hrEvent = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pContent->ParentPersistentUniqueID); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID"); + } + + if (hrEvent == S_OK) + { + // Adding this event parameter will allow WPD to scope this event to the container functional object + hrEvent = pEventParams->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, pContent->ContainerFunctionalObjectID); + CHECK_HR(hrEvent, "Failed to add WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID"); + } + + hr = m_DeviceContent.RemoveObjectsMarkedForDeletion(Scope); + CHECK_HR(hr, "Failed to remove all objects marked for deletion"); + } + + return hr; +} + +HRESULT FakeDevice::GetSupportedResources( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pResources) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pResources == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->GetSupportedResources(pResources); + CHECK_HR(hr, "Failed to get the supported resources for '%ws'", wszObjectID); + } + + return hr; +} + +HRESULT FakeDevice::GetResourceAttributes( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pAttributes == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + hr = pContent->GetResourceAttributes(Resource, pAttributes); + CHECK_HR(hr, "Failed to get the supported resources for '%ws'", wszObjectID); + } + + return hr; +} + +HRESULT FakeDevice::OpenResource( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((wszObjectID == NULL) || + (pResourceContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = GetContent(Scope, wszObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + if (hr == S_OK) + { + pResourceContext->m_Scope = Scope; + hr = pContent->OpenResource(Resource, dwMode, pResourceContext); + CHECK_HR(hr, "Failed to open resource for '%ws'", wszObjectID); + } + + return hr; +} + +HRESULT FakeDevice::ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) +{ + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if ((pResourceContext == NULL) || + (pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + *pdwNumBytesRead = 0; + + hr = GetContent(pResourceContext->m_Scope, pResourceContext->m_strObjectID, &pContent); + CHECK_HR(hr, "Failed to get content '%ws'", pResourceContext->m_strObjectID); + + if (hr == S_OK) + { + hr = pContent->ReadResourceData(pResourceContext, pBuffer, dwNumBytesToRead, pdwNumBytesRead); + CHECK_HR(hr, "Failed to read resource data for '%ws'", pResourceContext->m_strObjectID); + } + + return hr; +} + +HRESULT FakeDevice::GetContent( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppContent) +{ + HRESULT hr = S_OK; + + if (ppContent == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + } + + *ppContent = NULL; + + hr = m_DeviceContent.GetContent(Scope, wszObjectID, ppContent); + CHECK_HR(hr, "Failed to get content '%ws'", wszObjectID); + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/FakeDevice.h b/wpd/WpdServiceSampleDriver/FakeDevice.h new file mode 100644 index 00000000..22b23082 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeDevice.h @@ -0,0 +1,182 @@ +#pragma once + +/** + * This class represents an abstraction of a real device. + * Driver implementors should replace this with their own + * device I/O classes/libraries. + */ + +class FakeDevice +{ +public: + FakeDevice() : m_dwLastObjectID(0) + { + } + + ~FakeDevice() + { + } + + HRESULT InitializeContent(); + + FakeContactsService* GetContactsService(); + + ACCESS_SCOPE GetAccessScope( + _In_ IPortableDeviceValues* pParams); + + // Device Capabilities + // These are legacy commands that apply to the whole device, no access scope is required + HRESULT GetSupportedCommands( + _In_ IPortableDeviceKeyCollection* pCommands); + + HRESULT GetCommandOptions( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pOptions); + + HRESULT GetSupportedFunctionalCategories( + _In_ IPortableDevicePropVariantCollection* pFunctionalCategories); + + HRESULT GetFunctionalObjects( + _In_ REFGUID guidFunctionalCategory, + _In_ IPortableDevicePropVariantCollection* pFunctionalObjects); + + HRESULT GetSupportedContentTypes( + _In_ REFGUID guidFunctionalCategory, + _In_ IPortableDevicePropVariantCollection* pContentTypes); + + HRESULT GetSupportedFormats( + _In_ REFGUID guidContentType, + _In_ IPortableDevicePropVariantCollection* pFormats); + + HRESULT GetSupportedFormatProperties( + _In_ REFGUID guidObjectFormat, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT GetFixedPropertyAttributes( + _In_ REFGUID guidObjectFormat, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT GetSupportedEvents( + _In_ IPortableDevicePropVariantCollection* pEvents); + + HRESULT GetEventOptions( + _In_ IPortableDeviceValues* pOptions); + + // Enumeration + // Depending on the access scope, the driver can display only objects within the current + // scoped hierarchy tree + void InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszParentID, + _In_ WpdObjectEnumeratorContext* pEnumContext); + + HRESULT FindNext( + const DWORD dwNumObjectsRequested, + _In_ WpdObjectEnumeratorContext* pEnumContext, + _In_ IPortableDevicePropVariantCollection* pObjectIDCollection, + _Out_opt_ DWORD* pdwNumObjectsEnumerated); + + HRESULT GetObjectIDsByFormat( + ACCESS_SCOPE Scope, + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR wszParentObjectID, + const DWORD dwDepth, + _In_ IPortableDevicePropVariantCollection* pObjectIDs); + + HRESULT GetObjectIDsFromPersistentUniqueIDs( + ACCESS_SCOPE Scope, + _In_ IPortableDevicePropVariantCollection* pPersistentIDs, + _In_ IPortableDevicePropVariantCollection* pObjectIDs); + + // Property Management + // Depending on the access scope, the driver can allow access to properties of objects within the current + // scoped hierarchy tree + HRESULT GetSupportedProperties( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys); + + HRESULT GetAllPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pValues); + + HRESULT GetPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _In_ IPortableDeviceValues* pValues); + + HRESULT SetPropertyValues( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pValues, + _In_ IPortableDeviceValues* pResults, + _In_ IPortableDeviceValues* pEventParams, + _Out_ bool* pbObjectChanged); + + HRESULT GetPropertyAtributes( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + + // Object Management + // Depending on the access scope, the driver can limit access only to objects within the current + // scoped hierarchy tree + HRESULT CreatePropertiesOnlyObject( + ACCESS_SCOPE Scope, + _In_ IPortableDeviceValues* pObjectProperties, + _In_ IPortableDeviceValues* pEventParams, + _Outptr_result_nullonfailure_ LPWSTR* ppszNewObjectID); + + HRESULT DeleteObject( + ACCESS_SCOPE Scope, + const DWORD dwDeleteOptions, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceValues* pEventParams); + + // Resources + HRESULT GetSupportedResources( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ IPortableDeviceKeyCollection* pResources); + + HRESULT GetResourceAttributes( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes); + + HRESULT OpenResource( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext); + + HRESULT ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead); + +private: + HRESULT GetContent( + ACCESS_SCOPE Scope, + _In_ LPCWSTR wszObjectID, + _Outptr_result_nullonfailure_ FakeContent** ppContent); + + HRESULT RemoveObjectsMarkedForDeletion(); + +private: + + // Simulates content on the device + FakeDeviceContent m_DeviceContent; + + // Simulates contacts service functionality + FakeContactsService m_ContactsService; + + DWORD m_dwLastObjectID; +}; diff --git a/wpd/WpdServiceSampleDriver/FakeDeviceContent.cpp b/wpd/WpdServiceSampleDriver/FakeDeviceContent.cpp new file mode 100644 index 00000000..3770d1ed --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeDeviceContent.cpp @@ -0,0 +1,428 @@ +#include "stdafx.h" + +#include "FakeDeviceContent.tmh" + +const PropertyAttributeInfo g_SupportedDeviceProperties[] = +{ + {&WPD_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PARENT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_NAME, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_FORMAT, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTENT_TYPE, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CAN_DELETE, VT_BOOL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_FUNCTIONAL_OBJECT_CATEGORY, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_FIRMWARE_VERSION, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_POWER_LEVEL, VT_UI4, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_POWER_SOURCE, VT_UI4, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_PROTOCOL, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_MODEL, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_SERIAL_NUMBER, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, VT_BOOL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_MANUFACTURER, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_FRIENDLY_NAME, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_DEVICE_TYPE, VT_UI4, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, +}; + +HRESULT FakeDeviceContent::InitializeContent( + _Inout_ DWORD *pdwLastObjectID) +{ + HRESULT hr = S_OK; + + if (pdwLastObjectID == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add top level object: Contacts Service + CAutoPtr<FakeContactsServiceContent> pContactsService(new FakeContactsServiceContent()); + if (pContactsService) + { + hr = pContactsService->InitializeContent(pdwLastObjectID); + if (hr == S_OK) + { + _ATLTRY + { + m_Children.Add(pContactsService); + pContactsService.Detach(); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeContactsServiceContent"); + } + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate contacts service content"); + } + + // Add top level object: Storage + CAutoPtr<FakeStorage> pFakeStorage(new FakeStorage()); + if (pFakeStorage) + { + _ATLTRY + { + m_Children.Add(pFakeStorage); + pFakeStorage.Detach(); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeStorage"); + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate storage content"); + } + + return hr; +} + +HRESULT FakeDeviceContent::InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ WpdObjectEnumeratorContext* pEnumeratorContext) +{ + HRESULT hr = S_OK; + + if (pEnumeratorContext == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Initialize the enumeration context + if (Scope == CONTACTS_SERVICE_ACCESS) + { + // scoped by contacts service, so only the contacts service is visible + pEnumeratorContext->m_TotalChildren = 1; + } + else + { + // default device wide enumeration, all children are visible + pEnumeratorContext->m_TotalChildren = static_cast<DWORD>(m_Children.GetCount()); + } + + return hr; +} + +HRESULT FakeDeviceContent::GetSupportedProperties( + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + if (pKeys == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + // Add the PROPERTYKEYs for the 'DEVICE' object + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++) + { + hr = pKeys->Add(*g_SupportedDeviceProperties[dwIndex].pKey); + CHECK_HR(hr, "Failed to add device property"); + } + + return hr; +} + +HRESULT FakeDeviceContent::GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + + if (pStore == NULL) + { + hr = E_INVALIDARG; + return hr; + } + + // Set DEVICE object properties + if (IsEqualPropertyKey(Key, WPD_DEVICE_FIRMWARE_VERSION)) + { + hr = pStore->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, FirmwareVersion); + CHECK_HR(hr, "Failed to set WPD_DEVICE_FIRMWARE_VERSION"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_LEVEL)) + { + hr = pStore->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, PowerLevel); + CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_LEVEL"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_SOURCE)) + { + hr = pStore->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, PowerSource); + CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_SOURCE"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_PROTOCOL)) + { + hr = pStore->SetStringValue(WPD_DEVICE_PROTOCOL, Protocol); + CHECK_HR(hr, "Failed to set WPD_DEVICE_PROTOCOL"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_MODEL)) + { + hr = pStore->SetStringValue(WPD_DEVICE_MODEL, Model); + CHECK_HR(hr, "Failed to set WPD_DEVICE_MODEL"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_SERIAL_NUMBER)) + { + hr = pStore->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, SerialNumber); + CHECK_HR(hr, "Failed to set WPD_DEVICE_SERIAL_NUMBER"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_MANUFACTURER)) + { + hr = pStore->SetStringValue(WPD_DEVICE_MANUFACTURER, Manufacturer); + CHECK_HR(hr, "Failed to set WPD_DEVICE_MANUFACTURER"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_FRIENDLY_NAME)) + { + hr = pStore->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, FriendlyName); + CHECK_HR(hr, "Failed to set WPD_DEVICE_FRIENDLY_NAME"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_TYPE)) + { + hr = pStore->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, DeviceType); + CHECK_HR(hr, "Failed to set WPD_DEVICE_TYPE"); + } + + // Set general properties for DEVICE + else if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_ID, ObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + hr = pStore->SetStringValue(WPD_OBJECT_NAME, Name); + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, PersistentUniqueID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_PARENT_ID, ParentID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pStore->SetGuidValue(WPD_OBJECT_FORMAT, Format); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE"); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, FunctionalCategory); + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, ContainerFunctionalObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_DEVICE_SUPPORTS_NON_CONSUMABLE)) + { + hr = pStore->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, SupportsNonConsumable); + CHECK_HR(hr, "Failed to set WPD_DEVICE_SUPPORTS_NON_CONSUMABLE"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Property %ws.%d is not supported", CComBSTR(Key.fmtid), Key.pid); + } + + return hr; +} + + +HRESULT FakeDeviceContent::GetSupportedResources( + _In_ IPortableDeviceKeyCollection* pResources) +{ + HRESULT hr = S_OK; + + if (pResources == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (hr == S_OK) + { + hr = pResources->Add(WPD_RESOURCE_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ICON for the Device object"); + } + + return hr; +} + +HRESULT FakeDeviceContent::GetResourceAttributes( + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (IsEqualPropertyKey(Resource, WPD_RESOURCE_ICON)) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_DELETE"); + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedLargeIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_WRITE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_DELETE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE, FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE, FILE_OPTIMAL_WRITE_BUFFER_SIZE_VALUE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE"); + } + } + + return hr; +} + +HRESULT FakeDeviceContent::OpenResource( + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext) +{ + HRESULT hr = S_OK; + + if (pResourceContext == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Validate whether the params given to us are correct. In this case, we need to check that the object + // supports the resource requested, and can be opened in the requested access mode. + + // In this sample, we only support one resource (WPD_RESOURCE_ICON) for reading only. + // So if any resource or dwMode is specified, it must be invalid. + if (!IsEqualPropertyKey(Resource, WPD_RESOURCE_ICON)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Only WPD_RESOURCE_DEFAULT is supported in this sample driver"); + } + + if ((hr == S_OK) && ((dwMode & STGM_WRITE) != 0)) + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "This resource is not available for write access"); + } + + if (hr == S_OK) + { + // Initialize the resource context with ... + pResourceContext->m_strObjectID = ObjectID; + pResourceContext->m_Resource = Resource; + pResourceContext->m_BytesTransferred = 0; + pResourceContext->m_BytesTotal = GetResourceSize(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON); + } + + return hr; +} + +HRESULT FakeDeviceContent::ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) +{ + HRESULT hr = S_OK; + PBYTE pResource = NULL; + DWORD dwBytesToTransfer = 0; + + if ((pResourceContext == NULL) || + (pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + *pdwNumBytesRead = 0; + + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON); + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get the resource representing device icon data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (pResourceContext->m_BytesTotal >= pResourceContext->m_BytesTransferred) + { + dwBytesToTransfer = (DWORD)min((ULONGLONG)dwNumBytesToRead, (pResourceContext->m_BytesTotal - pResourceContext->m_BytesTransferred)); + + // Copy the embedded icon file data. + memcpy(pBuffer, pResource + pResourceContext->m_BytesTransferred, dwBytesToTransfer); + + // set the number of bytes actually read into to pBuffer + *pdwNumBytesRead = dwBytesToTransfer; + } + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/FakeDeviceContent.h b/wpd/WpdServiceSampleDriver/FakeDeviceContent.h new file mode 100644 index 00000000..7558a4f8 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeDeviceContent.h @@ -0,0 +1,95 @@ +#pragma once + +#define DEVICE_PROTOCOL_VALUE L"Contacts Services Sample Protocol ver 1.00" +#define DEVICE_FIRMWARE_VERSION_VALUE L"1.0.0.0" +#define DEVICE_POWER_LEVEL_VALUE 100 +#define DEVICE_MODEL_VALUE L"Contacts Service Device 2000" +#define DEVICE_FRIENDLY_NAME_VALUE L"Sample Device" +#define DEVICE_MANUFACTURER_VALUE L"Windows Portable Devices Group" +#define DEVICE_SERIAL_NUMBER_VALUE L"01234567890123-45676890123456" +#define DEVICE_SUPPORTS_NONCONSUMABLE_VALUE FALSE + +class FakeDeviceContent : public FakeContent +{ +public: + FakeDeviceContent() + { + ObjectID = WPD_DEVICE_OBJECT_ID; + PersistentUniqueID = WPD_DEVICE_OBJECT_ID; + ParentID = L""; + ParentPersistentUniqueID = L""; + ContainerFunctionalObjectID = L""; + Name = WPD_DEVICE_OBJECT_ID; + Protocol = DEVICE_PROTOCOL_VALUE; + FirmwareVersion = DEVICE_FIRMWARE_VERSION_VALUE; + Model = DEVICE_MODEL_VALUE; + Manufacturer = DEVICE_MANUFACTURER_VALUE; + FriendlyName = DEVICE_FRIENDLY_NAME_VALUE; + SerialNumber = DEVICE_SERIAL_NUMBER_VALUE; + PowerLevel = DEVICE_POWER_LEVEL_VALUE; + PowerSource = WPD_POWER_SOURCE_EXTERNAL; + DeviceType = WPD_DEVICE_TYPE_GENERIC; + Format = WPD_OBJECT_FORMAT_UNSPECIFIED; + ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + FunctionalCategory = WPD_FUNCTIONAL_CATEGORY_DEVICE; + RequiredScope = CONTACTS_SERVICE_ACCESS; + SupportsNonConsumable = DEVICE_SUPPORTS_NONCONSUMABLE_VALUE; + } + + virtual ~FakeDeviceContent() + { + } + + FakeDeviceContent(const FakeContent& src) + { + *this = src; + } + + virtual HRESULT FakeDeviceContent::InitializeContent( + _Inout_ DWORD *pdwLastObjectID); + + virtual HRESULT InitializeEnumerationContext( + ACCESS_SCOPE Scope, + _In_ WpdObjectEnumeratorContext* pEnumeratorContext); + + // Property Management + virtual HRESULT GetSupportedProperties( + _In_ IPortableDeviceKeyCollection* pKeys); + + virtual HRESULT GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore); + + // Resources + virtual HRESULT GetSupportedResources( + _In_ IPortableDeviceKeyCollection* pResources); + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Resource, + _In_ IPortableDeviceValues* pAttributes); + + virtual HRESULT OpenResource( + _In_ REFPROPERTYKEY Resource, + const DWORD dwMode, + _In_ WpdObjectResourceContext* pResourceContext); + + virtual HRESULT ReadResourceData( + _In_ WpdObjectResourceContext* pResourceContext, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + const DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead); + +public: + CAtlStringW Protocol; + CAtlStringW FirmwareVersion; + CAtlStringW Model; + CAtlStringW FriendlyName; + CAtlStringW SerialNumber; + CAtlStringW Manufacturer; + + GUID FunctionalCategory; + BOOL SupportsNonConsumable; + DWORD PowerLevel; + DWORD PowerSource; + DWORD DeviceType; +}; diff --git a/wpd/WpdServiceSampleDriver/FakeStorage.cpp b/wpd/WpdServiceSampleDriver/FakeStorage.cpp new file mode 100644 index 00000000..0d54e671 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeStorage.cpp @@ -0,0 +1,217 @@ +#include "stdafx.h" + +#include "FakeStorage.tmh" + +const PropertyAttributeInfo g_SupportedStorageProperties[] = +{ + {&WPD_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PERSISTENT_UNIQUE_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_PARENT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_NAME, VT_LPWSTR, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_FORMAT, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTENT_TYPE, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CAN_DELETE, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_TYPE, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_FILE_SYSTEM_TYPE, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_CAPACITY, VT_UI8, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_FREE_SPACE_IN_BYTES, VT_UI8, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_SERIAL_NUMBER, VT_LPWSTR, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, + {&WPD_STORAGE_DESCRIPTION, VT_LPWSTR, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, NULL}, + {&WPD_FUNCTIONAL_OBJECT_CATEGORY, VT_CLSID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, NULL}, +}; + +HRESULT FakeStorage::GetSupportedProperties( + _In_ IPortableDeviceKeyCollection *pKeys) +{ + HRESULT hr = S_OK; + + if (pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedStorageProperties); dwIndex++) + { + hr = pKeys->Add(*g_SupportedStorageProperties[dwIndex].pKey); + CHECK_HR(hr, "Failed to add storage property"); + } + + return hr; +} + +HRESULT FakeStorage::GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore) +{ + HRESULT hr = S_OK; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (IsEqualPropertyKey(Key, WPD_STORAGE_SERIAL_NUMBER)) + { + hr = pStore->SetStringValue(WPD_STORAGE_SERIAL_NUMBER, SerialNumber); + CHECK_HR(hr, "Failed to set WPD_STORAGE_SERIAL_NUMBER"); + } + else if (IsEqualPropertyKey(Key, WPD_STORAGE_FREE_SPACE_IN_BYTES)) + { + hr = pStore->SetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, FreeSpace); + CHECK_HR(hr, "Failed to set WPD_STORAGE_FREE_SPACE_IN_BYTES"); + } + else if (IsEqualPropertyKey(Key, WPD_STORAGE_CAPACITY)) + { + hr = pStore->SetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY, Capacity); + CHECK_HR(hr, "Failed to set WPD_STORAGE_CAPACITY"); + } + else if (IsEqualPropertyKey(Key, WPD_STORAGE_TYPE)) + { + hr = pStore->SetUnsignedIntegerValue(WPD_STORAGE_TYPE, StorageType); + CHECK_HR(hr, "Failed to set WPD_STORAGE_TYPE"); + } + else if (IsEqualPropertyKey(Key, WPD_STORAGE_FILE_SYSTEM_TYPE)) + { + hr = pStore->SetStringValue(WPD_STORAGE_FILE_SYSTEM_TYPE, FileSystemType); + CHECK_HR(hr, "Failed to set WPD_STORAGE_FILE_SYSTEM_TYPE"); + } + else if (IsEqualPropertyKey(Key, WPD_STORAGE_DESCRIPTION)) + { + hr = pStore->SetStringValue(WPD_STORAGE_DESCRIPTION, Description); + CHECK_HR(hr, "Failed to set WPD_STORAGE_DESCRIPTION"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_ID, ObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + hr = pStore->SetStringValue(WPD_OBJECT_NAME, Name); + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, PersistentUniqueID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_PARENT_ID, ParentID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT)) + { + hr = pStore->SetGuidValue(WPD_OBJECT_FORMAT, Format); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE)) + { + hr = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE)) + { + hr = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE"); + } + else if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY)) + { + hr = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, FunctionalCategory); + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + } + else if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID)) + { + hr = pStore->SetStringValue(WPD_OBJECT_CONTAINER_FUNCTIONAL_OBJECT_ID, ContainerFunctionalObjectID); + CHECK_HR(hr, "Failed to set WPD_STORAGE_DESCRIPTION"); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + CHECK_HR(hr, "Property {%ws}.%d is not supported", CComBSTR(Key.fmtid), Key.pid); + } + + return hr; +} + +HRESULT FakeStorage::WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value) +{ + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(Key, WPD_OBJECT_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + Name = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME because type was not VT_LPWSTR"); + } + } + else if(IsEqualPropertyKey(Key, WPD_STORAGE_DESCRIPTION)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + Description = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_STORAGE_DESCRIPTION because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_STORAGE_DESCRIPTION because type was not VT_LPWSTR"); + } + } + else + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(Key.fmtid), Key.pid, ObjectID); + } + + return hr; +} + + +HRESULT FakeStorage::GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + hr = SetPropertyAttributes(Key, &g_SupportedStorageProperties[0], ARRAYSIZE(g_SupportedStorageProperties), pAttributes); + CHECK_HR(hr, "Failed to set storage property attributes"); + + return hr; +} + diff --git a/wpd/WpdServiceSampleDriver/FakeStorage.h b/wpd/WpdServiceSampleDriver/FakeStorage.h new file mode 100644 index 00000000..bb83d683 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/FakeStorage.h @@ -0,0 +1,76 @@ +#pragma once + +/** + * This class represents an abstraction of a storage content object. + * Driver implementors should replace this with their own + * device I/O classes/libraries. + */ + +#define STORAGE_OBJECT_ID L"123ABC" +#define STORAGE_CAPACITY_VALUE 1024 * 1024 +#define STORAGE_FREE_SPACE_IN_BYTES_VALUE STORAGE_CAPACITY_VALUE +#define STORAGE_SERIAL_NUMBER_VALUE L"98765432109876-54321098765432" +#define STORAGE_OBJECT_NAME_VALUE L"Internal Memory" +#define STORAGE_FILE_SYSTEM_TYPE_VALUE L"FAT32" +#define STORAGE_DESCRIPTION_VALUE L"Phone Memory Storage System" +#define STORAGE_CONTAINER_FUNCTIONAL_OBJECT_ID WPD_DEVICE_OBJECT_ID +#define STORAGE_TYPE WPD_STORAGE_TYPE_FIXED_ROM + +class FakeStorage : public FakeContent +{ +public: + FakeStorage() + { + ObjectID = STORAGE_OBJECT_ID; + PersistentUniqueID = STORAGE_OBJECT_ID; + ParentID = WPD_DEVICE_OBJECT_ID; + Name = STORAGE_OBJECT_NAME_VALUE; + ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + Format = WPD_OBJECT_FORMAT_UNSPECIFIED; + FunctionalCategory = WPD_FUNCTIONAL_CATEGORY_STORAGE; + ContainerFunctionalObjectID = WPD_DEVICE_OBJECT_ID; + ParentPersistentUniqueID = WPD_DEVICE_OBJECT_ID; + + Description = STORAGE_DESCRIPTION_VALUE; + Capacity = STORAGE_CAPACITY_VALUE; + FreeSpace = STORAGE_CAPACITY_VALUE; + SerialNumber = STORAGE_SERIAL_NUMBER_VALUE; + FileSystemType = STORAGE_FILE_SYSTEM_TYPE_VALUE; + StorageType = STORAGE_TYPE; + } + + FakeStorage(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeStorage() + { + } + + virtual HRESULT GetSupportedProperties( + _In_ IPortableDeviceKeyCollection* pKeys); + + virtual HRESULT GetValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pStore); + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY Key, + _In_ REFPROPVARIANT Value); + + virtual HRESULT GetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pAttributes); + +public: + // Standard WPD properties + CAtlStringW Description; + CAtlStringW SerialNumber; + CAtlStringW FileSystemType; + + GUID FunctionalCategory; + ULONGLONG FreeSpace; + ULONGLONG Capacity; + DWORD StorageType; +}; diff --git a/wpd/WpdServiceSampleDriver/Queue.cpp b/wpd/WpdServiceSampleDriver/Queue.cpp new file mode 100644 index 00000000..77981b5f --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Queue.cpp @@ -0,0 +1,425 @@ +// Queue.cpp : Implementation of CQueue + +#include "stdafx.h" +#include "Queue.h" +#include <devioctl.h> + +#include "Queue.tmh" + +// Add table used to lookup the Access required for Wpd Commands +BEGIN_WPD_COMMAND_ACCESS_MAP(g_WpdCommandAccessMap) + DECLARE_WPD_STANDARD_COMMAND_ACCESS_ENTRIES + // Add any custom commands here e.g. + // WPD_COMMAND_ACCESS_ENTRY(MyCustomCommand, WPD_COMMAND_ACCESS_READWRITE) +END_WPD_COMMAND_ACCESS_MAP + +// This enables use to use VERIFY_WPD_COMMAND_ACCESS to check command access function for us. +DECLARE_VERIFY_WPD_COMMAND_ACCESS; + +/****************************************************************************** + * This function calls the WpdBaseDriver to handle the WPD message. In order + * to do this it does the following: + * + * - Deserializes pBuffer into an IPortableDeviceValues which holds the command + * input parameters from the WPD application. + * - Creates an IPortableDeviceValues for the results. + * - Calls the WpdBaseDriver to handle the message. (The results of this + * operation are put into the previously created results IPortableDeviceValues.) + * - The results IPortableDeviceValues is then serialized back into pBuffer, making + * sure that it does not overrun ulOutputBufferLength. + * + *****************************************************************************/ +HRESULT CQueue::ProcessWpdMessage( + ULONG ControlCode, + _In_ ContextMap* pClientContextMap, + _In_opt_ LPCWSTR pszFileName, + _In_ IWDFDevice* pDevice, + _In_reads_bytes_(ulInputBufferLength) PVOID pInBuffer, + ULONG ulInputBufferLength, + _Out_writes_bytes_to_(ulOutputBufferLength, *pdwBytesWritten) PVOID pOutBuffer, + ULONG ulOutputBufferLength, + _Out_ DWORD* pdwBytesWritten) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + CComPtr<WpdBaseDriver> pWpdBaseDriver; + + *pdwBytesWritten = 0; + + if (hr == S_OK) + { + hr = m_pWpdSerializer->GetIPortableDeviceValuesFromBuffer((BYTE*)pInBuffer, + ulInputBufferLength, + &pParams); + CHECK_HR(hr, "Failed to deserialize command parameters from input buffer"); + } + + // Verify that that command was sent with the appropriate access + if (hr == S_OK) + { + hr = VERIFY_WPD_COMMAND_ACCESS(ControlCode, pParams, g_WpdCommandAccessMap); + CHECK_HR(hr, "Wpd Command was sent with incorrect access flags"); + } + + // Create the WPD results collection + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Insert the client context map as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the context map. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, pClientContextMap); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Insert the IWDFDevice interface as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the WUDF Device object. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, pDevice); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT"); + } + + // Insert the IWpdSerializer interface as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the WPD Serializer object. + if (hr == S_OK) + { + hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, m_pWpdSerializer); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT"); + } + + // Insert the Service Object ID as one of this driver's private properties. This is + // just a convenient place holder which allows other methods down the chain to + // access the Service Object ID. + if ((hr == S_OK) && (pszFileName != NULL)) + { + hr = pParams->SetStringValue(PRIVATE_SAMPLE_DRIVER_REQUEST_FILENAME, pszFileName); + CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_REQUEST_FILENAME"); + } + + // Get the WpdBaseDriver so we can dispatch the message + if (hr == S_OK) + { + hr = GetWpdBaseDriver(pDevice, &pWpdBaseDriver); + CHECK_HR(hr, "Failed to get WpdBaseDriver"); + } + + if (hr == S_OK) + { + hr = pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, "Failed to handle WPD command"); + } + + if (hr == S_OK) + { + hr = m_pWpdSerializer->WriteIPortableDeviceValuesToBuffer(ulOutputBufferLength, + pResults, + (BYTE*)pOutBuffer, + pdwBytesWritten); + CHECK_HR(hr, "Failed to serialize results to output buffer"); + } + + return hr; +} + +/****************************************************************************** + * This method gets the WpdBaseDriver associated with the UMDF device object. + * The caller should Release *ppWpdBaseDriver when it is done. + * + * When this device was created, we assigned the WpdBaseDriver as the context. + * So, in order to retrieve the correct WpdBaseDriver for this device, we simply + * get the device context. + *****************************************************************************/ +HRESULT CQueue::GetWpdBaseDriver( + _In_ IWDFDevice* pDevice, + _Outptr_result_nullonfailure_ WpdBaseDriver** ppWpdBaseDriver) +{ + HRESULT hr = S_OK; + WpdBaseDriver* pContext = NULL; + + if((pDevice == NULL) || (ppWpdBaseDriver == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter for pDevice or ppWpdBaseDriver"); + return hr; + } + + *ppWpdBaseDriver = NULL; + + if(SUCCEEDED(hr)) + { + hr = pDevice->RetrieveContext((void**)&pContext); + if(SUCCEEDED(hr)) + { + if(pContext != NULL) + { + pContext->AddRef(); + *ppWpdBaseDriver = pContext; + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Device context is NULL"); + } + } + } + + return hr; +} + + +/****************************************************************************** + * This method gets the Filename associated with the Request object + * The caller should CoTaskMemFree *ppszFileName when it is done. + *****************************************************************************/ +HRESULT CQueue::GetFileName( + _In_ IWDFFile* pFileObject, + _Outptr_result_maybenull_ LPWSTR* ppszFileName) +{ + HRESULT hr = S_OK; + DWORD cchFileName = 0; + LPWSTR pszFileName = NULL; + + if((pFileObject == NULL) || (ppszFileName == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter for pFileObject or ppszFileName"); + return hr; + } + + *ppszFileName = NULL; + + if (hr == S_OK) + { + hr = pFileObject->RetrieveFileName(NULL, &cchFileName); + CHECK_HR(hr, "Failed to get the filename size from WDF File Object"); + + if (hr == S_OK && cchFileName > 1) + { + pszFileName = (LPWSTR)CoTaskMemAlloc(cchFileName * sizeof(WCHAR)); + if (pszFileName == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate memory to hold the filename"); + } + else + { + hr = pFileObject->RetrieveFileName(pszFileName, &cchFileName); + CHECK_HR(hr, "Failed to get the filename from WDF File Object"); + } + + // The expected filename contains '\' + ServiceObjectID + if (hr == S_OK && (pszFileName != NULL) && (cchFileName > 1)) + { + // Check that the leading character is '\' + if (pszFileName[0] == L'\\') + { + // Skip the leading \\ character + *ppszFileName = AtlAllocTaskWideString(pszFileName + 1); + if (*ppszFileName == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate memory to hold the result filename"); + } + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + } + } + } + + CoTaskMemFree(pszFileName); + + return hr; +} + + +// CQueue + +STDMETHODIMP_ (void) +CQueue::OnCreateFile( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ IWDFFile* pFileObject + ) +{ + UNREFERENCED_PARAMETER(pQueue); + // This critical section protects the section of code where we + // Create the serializer and results interfaces used in handling I/O messages. + // We only need to create them once, then we hang on to them for the lifetime of this + // queue object. + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + HRESULT hr = S_OK; + + // Create the WPD serializer + if ((hr == S_OK) && + (m_pWpdSerializer == NULL)) + { + hr = CoCreateInstance(CLSID_WpdSerializer, + NULL, + CLSCTX_INPROC_SERVER, + IID_IWpdSerializer, + (VOID**)&m_pWpdSerializer); + + CHECK_HR(hr, "Failed to CoCreate CLSID_WpdSerializer"); + } + + // Create the client context map and associate it with the File Object + // so we can obtain it on a per-client basis. + if (hr == S_OK) + { + ContextMap* pClientContextMap = new ContextMap(); + + if(pClientContextMap != NULL) + { + hr = pFileObject->AssignContext(this, (void*)pClientContextMap); + CHECK_HR(hr, "Failed to set client context map"); + + // Release the client context map if we cannot set it + // properly + if(FAILED(hr)) + { + pClientContextMap->Release(); + pClientContextMap = NULL; + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create client context map"); + } + } + + pRequest->Complete(hr); + return; +} + +STDMETHODIMP_ (void) +CQueue::OnDeviceIoControl( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes + ) +{ + UNREFERENCED_PARAMETER(InputBufferSizeInBytes); + UNREFERENCED_PARAMETER(OutputBufferSizeInBytes); + + HRESULT hr = S_OK; + DWORD dwBytesWritten = 0; + + if(IS_WPD_IOCTL(ControlCode)) + { + BYTE* pInputBuffer = NULL; + SIZE_T cbInputBuffer = 0; + BYTE* pOutputBuffer = NULL; + SIZE_T cbOutputBuffer = 0; + ContextMap* pClientContextMap = NULL; + LPWSTR pszFileName = NULL; + + CComPtr<IWDFMemory> pMemoryIn; + CComPtr<IWDFMemory> pMemoryOut; + CComPtr<IWDFDevice> pDevice; + CComPtr<IWDFFile> pFileObject; + + // + // Get input memory buffer, the memory object is always returned even if the + // underlying buffer is NULL + // + pRequest->GetInputMemory(&pMemoryIn); + pInputBuffer = (BYTE*) pMemoryIn->GetDataBuffer(&cbInputBuffer); + + // + // Get output memory buffer, the memory object is always returned even if the + // underlying buffer is NULL + // + pRequest->GetOutputMemory(&pMemoryOut); + pOutputBuffer = (BYTE*) pMemoryOut->GetDataBuffer(&cbOutputBuffer); + + // Get the Context map for this client + pRequest->GetFileObject(&pFileObject); + if (pFileObject != NULL) + { + hr = pFileObject->RetrieveContext((void**)&pClientContextMap); + CHECK_HR(hr, "Failed to get Contextmap from WDF File Object"); + + if (hr == S_OK) + { + hr = GetFileName(pFileObject, &pszFileName); + CHECK_HR(hr, "Failed to get Filename from WDF File Object"); + } + + if (hr == S_OK) + { + // Get the device object + pQueue->GetDevice(&pDevice); + hr = ProcessWpdMessage(ControlCode, + pClientContextMap, + pszFileName, + pDevice, + pInputBuffer, + (DWORD)cbInputBuffer, + pOutputBuffer, + (DWORD)cbOutputBuffer, + &dwBytesWritten); + } + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "WDF File Object is NULL"); + } + + CoTaskMemFree(pszFileName); + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Received invalid/unsupported IOCTL code '0x%lx'",ControlCode); + } + + // Complete the request + if (hr == S_OK) + { + pRequest->CompleteWithInformation(hr, dwBytesWritten); + } + else + { + pRequest->Complete(hr); + } + + return; +} + +STDMETHODIMP_ (void) +CQueue::OnCleanup( + _In_ IWDFObject* pWdfObject + ) +{ + // Destroy the client context map + HRESULT hr = S_OK; + ContextMap* pClientContextMap = NULL; + + hr = pWdfObject->RetrieveContext((void**)&pClientContextMap); + if((hr == S_OK) && (pClientContextMap != NULL)) + { + pClientContextMap->Release(); + pClientContextMap = NULL; + } +} + diff --git a/wpd/WpdServiceSampleDriver/Queue.h b/wpd/WpdServiceSampleDriver/Queue.h new file mode 100644 index 00000000..dceb3e29 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Queue.h @@ -0,0 +1,98 @@ +// Queue.h : Declaration of the CQueue + +#pragma once +#include "resource.h" // main symbols +#include "WpdServiceSampleDriver.h" + +class ATL_NO_VTABLE CQueue : + public CComObjectRootEx<CComMultiThreadModel>, + public IQueueCallbackDeviceIoControl, + public IQueueCallbackCreate, + public IObjectCleanup +{ +public: + CQueue() + { + + } + + DECLARE_NOT_AGGREGATABLE(CQueue) + + BEGIN_COM_MAP(CQueue) + COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl) + COM_INTERFACE_ENTRY(IQueueCallbackCreate) + END_COM_MAP() + +public: + static + HRESULT CreateInstance( + _COM_Outptr_ IUnknown** ppUkwn) + { + *ppUkwn = NULL; + CComObject< CQueue> *pMyQueue = NULL; + HRESULT hr = CComObject<CQueue>::CreateInstance( &pMyQueue ); + if( SUCCEEDED (hr) ) + { + pMyQueue->AddRef(); + hr = pMyQueue->QueryInterface( __uuidof(IUnknown), (void **) ppUkwn ); + pMyQueue->Release(); + pMyQueue = NULL; + } + + return hr; + } + + // + // Wdf Callbacks + // + + // IQueueCallbackCreateClose + // + STDMETHOD_ (void, OnCreateFile)( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + _In_ IWDFFile* pFileObject + ); + + // + // IQueueCallbackDeviceIoControl + // + STDMETHOD_ (void, OnDeviceIoControl)( + _In_ IWDFIoQueue* pQueue, + _In_ IWDFIoRequest* pRequest, + ULONG ControlCode, + SIZE_T InputBufferSizeInBytes, + SIZE_T OutputBufferSizeInBytes + ); + + // + // IObjectCleanup + // + STDMETHOD_ (void, OnCleanup)( + _In_ IWDFObject* pWdfObject + ); + +private: + HRESULT ProcessWpdMessage( + ULONG ControlCode, + _In_ ContextMap* pClientContextMap, + _In_opt_ LPCWSTR pszFileName, + _In_ IWDFDevice* pDevice, + _In_reads_bytes_(ulInputBufferLength) PVOID pInBuffer, + ULONG ulInputBufferLength, + _Out_writes_bytes_to_(ulOutputBufferLength, *pdwBytesWritten) PVOID pOutBuffer, + ULONG ulOutputBufferLength, + _Out_ DWORD* pdwBytesWritten); + + HRESULT GetWpdBaseDriver( + _In_ IWDFDevice* pDevice, + _Outptr_result_nullonfailure_ WpdBaseDriver** ppWpdBaseDriver); + + HRESULT GetFileName( + _In_ IWDFFile* pFileObject, + _Outptr_result_maybenull_ LPWSTR* ppszFilename); + + CComPtr<IWpdSerializer> m_pWpdSerializer; + CComAutoCriticalSection m_CriticalSection; +}; + diff --git a/wpd/WpdServiceSampleDriver/ReadMe.md b/wpd/WpdServiceSampleDriver/ReadMe.md new file mode 100644 index 00000000..d9ed7246 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/ReadMe.md @@ -0,0 +1,20 @@ +WPD service sample driver +========================= + +The WpdServiceSampleDriver shows how to extend the WpdHelloWorldDriver sample so that it supports a simulated device with a Contacts device service. By using this device service, an application can discover events, methods, and properties that operate on Contacts that are stored on the device. And, the application can use the Contacts device service to handle these events, invoke these methods, or retrieve these properties. For example, the application might invoke methods to synchronize the Contacts that are found on the device with the contacts that are stored on a computer or to read the Name property for a given Contact. + +A device service is an extension of a functional object. In addition to logically grouping device capabilities, a device service provides applications that can programmatically discover those capabilities. + +> **Note**Â Â This driver was written in the simplest way to demonstrate concepts. Therefore, the sample driver might perform operations or be structured in a way that are inefficient in a production driver. Additionally, this sample does not use real hardware. Instead, it simulates a device by using data structures in memory. Therefore the driver might be implemented in a way that is unrealistic for production hardware. + +For a complete description of this sample and its underlying code and functionality, refer to the [WPD Service Sample Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597714) description in the Windows Driver Kit documentation. + + +Related topics +-------------- + +[WPD Design Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597864) + +[WPD Driver Development Tools](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597568) + +[WPD Programming Guide](http://msdn.microsoft.com/en-us/library/windows/hardware/) diff --git a/wpd/WpdServiceSampleDriver/SampleContactsServiceIcon.ico b/wpd/WpdServiceSampleDriver/SampleContactsServiceIcon.ico Binary files differnew file mode 100644 index 00000000..28648294 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/SampleContactsServiceIcon.ico diff --git a/wpd/WpdServiceSampleDriver/SampleDeviceIcon.ico b/wpd/WpdServiceSampleDriver/SampleDeviceIcon.ico Binary files differnew file mode 100644 index 00000000..33a1d1a5 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/SampleDeviceIcon.ico diff --git a/wpd/WpdServiceSampleDriver/Stdafxsrc.cpp b/wpd/WpdServiceSampleDriver/Stdafxsrc.cpp new file mode 100644 index 00000000..5105a28d --- /dev/null +++ b/wpd/WpdServiceSampleDriver/Stdafxsrc.cpp @@ -0,0 +1 @@ +#include "Stdafx.h"
\ No newline at end of file diff --git a/wpd/WpdServiceSampleDriver/WpdBaseDriver.cpp b/wpd/WpdServiceSampleDriver/WpdBaseDriver.cpp new file mode 100644 index 00000000..adb9d3b3 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdBaseDriver.cpp @@ -0,0 +1,393 @@ +#include "stdafx.h" + +#include "WpdBaseDriver.tmh" + +WpdBaseDriver::WpdBaseDriver() : + m_cRef(1) +{ +} + +WpdBaseDriver::~WpdBaseDriver() +{ + +} + +ULONG __stdcall WpdBaseDriver::AddRef() +{ + InterlockedIncrement((long*) &m_cRef); + return m_cRef; +} + +_At_(this, __drv_freesMem(Mem)) +ULONG __stdcall WpdBaseDriver::Release() +{ + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; +} + +HRESULT __stdcall WpdBaseDriver::QueryInterface( + REFIID riid, + void** ppv) +{ + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + return hr; +} + +/** + * This method is called to initialize the driver object. + * This is where the driver would set up it's I/O libraries + * and so on. + */ +HRESULT WpdBaseDriver::Initialize() +{ + HRESULT hr = m_Device.InitializeContent(); + CHECK_HR(hr, ("Failed to initialize content")); + + if (hr == S_OK) + { + hr = m_Service.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdService")); + } + + if (hr == S_OK) + { + hr = m_ObjectEnum.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdObjectEnum")); + } + + if (hr == S_OK) + { + m_Capabilities.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdCapabilities")); + } + + if (hr == S_OK) + { + m_ObjectManagement.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdObjectManagement")); + } + + if (hr == S_OK) + { + m_ObjectProperties.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdObjectProperties")); + } + + if (hr == S_OK) + { + m_ObjectResources.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdObjectResources")); + } + + if (hr == S_OK) + { + m_ObjectPropertiesBulk.Initialize(&m_Device); + CHECK_HR(hr, ("Failed to initialize WpdObjectPropertiesBulk")); + } + + return hr; +} + +/** + * This method is called to uninitialize the driver object. + * In a real driver, this is where the driver would clean up + * any resources held by this driver. + */ +VOID WpdBaseDriver::Uninitialize() +{ +} + +HRESULT WpdBaseDriver::DispatchWpdMessage(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + GUID guidCommandCategory = {0}; + DWORD dwCommandID = 0; + PROPERTYKEY CommandKey = WPD_PROPERTY_NULL; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, &guidCommandCategory); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_CATEGORY from input parameters"); + } + + if (hr == S_OK) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, &dwCommandID); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_ID from input parameters"); + } + + // If WPD_PROPERTY_COMMON_COMMAND_CATEGORY or WPD_PROPERTY_COMMON_COMMAND_ID could not be extracted + // properly then we should return E_INVALIDARG to the client. + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_COMMAND_CATEGORY or WPD_PROPERTY_COMMON_COMMAND_ID from input parameters"); + } + + if (hr == S_OK) + { + CommandKey.fmtid = guidCommandCategory; + CommandKey.pid = dwCommandID; + + if (CommandKey.fmtid == WPD_CATEGORY_OBJECT_ENUMERATION) + { + hr = m_ObjectEnum.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_OBJECT_PROPERTIES) + { + hr = m_ObjectProperties.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_OBJECT_RESOURCES) + { + hr = m_ObjectResources.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_CAPABILITIES) + { + hr = m_Capabilities.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if(CommandKey.fmtid == WPD_CATEGORY_OBJECT_MANAGEMENT) + { + hr = m_ObjectManagement.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if(CommandKey.fmtid == WPD_CATEGORY_OBJECT_PROPERTIES_BULK) + { + hr = m_ObjectPropertiesBulk.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (IsEqualPropertyKey(CommandKey, WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS)) + { + hr = OnGetObjectIDsFromPersistentUniqueIDs(pParams, pResults); + } + else if(IsEqualPropertyKey(CommandKey, WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION)) + { + hr = OnSaveClientInfo(pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_SERVICE_COMMON || + CommandKey.fmtid == WPD_CATEGORY_SERVICE_METHODS || + CommandKey.fmtid == WPD_CATEGORY_SERVICE_CAPABILITIES) + { + hr = m_Service.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "Unknown command %ws.%d received",CComBSTR(CommandKey.fmtid), CommandKey.pid); + } + } + + HRESULT hrTemp = pResults->SetErrorValue(WPD_PROPERTY_COMMON_HRESULT, hr); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_COMMON_HRESULT")); + + // Set to a success code, to indicate that the message was received. + // the return code for the actual command's results is stored in the + // WPD_PROPERTY_COMMON_HRESULT property. + hr = S_OK; + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS: Contains an IPortableDevicePropVariantCollection of VT_LPWSTR, + * indicating the PersistentUniqueIDs. + * + * The driver should: + * - Iterate through the PersistentUniqueIDs, and convert to a currently valid object id. + * This object ID list should be returned as an IPortableDevicePropVariantCollection of VT_LPWSTR + * in WPD_PROPERTY_COMMON_OBJECT_IDS. + * Order is implicit, i.e. the first element in the Persistent Unique ID list corresponds to the + * to the first element of the ObjectID list and so on. + * + * For those elements where an existing ObjectID could not be found (e.g. the + * object is no longer present on the device), the element will contain the + * empty string (L""). + */ +HRESULT WpdBaseDriver::OnGetObjectIDsFromPersistentUniqueIDs( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pPersistentIDs; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + + if((pParams == NULL) || + (pResults == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Get the list of Persistent IDs + if (hr == S_OK) + { + hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS, &pPersistentIDs); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_PERSISTENT_UNIQUE_IDS"); + } + + // Create the collection to hold the ObjectIDs + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDs); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_Device.GetAccessScope(pParams); + hr = m_Device.GetObjectIDsFromPersistentUniqueIDs(Scope, pPersistentIDs, pObjectIDs); + CHECK_HR(hr, "Failed to get object IDs from persistent IDs"); + } + + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_COMMON_OBJECT_IDS, pObjectIDs); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_OBJECT_IDS"); + } + + return hr; +} + + +/** + * This method is called when we receive a WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_COMMON_CLIENT_INFORMATION: Contains information about the client, including version + * and optionally the client event cookie. + * + * The driver should: + * - Save the client information and return an LPWSTR context for this client. + * The client can be identified using this context for subsequent commands to the driver. + */ +HRESULT WpdBaseDriver::OnSaveClientInfo( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + ClientContext* pContext = NULL; + ContextMap* pContextMap = NULL; + + CComPtr<IPortableDeviceValues> pClientInfo; + + if((pParams == NULL) || + (pResults == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = CoCreateGuid(&guidContext); + if (hr == S_OK) + { + bstrContext = guidContext; + if(bstrContext.Length() == 0) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create BSTR from GUID"); + } + } + + // Get the client info + if (hr == S_OK) + { + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_COMMON_CLIENT_INFORMATION, &pClientInfo); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_COMMON_CLIENT_INFORMATION"); + } + + // Get the context map which the driver stored in pParams for convenience + if (hr == S_OK) + { + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, "Failed to get client context map"); + } + + // Create the new client info context we will save in the context map + if (hr == S_OK) + { + pContext = new ClientContext(); + if(pContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, ("Could not allocate memory for client info context")); + } + } + + // Save the client info. Since these are optional, none of this is fatal if + // they don't exist. + if (hr == S_OK) + { + LPWSTR pszClientName = NULL; + LPWSTR pszEventCookie = NULL; + + pClientInfo->GetStringValue(WPD_CLIENT_NAME, &pszClientName); + if(pszClientName != NULL) + { + pContext->ClientName = pszClientName; + } + pClientInfo->GetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, &(pContext->MajorVersion)); + pClientInfo->GetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, &(pContext->MinorVersion)); + pClientInfo->GetUnsignedIntegerValue(WPD_CLIENT_REVISION, &(pContext->Revision)); + + pClientInfo->GetStringValue(WPD_CLIENT_EVENT_COOKIE, &pszEventCookie); + if (pszEventCookie != NULL) + { + pContext->EventCookie = pszEventCookie; + } + + CoTaskMemFree(pszClientName); + CoTaskMemFree(pszEventCookie); + } + + if ((hr == S_OK) && + (pContext->ClientName.GetLength() > 0) && + (pContextMap != NULL)) + { + CAtlStringW strContext; + hr = pContextMap->Add(pContext, strContext); + CHECK_HR(hr, "Failed to add client info context to context map"); + + if (hr == S_OK) + { + hr = pResults->SetStringValue(WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT, strContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT"); + } + } + + SAFE_RELEASE(pContext); + SAFE_RELEASE(pContextMap); + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdBaseDriver.h b/wpd/WpdServiceSampleDriver/WpdBaseDriver.h new file mode 100644 index 00000000..3e731f52 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdBaseDriver.h @@ -0,0 +1,45 @@ +#pragma once + +class WpdBaseDriver : + public IUnknown +{ +public: + WpdBaseDriver(); + virtual ~WpdBaseDriver(); + + HRESULT Initialize(); + VOID Uninitialize(); + + HRESULT DispatchWpdMessage(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT OnGetObjectIDsFromPersistentUniqueIDs(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + + HRESULT OnSaveClientInfo(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +public: // IUnknown + ULONG __stdcall AddRef(); + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release(); + + HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); + +public: + WpdObjectEnumerator m_ObjectEnum; + WpdObjectManagement m_ObjectManagement; + WpdObjectProperties m_ObjectProperties; + WpdObjectResources m_ObjectResources; + WpdObjectPropertiesBulk m_ObjectPropertiesBulk; + WpdCapabilities m_Capabilities; + WpdService m_Service; + +private: + FakeDevice m_Device; + ULONG m_cRef; +}; + diff --git a/wpd/WpdServiceSampleDriver/WpdCapabilities.cpp b/wpd/WpdServiceSampleDriver/WpdCapabilities.cpp new file mode 100644 index 00000000..0e5055bc --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdCapabilities.cpp @@ -0,0 +1,674 @@ +#include "stdafx.h" + +#include "WpdCapabilities.tmh" + +WpdCapabilities::WpdCapabilities() : m_pDevice(NULL) +{ + +} + +WpdCapabilities::~WpdCapabilities() +{ + +} + +HRESULT WpdCapabilities::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pDevice = pDevice; + return S_OK; +} + +HRESULT WpdCapabilities::DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_CAPABILITIES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS)) + { + hr = OnGetSupportedCommands(pParams, pResults); + CHECK_HR(hr, "Failed to get supported commands"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS)) + { + hr = OnGetCommandOptions(pParams, pResults); + CHECK_HR(hr, "Failed to get command options"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES)) + { + hr = OnGetFunctionalCategories(pParams, pResults); + CHECK_HR(hr, "Failed to get functional categories"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS)) + { + hr = OnGetFunctionalObjects(pParams, pResults); + CHECK_HR(hr, "Failed to get functional objects"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES)) + { + hr = OnGetSupportedContentTypes(pParams, pResults); + CHECK_HR(hr, "Failed to get supported content types"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS)) + { + hr = OnGetSupportedFormats(pParams, pResults); + CHECK_HR(hr, "Failed to get supported formats"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES)) + { + hr = OnGetSupportedFormatProperties(pParams, pResults); + CHECK_HR(hr, "Failed to get supported format properties"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES)) + { + hr = OnGetFixedPropertyAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get fixed property attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS)) + { + hr = OnGetSupportedEvents(pParams, pResults); + CHECK_HR(hr, "Failed to get supported events"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS)) + { + hr = OnGetEventOptions(pParams, pResults); + CHECK_HR(hr, "Failed to get event options"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all commands supported by this driver as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS. + * This includes custom commands, if any. + * + * Note that certain commands require a "command target" to function correctly. + * (e.g. delete object command) It is understood that not all objects are necessarily + * valid targets (e.g. you cannot delete the device object). + */ +HRESULT WpdCapabilities::OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pCommands; + + // CoCreate a collection to store the supported commands. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pCommands); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add the supported commands to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedCommands(pCommands); + CHECK_HR(hr, "Failed to get the supported commands"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS, pCommands); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_COMMAND: a collection of property keys containing a single value, + * which identifies the specific command options are requested to return. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, containing + * the relevant options. If no options are available for this command, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdCapabilities::OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + PROPERTYKEY Command = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pOptions; + + // Get the command whose options have been requested + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_COMMAND, &Command); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_COMMAND"); + } + + // CoCreate a collection to store the command options. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add command options to the collection + if (hr == S_OK) + { + hr = m_pDevice->GetCommandOptions(Command, pOptions); + CHECK_HR(hr, "Failed to get the command options"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, pOptions); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in + * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, containing + * the supported functional categories for this device. + */ +HRESULT WpdCapabilities::OnGetFunctionalCategories( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pFunctionalCategories; + + UNREFERENCED_PARAMETER(pParams); + + // CoCreate a collection to store the supported functional categories. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalCategories); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported functional categories to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedFunctionalCategories(pFunctionalCategories); + CHECK_HR(hr, "Failed to get the supported functional categories"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, pFunctionalCategories); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS + * command. It is sent when the caller is interesting in finding the object IDs for all + * functional objects belonging to the specified functional category. + * Note: the number of functional objects is expected to be very small (less than 8 for the + * whole device). + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY - a GUID value containing the category + * the caller is looking for. If the value is WPD_FUNCTIONAL_CATEGORY_ALL, then the driver + * must return all functional objects, no matter which category they belong to. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_LPWSTR) in + * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, containing + * the ids of the functional objects who belong to the specified functional category. + * If there are no objects in the specified category, the driver should return an + * empty collection. + */ +HRESULT WpdCapabilities::OnGetFunctionalObjects( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidFunctionalCategory = GUID_NULL; + CComPtr<IPortableDevicePropVariantCollection> pFunctionalObjects; + + // Get the functional category whose functional object identifiers have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"); + } + + // CoCreate a collection to store the supported functional object identifiers. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalObjects); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported functional object identifiers for the specified functional + // category to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetFunctionalObjects(guidFunctionalCategory, pFunctionalObjects); + CHECK_HR(hr, "Failed to get the functional objects"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, pFunctionalObjects); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES + * command. This message is sent when the client needs to know the possible content types supported + * by the specified functional category. + * If the driver has multiple functional objects that may support different content types, + * the driver should simply merge them together and report all possible types in one list here. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY - a GUID value containing the functional category + * whose content types the caller is interested in. If the value is WPD_FUNCTIONAL_CATEGORY_ALL, then the driver + * must return a list of all content types supported by the device. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in + * WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, containing + * the content types supported by the specified functional category. + * If there are no objects in the specified category, the driver should return an + * empty collection. + */ +HRESULT WpdCapabilities::OnGetSupportedContentTypes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidFunctionalCategory = GUID_NULL; + CComPtr<IPortableDevicePropVariantCollection> pContentTypes; + + // Get the functional category whose supported content types have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"); + } + + // CoCreate a collection to store the supported content types. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pContentTypes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported content types for the specified functional + // category to the collection. + // Note that the contacts service does not support any content types + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedContentTypes(guidFunctionalCategory, pContentTypes); + CHECK_HR(hr, "Failed to get the content types"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, pContentTypes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS + * command. This message is sent when the client needs to know the possible formats supported + * by the specified content type (e.g. for image objects, the driver may choose to support JPEG and BMP files). + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE - a GUID value containing the content type + * whose formats the caller is interested in. If the value is WPD_CONTENT_TYPE_ALL, then the driver + * must return a list of all formats supported by the device. + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in + * WPD_PROPERTY_CAPABILITIES_FORMATS, indicating the formats supported by the + * specified content type. + * If there are no formats supported by the specified content type, the driver should return an + * empty collection. + */ +HRESULT WpdCapabilities::OnGetSupportedFormats( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidContentType = GUID_NULL; + CComPtr<IPortableDevicePropVariantCollection> pFormats; + + // Get the content type whose supported formats have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE, &guidContentType); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE"); + } + + // CoCreate a collection to store the supported formats. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFormats); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported formats for the specified content type to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedFormats(guidContentType, pFormats); + CHECK_HR(hr, "Failed to get the content types"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_FORMATS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FORMATS, pFormats); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FORMATS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES + * command. This message is sent when the client needs to know the typical properties for objects of + * a given format. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_FORMAT - a GUID value specifying the format the caller is interested in. + * + * The driver should: + * - Return an IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, + * containing the property keys. + */ +HRESULT WpdCapabilities::OnGetSupportedFormatProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidObjectFormat = GUID_NULL; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // Get the object format whose supported properties have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT"); + } + + // CoCreate a collection to store the supported properties. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add the supported properties for the specified object format to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedFormatProperties(guidObjectFormat, pKeys); + CHECK_HR(hr, "Failed to get supported properties for a format"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES + * command. This message is sent when the client needs to know the property attributes that + * are the same for all objects of the given format. + * + * Typically, a driver treats objects of a given format the same. Many properties therefore will + * have attributes that are identical across all objects of that format. + * These can be returned here. There are some attributes which may be differ per object instance, + * which are not returned here. + * See WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_FORMAT - a GUID value specifying the format the caller is interested in. + * - WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS - a collection of property keys containing a single value, + * which is the key identifying the specific property attributes we are requested to return. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES + * containing the fixed property attributes. + */ +HRESULT WpdCapabilities::OnGetFixedPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID guidObjectFormat = GUID_NULL; + PROPERTYKEY key = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pAttributes; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object format whose fixed property attributes have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT"); + } + + // Get the property whose fixed property attributes have been requested + if(hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, &key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS"); + } + + // CoCreate a collection to store the fixed property attributes. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add the fixed property attributes for the specified object format and property + if (hr == S_OK) + { + hr = m_pDevice->GetFixedPropertyAttributes(guidObjectFormat, key, pAttributes); + CHECK_HR(hr, "Failed to get fixed property attributes"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all events supported by this driver should be returned as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS. + * That includes custom commands, if any. + */ +HRESULT WpdCapabilities::OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pEvents; + UNREFERENCED_PARAMETER(pParams); + + // CoCreate a collection to store the supported events. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pEvents); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Add the supported events to the collection. + if (hr == S_OK) + { + hr = m_pDevice->GetSupportedEvents(pEvents); + CHECK_HR(hr, "Failed to get supported events"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS, pEvents); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_CAPABILITIES_EVENT: a GUID value indicating the Event whose options should be returned. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, containing + * the relevant options. + */ +HRESULT WpdCapabilities::OnGetEventOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Event = GUID_NULL; + CComPtr<IPortableDeviceValues> pOptions; + + // Get the event whose options have been requested + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_EVENT, &Event); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_EVENT"); + } + + // CoCreate a collection to store the event options. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add event options to the collection + if (hr == S_OK) + { + hr = m_pDevice->GetEventOptions(pOptions); + CHECK_HR(hr, "Failed to get event options"); + } + + // Set the WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, pOptions); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS"); + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdCapabilities.h b/wpd/WpdServiceSampleDriver/WpdCapabilities.h new file mode 100644 index 00000000..2f13d3d9 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdCapabilities.h @@ -0,0 +1,59 @@ +#pragma once + +class WpdCapabilities +{ +public: + WpdCapabilities(); + virtual ~WpdCapabilities(); + + HRESULT Initialize(_In_ FakeDevice* pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFunctionalCategories( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFunctionalObjects( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedContentTypes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedFormats( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedFormatProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFixedPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetEventOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeDevice* m_pDevice; +}; + diff --git a/wpd/WpdServiceSampleDriver/WpdObjectEnum.cpp b/wpd/WpdServiceSampleDriver/WpdObjectEnum.cpp new file mode 100644 index 00000000..58f5faba --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectEnum.cpp @@ -0,0 +1,311 @@ +#include "stdafx.h" + +#include "WpdObjectEnum.tmh" + +WpdObjectEnumerator::WpdObjectEnumerator() : m_pDevice(NULL) +{ + +} + +WpdObjectEnumerator::~WpdObjectEnumerator() +{ + +} + +HRESULT WpdObjectEnumerator::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pDevice = pDevice; + return S_OK; +} + +HRESULT WpdObjectEnumerator::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_OBJECT_ENUMERATION) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + } + + if (hr == S_OK) + { + if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_START_FIND.pid) + { + hr = OnStartFind(pParams, pResults); + CHECK_HR(hr, "Failed to begin enumeration"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT.pid) + { + hr = OnFindNext(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to find next object"); + } + } + else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_END_FIND.pid) + { + hr = OnEndFind(pParams, pResults); + CHECK_HR(hr, "Failed to end enumeration"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_START_FIND + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID: the parent where we should start + * the enumeration. + * - WPD_PROPERTY_OBJECT_ENUMERATION_FILTER: the filter to use when doing + * enumeration. Since this parameter is optional, it may not exist. + * This driver currently ignores the filter parameter. + * + * The driver should: + * - Create a new context for this enumeration. + * - Set the string identifier in WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT for the newly created enumeration context. + * This value will be passed back during OnFindNext and OnEndFind. + */ +HRESULT WpdObjectEnumerator::OnStartFind( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszParentID = NULL; + ContextMap* pContextMap = NULL; + CAtlStringW strEnumContext; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier of the parent where the enumeration is starting from. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID, &wszParentID); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID"); + } + + // Get the client context map so we can store an enumeration context for this enumeration + // operation. + if (SUCCEEDED(hr)) + { + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, "Failed to get client context map"); + } + + // Create and initialize a new enumeration context. + // Add the new enumertion context to the client context map. This context is used to + // keep track of this particular enumeration operation. + if (SUCCEEDED(hr)) + { + WpdObjectEnumeratorContext* pEnumeratorContext = new WpdObjectEnumeratorContext(); + + if (pEnumeratorContext != NULL) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + m_pDevice->InitializeEnumerationContext(Scope, wszParentID, pEnumeratorContext); + + // Add the enumeration context to the client context map. This calls AddRef() on pEnumeratorContext + hr = pContextMap->Add(pEnumeratorContext, strEnumContext); + CHECK_HR(hr, "Failed to add the enumeration context"); + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate enumeration context"); + } + + SAFE_RELEASE(pEnumeratorContext); + } + + // Set the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT value in the results. + // This context identifier will be passed back during OnFindNext and OnEndFind to allow the driver to access it. + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, strEnumContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszParentID); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectEnumerator::OnFindNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszEnumContext = NULL; + DWORD dwNumObjectsRequested = 0; + DWORD dwNumObjectsEnumerated = 0; + WpdObjectEnumeratorContext* pEnumeratorContext = NULL; + + CComPtr<IPortableDevicePropVariantCollection> pObjectIDCollection; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the enumeration context identifier for this enumeration operation. + // The enumeration context identifier is needed to lookup the specific + // enumeration context in the client context map for this enumeration operation. + // NOTE that more than one enumeration may be in progress. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Get the number of objects requested for this enumeration call. + // The driver should always attempt to meet this requested value. + // If there are fewer children than requested, the driver should return the remaining + // children and a return code of S_FALSE. + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED, &dwNumObjectsRequested); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED"); + } + } + + // Get the enumeration context for this enumeration operation. + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, wszEnumContext, (IUnknown**)&pEnumeratorContext); + CHECK_HR(hr, "Failed to get the enumeration context"); + } + + // CoCreate a collection to store the object identifiers being returned for this enumeration call. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDCollection); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (SUCCEEDED(hr)) + { + hr = m_pDevice->FindNext(dwNumObjectsRequested, pEnumeratorContext, pObjectIDCollection, &dwNumObjectsEnumerated); + CHECK_HR(hr, "Failed to get the next object"); + } + + // Set the collection of object identifiers enumerated in the results + if (SUCCEEDED(hr)) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS, pObjectIDCollection); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS"); + } + + // If the enumeration context reports that their are no more objects to return then return S_FALSE indicating to the + // caller that we are finished. + if (SUCCEEDED(hr)) + { + // Update the number of children we have enumerated and returned to the caller + pEnumeratorContext->m_ChildrenEnumerated += dwNumObjectsEnumerated; + + // Check the number requested against the number enumerated and set the HRESULT + // accordingly. + if (dwNumObjectsEnumerated < dwNumObjectsRequested) + { + // We returned less than the number of objects requested to the caller + hr = S_FALSE; + } + else + { + // We returned exactly the number of objects requested to the caller + hr = S_OK; + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszEnumContext); + + SAFE_RELEASE(pEnumeratorContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_END_FIND + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT: the context the driver returned to + * the client in OnStartFind. + * + * The driver should: + * - Destroy any data associated with this context. + */ +HRESULT WpdObjectEnumerator::OnEndFind( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszEnumContext = NULL; + ContextMap* pContextMap = NULL; + + UNREFERENCED_PARAMETER(pResults); + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the enumeration context identifier for this enumeration operation. We will + // need this to lookup the specific enumeration context in the client context map. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Get the client context map so we can retrieve the enumeration context for this enumeration + // operation using the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT property value obtained above. + if (SUCCEEDED(hr)) + { + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, "Failed to get the client context map"); + } + + // Destroy any data allocated/associated with the enumeration context and then remove it from the context map. + // We no longer need to keep this context around because the enumeration has been ended. + if (SUCCEEDED(hr)) + { + pContextMap->Remove(wszEnumContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszEnumContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + diff --git a/wpd/WpdServiceSampleDriver/WpdObjectEnum.h b/wpd/WpdServiceSampleDriver/WpdObjectEnum.h new file mode 100644 index 00000000..2b140242 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectEnum.h @@ -0,0 +1,105 @@ +#pragma once + +// This class is used to store the context for a specific enumeration. +class WpdObjectEnumeratorContext : public IUnknown +{ +public: + WpdObjectEnumeratorContext() : + m_cRef(1), + m_TotalChildren(0), + m_ChildrenEnumerated(0), + m_Scope(FULL_DEVICE_ACCESS) + { + + } + + ~WpdObjectEnumeratorContext() + { + + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +private: + DWORD m_cRef; + +public: + bool HasMoreChildrenToEnumerate() + { + return ((m_TotalChildren - m_ChildrenEnumerated) > 0); + } + +// WpdObjectEnumeratorContext specific data +public: + ACCESS_SCOPE m_Scope; + CAtlStringW m_strParentObjectID; // object identifier of the object whose children are being enumerated + DWORD m_TotalChildren; // number of bytes transferred from the resource to the caller + DWORD m_ChildrenEnumerated; // number of children returned during the enumeration operation +}; + +class WpdObjectEnumerator +{ +public: + WpdObjectEnumerator(); + virtual ~WpdObjectEnumerator(); + + HRESULT Initialize(_In_ FakeDevice* pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnStartFind( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnFindNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnEndFind( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeDevice* m_pDevice; +}; diff --git a/wpd/WpdServiceSampleDriver/WpdObjectManagement.cpp b/wpd/WpdServiceSampleDriver/WpdObjectManagement.cpp new file mode 100644 index 00000000..932735ae --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectManagement.cpp @@ -0,0 +1,297 @@ +#include "stdafx.h" + +#include "WpdObjectManagement.tmh" + +WpdObjectManagement::WpdObjectManagement() : m_pDevice(NULL) +{ + +} + +WpdObjectManagement::~WpdObjectManagement() +{ + +} + +HRESULT WpdObjectManagement::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pDevice = pDevice; + return S_OK; +} + +HRESULT WpdObjectManagement::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_OBJECT_MANAGEMENT) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + } + + if (hr == S_OK) + { + if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY.pid) + { + hr = OnCreateObjectWithPropertiesOnly(pParams, pResults); + CHECK_HR(hr, "Failed to create object"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS.pid) + { + hr = OnDelete(pParams, pResults); + CHECK_HR(hr, "Failed to delete object"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + + return hr; +} + + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES: Contains an IPortableDeviceValues, describing + * properties of the new object. At the very least, it will contain: + * - WPD_OBJECT_NAME: The object name. + * - WPD_PARENT_ID: Identifies the parent object. The object should be inserted as a child of + * this parent (e.g. this would be the target directory in a file system based device). + * + * The driver should: + * - Create the object, and return its ID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID. + */ +HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesOnly( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + + CComPtr<IPortableDeviceValues> pObjectProperties; + CComPtr<IPortableDeviceValues> pEventParams; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pEventParams); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + + // Get the Object Properties + if (SUCCEEDED(hr)) + { + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pObjectProperties); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); + } + + // Save the object to the device here. + if (SUCCEEDED(hr)) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->CreatePropertiesOnlyObject(Scope, pObjectProperties, pEventParams, &pszObjectID); + CHECK_HR(hr, "Failed to save new (properties only) object to device"); + } + + if (SUCCEEDED(hr)) + { + // Create is successful, so we post an event. + // This is best effort, so errors are ignored + HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); + CHECK_HR(hrEvent, "Failed post event for new object [%ws] (errors ignored)", pszObjectID); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectID); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS: the ObjectIDs, indicating which objects to delete. These may + * contain children. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS: Flag parameter indicating delete options. Must be one + * of the following: + * - PORTABLE_DEVICE_DELETE_NO_RECURSION - Deletes the + * object only. This should fail if children exist. + * - PORTABLE_DEVICE_DELETE_WITH_RECURSION - Deletes this + * object and all children. + * + * The driver should: + * - If the flag is PORTABLE_DEVICE_DELETE_NO_RECURSION the driver should delete the + * specified object only. If the object still has children the driver should not delete + * the object and instead return HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION). + * - If the flag is PORTABLE_DEVICE_DELETE_WITH_RECURSION the driver should delete the + * specified object and all of its children. + * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS. It contains an IPortableDevicePropVariantCollection of + * VT_ERROR values indicating the success or failure of the operation for that element. + * Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on. + * - The driver should return: + * - S_OK if all objects were deleted successfully. + * - S_FALSE if any object delete failed. + * - An error return indicates that the driver did not delete any objects, and + * WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS is ignored. + */ +HRESULT WpdObjectManagement::OnDelete( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + DWORD dwOptions = PORTABLE_DEVICE_DELETE_NO_RECURSION; + BOOL bDeleteFailed = FALSE; + VARTYPE vt = VT_EMPTY; + + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + CComPtr<IPortableDevicePropVariantCollection> pDeleteResults; + CComPtr<IPortableDeviceValues> pEventParams; + + if (hr == S_OK) + { + hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); + } + + // Ensure that this is a collection of VT_LPWSTR + if (hr == S_OK) + { + hr = pObjectIDs->GetType(&vt); + CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); + if (hr == S_OK) + { + if (vt != VT_LPWSTR) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR"); + } + } + } + + if (hr == S_OK) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS, &dwOptions); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pDeleteResults); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pEventParams); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + DWORD cObjects = 0; + // Loop through the object list and attempt to delete + hr = pObjectIDs->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objects to delete"); + + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + + for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) + { + HRESULT hrTemp = S_OK; + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + // Get the next Object to delete + hr = pObjectIDs->GetAt(dwIndex, &pv); + CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); + if (hr == S_OK) + { + HRESULT hrDelete = S_OK; + PROPVARIANT pvResult = {0}; + + PropVariantInit(&pvResult); + + hrDelete = m_pDevice->DeleteObject(Scope, dwOptions, pv.pwszVal, pEventParams); + CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal); + + if(FAILED(hrDelete)) + { + bDeleteFailed = TRUE; + } + else + { + // Delete is successful, so we post an event. + // This is best effort, so errors are ignored + HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); + CHECK_HR(hrEvent, "Failed post event for deleted object [%ws] (errors ignored)", pv.pwszVal); + } + + // Clear event parameters for reuse + pEventParams->Clear(); + + // Save this result + pvResult.vt = VT_ERROR; + pvResult.scode = hrDelete; + hrTemp = pDeleteResults->Add(&pvResult); + PropVariantClear(&pvResult); + CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); + + PropVariantClear(&pv); + } + else + { + break; + } + } + } + } + + // Set the results + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS, pDeleteResults); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS"); + } + + // If an object failed to delete, make sure we return S_FALSE + if ((hr == S_OK) && (bDeleteFailed)) + { + hr = S_FALSE; + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdObjectManagement.h b/wpd/WpdServiceSampleDriver/WpdObjectManagement.h new file mode 100644 index 00000000..b7a8413c --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectManagement.h @@ -0,0 +1,27 @@ +#pragma once + +class WpdObjectManagement +{ +public: + WpdObjectManagement(); + ~WpdObjectManagement(); + + HRESULT Initialize(_In_ FakeDevice* m_pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + + HRESULT OnCreateObjectWithPropertiesOnly( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDelete( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeDevice* m_pDevice; +}; diff --git a/wpd/WpdServiceSampleDriver/WpdObjectProperties.cpp b/wpd/WpdServiceSampleDriver/WpdObjectProperties.cpp new file mode 100644 index 00000000..c9509f47 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectProperties.cpp @@ -0,0 +1,543 @@ +#include "stdafx.h" + +#include "WpdObjectProperties.tmh" + +WpdObjectProperties::WpdObjectProperties() : m_pDevice(NULL) +{ +} + +WpdObjectProperties::~WpdObjectProperties() +{ + +} + +HRESULT WpdObjectProperties::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pDevice = pDevice; + return S_OK; +} + +HRESULT WpdObjectProperties::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_OBJECT_PROPERTIES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED)) + { + hr = OnGetSupportedProperties(pParams, pResults); + CHECK_HR(hr, "Failed to get supported properties"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET)) + { + hr = OnGetPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL)) + { + hr = OnGetAllPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get all properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_SET)) + { + hr = OnSetPropertyValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to set properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES)) + { + hr = OnGetPropertyAttributes(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get property attributes"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_DELETE)) + { + hr = OnDeleteProperties(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to delete properties"); + } + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have + * been requested. + * + * - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties. + * Since this parameter is optional, it may not exist. + * ! This driver currently ignores the filter parameter. ! + * + * The driver should: + * - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS + */ +HRESULT WpdObjectProperties::OnGetSupportedProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose supported properties have been requested + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing string value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // CoCreate a collection to store the supported property keys. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add supported property keys for the specified object to the collection + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetSupportedProperties(Scope, wszObjectID, pKeys); + CHECK_HR(hr, "Failed to add supported property keys for object '%ws'", wszObjectID); + } + + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which + * specific property values we are requested to return. + * + * The driver should: + * - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were read successfully. + * - S_FALSE should be returned if any property read failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the list of property keys for the property values the caller wants to retrieve from the specified object + if (hr == S_OK) + { + hr = pParams->GetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &pKeys); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // CoCreate a collection to store the property values. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Read the specified properties on the specified object and add the property values to the collection. + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetPropertyValues(Scope, wszObjectID, pKeys, pValues); + CHECK_HR(hr, "Failed to get property values for object '%ws'", wszObjectID); + } + + // S_OK or S_FALSE can be returned from GetPropertyValues( ). + // S_FALSE means that 1 or more property values could not be retrieved successfully. + // The value for the specified property should be set to an error HRESULT of + // the reason why the property could not be read. + // (e.g. If the property being requested is not supported on the object then an error of + // HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) should be set as the value. + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results. + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested. + * + * The driver should: + * - Return all property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were read successfully. + * - S_FALSE should be returned if any property read failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetAllPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // CoCreate a collection to store the property values. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // First we make a request for ALL supported property keys for the specified object. + // Next, we delegate to our helper function GetPropertyValuesForObject( ) passing + // the entire property key collection. This will reuse existing implementation + // in our driver to perform the GetAllPropertyValues operation. + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetAllPropertyValues(Scope, wszObjectID, pValues); + CHECK_HR(hr, "Failed to get all property values for object '%ws'", wszObjectID); + } + + // S_OK or S_FALSE can be returned from GetAllPropertyValues( ). + // S_FALSE means that 1 or more property values could not be retrieved successfully. + // The value for the specified property key should be set to the error HRESULT of + // the reason why the property could not be read. + // (i.e. an error of HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) if a property value was + // requested and is not supported by the specified object.) + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues); + CHECK_HR(hrTemp, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which + * specific property values we are requested to write. + * + * The driver should: + * - Write all requested property values. For each property, a write result should be returned in the + * write result property store. + * - If any property write failed, the corresponding write result value should be + * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. + * - S_OK should be returned if all properties were written successfully. + * - S_FALSE should be returned if any property write failed. + * - Any error return indicates that the driver did not write any results, and the caller will + * not attempt to unpack any property write results. + */ +HRESULT WpdObjectProperties::OnSetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + bool bObjectChanged = false; + + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceValues> pWriteResults; + CComPtr<IPortableDeviceValues> pEventParams; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property values are being set + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the caller-supplied property values requested to be set on the object + if (hr == S_OK) + { + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"); + } + + // CoCreate a collection to store the property set operation results. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pWriteResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // CoCreate a collection to store the property set event parameters. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pEventParams); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Set the property values on the specified object + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->SetPropertyValues(Scope, wszObjectID, pValues, pWriteResults, pEventParams, &bObjectChanged); + CHECK_HR(hr, "Failed to set property values on object '%ws'", wszObjectID); + } + + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS value in the results + HRESULT hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS, pWriteResults); + CHECK_HR(hrTemp, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS"); + + if (FAILED(hrTemp)) + { + hr = hrTemp; + } + + if (SUCCEEDED(hr) && bObjectChanged) + { + // Post the event indicating the object has changed + hrTemp = PostWpdEvent(pParams, pEventParams); + CHECK_HR(hrTemp, "Failed post event for updated object [%ws] (errors ignored)", wszObjectID); + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property attributes we want to return. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys containing a single value, + * which is the key identifying the specific property attributes we are requested to return. + * + * The driver should: + * - Return the requested property attributes. If any property attributes failed to be retrieved, + * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the + * HRESULT reason for the failure. + * - S_OK should be returned if all property attributes were read successfully. + * - S_FALSE should be returned if any property attribute failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectProperties::OnGetPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + PROPERTYKEY Key = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pAttributes; + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the object identifier whose property attributes have been requested + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the list of property keys whose attributes are being requested + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &Key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS"); + } + + // CoCreate a collection to store the property attributes. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Get the attributes for the specified properties on the specified object and add them + // to the collection. + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetPropertyAtributes(Scope, wszObjectID, Key, pAttributes); + CHECK_HR(hr, "Failed to get property attributes"); + } + + if (SUCCEEDED(hr)) + { + // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES value in the results + HRESULT hrTemp = S_OK; + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributes); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_DELETE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose properties should be deleted. + * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys indicating which + * properties to delete. + * + * The driver should: + * - Delete the specified properties from the object. + * - S_OK should be returned if all specified properties were successfully deleted. + * - E_ACCESSDENIED should be returned if the client attempts to delete a property which is not deletable (i.e. + * WPD_PROPERTY_ATTRIBUTE_CAN_DELETE is FALSE for that property.) + */ +HRESULT WpdObjectProperties::OnDeleteProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = E_ACCESSDENIED; + + UNREFERENCED_PARAMETER(pParams); + UNREFERENCED_PARAMETER(pResults); + + // This driver has no properties which can be deleted. + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdObjectProperties.h b/wpd/WpdServiceSampleDriver/WpdObjectProperties.h new file mode 100644 index 00000000..95a0f94c --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectProperties.h @@ -0,0 +1,42 @@ +#pragma once + +class WpdObjectProperties +{ +public: + WpdObjectProperties(); + virtual ~WpdObjectProperties(); + + HRESULT Initialize(_In_ FakeDevice* pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetAllPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSetPropertyValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDeleteProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeDevice* m_pDevice; +}; diff --git a/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.cpp b/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.cpp new file mode 100644 index 00000000..a972d2cb --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.cpp @@ -0,0 +1,1068 @@ +#include "stdafx.h" + +#include "WpdObjectPropertiesBulk.tmh" + +#define MAX_OBJECTS_TO_RETURN 20 + +WpdObjectPropertiesBulk::WpdObjectPropertiesBulk() +{ + +} + +WpdObjectPropertiesBulk::~WpdObjectPropertiesBulk() +{ + +} + +HRESULT WpdObjectPropertiesBulk::Initialize(_In_ FakeDevice *pDevice) +{ + HRESULT hr = S_OK; + + if(pDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pDevice = pDevice; + return hr; +} + + +HRESULT WpdObjectPropertiesBulk::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (Command.fmtid != WPD_CATEGORY_OBJECT_PROPERTIES_BULK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + + if (hr == S_OK) + { + if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_START)) + { + hr = OnGetValuesByObjectListStart(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to start bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_NEXT)) + { + hr = OnGetValuesByObjectListNext(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to do next bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_END)) + { + hr = OnGetValuesByObjectListEnd(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to end bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_START)) + { + hr = OnGetValuesByObjectFormatStart(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to start bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_NEXT)) + { + hr = OnGetValuesByObjectFormatNext(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to do next bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_END)) + { + hr = OnGetValuesByObjectFormatEnd(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to end bulk property operation "); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_START)) + { + hr = OnSetValuesByObjectListStart(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to set bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT)) + { + hr = OnSetValuesByObjectListNext(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to set bulk property operation"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END)) + { + hr = OnSetValuesByObjectListEnd(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to set bulk property operation"); + } + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_START + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_IDS: identifies the objects whose property + * values we want to return. + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PROPERTY_KEYS: a collection of property keys, identifying which + * specific property values we are requested to return. If this property doesn't exist, + * then the client is asking for all values. + * + * The driver should: + * - Create a new context for this bulk property operation. + * - Return an identifier for the context in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectListStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + CComPtr<IPortableDeviceKeyCollection> pKeys; + ContextMap* pContextMap = NULL; + + // Get the IPortableDevicePropVariantCollection which contains the collection + // of object identifiers the bulk operation is being performed on. + hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_IDS, &pObjectIDs); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_IDS"); + + // Get the IPortableDeviceKeyCollection which contains the collection + // keys of properties being read on the multiple objects. + if (SUCCEEDED(hr)) + { + hr = pParams->GetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PROPERTY_KEYS, &pKeys); + if (FAILED(hr)) + { + // Client is asking for all properties. + pKeys = NULL; + hr = S_OK; + } + } + + // Get the context map which the driver stored in pParams for convenience + 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)) + { + LPWSTR pwszContext = NULL; + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = CreateBulkPropertiesContext(Scope, pContextMap, pObjectIDs, pKeys, &pwszContext); + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, pwszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + } + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_NEXT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnGetValuesByObjectListStart. + * + * The driver should: + * - Return the next set of property values in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES. + * If there are no more properties to be read an + * empty collection should be returned. + * - It is up to the driver to return as many object property values as it wants. If zero values are returned + * it is assumed the bulk operation is complete and the WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_END + * will be called next. + * + * - S_OK should be returned if the collection can be returned successfully. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectListNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + BulkPropertiesContext* pContext = NULL; + DWORD cObjects = 0; + CComPtr<IPortableDeviceValuesCollection> pCollection; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the bulk property operation context + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pwszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get bulk property context"); + } + + if (SUCCEEDED(hr)) + { + hr = pContext->ObjectIDs->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objectIDs from bulk properties context"); + } + + if (SUCCEEDED(hr)) + { + cObjects = cObjects - pContext->NextObject; + if(cObjects > MAX_OBJECTS_TO_RETURN) + { + cObjects = MAX_OBJECTS_TO_RETURN; + } + } + + // Make sure the the collection holds VT_LPWSTR values. + if (SUCCEEDED(hr)) + { + hr = pContext->ObjectIDs->ChangeType(VT_LPWSTR); + CHECK_HR(hr, "Failed to change objectIDs collection to VT_LPWSTR"); + } + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValuesCollection, + (VOID**) &pCollection); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = pContext->NextObject, dwCount = 0; dwCount < cObjects; dwCount++, dwIndex++) + { + CComPtr<IPortableDeviceValues> pValues; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + hr = pContext->ObjectIDs->GetAt(dwIndex, &pv); + CHECK_HR(hr, "Failed to get next object ID from bulk properties context"); + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); + } + + if (SUCCEEDED(hr)) + { + // If a key list was supplied, get the specified object properties, otherwise get all + // properties. + if(pContext->Properties != NULL) + { + hr = m_pDevice->GetPropertyValues(pContext->Scope, pv.pwszVal, pContext->Properties, pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + else + { + hr = m_pDevice->GetAllPropertyValues(pContext->Scope, pv.pwszVal, pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + } + + // Add the ObjectID to the returned results + if (SUCCEEDED(hr)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, pv.pwszVal); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID for %ws", pv.pwszVal); + } + + if (SUCCEEDED(hr)) + { + hr = pCollection->Add(pValues); + CHECK_HR(hr, "Failed to add IPortableDeviceValues to IPortableDeviceValuesCollection"); + } + + PropVariantClear(&pv); + + pContext->NextObject += 1; + } + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES, pCollection); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + + SAFE_RELEASE(pContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_LIST_END + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnGetValuesByObjectListStart. + * + * The driver should: + * - Destroy any resources associated with this context. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectListEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + ContextMap* pContextMap = NULL; + UNREFERENCED_PARAMETER(pResults); + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the context map which the driver stored in pParams for convenience + 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 = DestroyBulkPropertiesContext(pContextMap, pwszContext); + CHECK_HR(hr, "Failed to destroy bulk property context %ws", pwszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_START + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_FORMAT: Identifies the format of the objects the + * client is interested in. + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PARENT_OBJECT_ID: Identifies the parent object from which the + * operation should start. + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_DEPTH: Indicates the hierarchical depth of the operation + * from the parent object. + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PROPERTY_KEYS: a collection of property keys, identifying which + * specific property values we are requested to return. If this doesn't exist, then + * ALL object proeprties should be returned for the specified objects. + * + * The driver should: + * - Create a new context for this bulk property operation. + * - Return an identifier for the context in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectFormatStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + GUID guidObjectFormat = GUID_NULL; + LPWSTR pszParentObjectID = NULL; + DWORD dwDepth = 0; + ContextMap* pContextMap = NULL; + + // Get the object format. + hr = pParams->GetGuidValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_FORMAT, &guidObjectFormat); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_BULK_OBJECT_FORMAT"); + + // Get the parent object id. + if (SUCCEEDED(hr)) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PARENT_OBJECT_ID, &pszParentObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PARENT_OBJECT_ID"); + } + + // Get the depth. + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_DEPTH, &dwDepth); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_BULK_DEPTH"); + } + + // Get the IPortableDeviceKeyCollection which contains the collection + // keys of properties being read on the multiple objects. + if (SUCCEEDED(hr)) + { + hr = pParams->GetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_PROPERTY_KEYS, &pKeys); + if (FAILED(hr)) + { + // Client is asking for all properties. + pKeys = NULL; + hr = S_OK; + } + } + + // Get the context map which the driver stored in pParams for convenience + 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)) + { + LPWSTR pwszContext = NULL; + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = CreateBulkPropertiesContext(Scope, pContextMap, guidObjectFormat, pszParentObjectID, dwDepth, pKeys, &pwszContext); + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, pwszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszParentObjectID); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_NEXT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnGetValuesByObjectFormatStart. + * + * The driver should: + * - Return the next set of property values in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES. + * If there are no more properties to be read an + * empty collection should be returned. + * - It is up to the driver to return as many object property values as it wants. If zero values are returned + * it is assumed the bulk operation is complete and the WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_END + * will be called next. + * + * - S_OK should be returned if the collection can be returned successfully. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectFormatNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + BulkPropertiesContext* pContext = NULL; + DWORD cObjects = 0; + CComPtr<IPortableDeviceValuesCollection> pCollection; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the bulk property operation context + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pwszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get bulk property context"); + } + + // Make sure the the collection holds VT_LPWSTR values. + if (SUCCEEDED(hr)) + { + hr = pContext->ObjectIDs->ChangeType(VT_LPWSTR); + CHECK_HR(hr, "Failed to change objectIDs collection to VT_LPWSTR"); + } + + if (SUCCEEDED(hr)) + { + hr = pContext->ObjectIDs->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objectIDs from bulk properties context"); + } + + if (SUCCEEDED(hr)) + { + cObjects = cObjects - pContext->NextObject; + if(cObjects > MAX_OBJECTS_TO_RETURN) + { + cObjects = MAX_OBJECTS_TO_RETURN; + } + } + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValuesCollection, + (VOID**) &pCollection); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = pContext->NextObject, dwCount = 0; dwCount < cObjects; dwCount++, dwIndex++) + { + CComPtr<IPortableDeviceValues> pValues; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + hr = pContext->ObjectIDs->GetAt(dwIndex, &pv); + CHECK_HR(hr, "Failed to get next object ID from bulk properties context"); + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); + } + + if (SUCCEEDED(hr)) + { + // If a key list was supplied, get the specified object properties, other get all + // properties. + if(pContext->Properties != NULL) + { + hr = m_pDevice->GetPropertyValues(pContext->Scope, pv.pwszVal, pContext->Properties, pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + else + { + hr = m_pDevice->GetAllPropertyValues(pContext->Scope,pv.pwszVal, pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + } + + // Add the ObjectID to the returned results + if (SUCCEEDED(hr)) + { + hr = pValues->SetStringValue(WPD_OBJECT_ID, pv.pwszVal); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID for %ws", pv.pwszVal); + } + + if (SUCCEEDED(hr)) + { + hr = pCollection->Add(pValues); + CHECK_HR(hr, "Failed to add IPortableDeviceValues to IPortableDeviceValuesCollection"); + } + + PropVariantClear(&pv); + pContext->NextObject++; + } + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES, pCollection); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + SAFE_RELEASE(pContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_GET_VALUES_BY_OBJECT_FORMAT_END + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnGetValuesByObjectFormatStart. + * + * The driver should: + * - Destroy any resources associated with this context. + */ +HRESULT WpdObjectPropertiesBulk::OnGetValuesByObjectFormatEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + ContextMap* pContextMap = NULL; + UNREFERENCED_PARAMETER(pResults); + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the context map which the driver stored in pParams for convenience + 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 = DestroyBulkPropertiesContext(pContextMap, pwszContext); + CHECK_HR(hr, "Failed to destroy bulk property context %ws", pwszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_START + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES: holds a collection of IPortableDeviceValues which + * indicate which object properties to set. + * + * The driver should: + * - Create a new context for this bulk property operation. + * - Return an identifier for the context in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT. + */ +HRESULT WpdObjectPropertiesBulk::OnSetValuesByObjectListStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValuesCollection> pValuesCollection; + ContextMap* pContextMap = NULL; + + // Get the IPortableDevicePropVariantCollection which contains the collection + // of object identifiers the bulk operation is being performed on. + hr = pParams->GetIPortableDeviceValuesCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES, &pValuesCollection); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_BULK_VALUES"); + + // Get the context map which the driver stored in pParams for convenience + 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)) + { + LPWSTR pwszContext = NULL; + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = CreateBulkPropertiesContext(Scope, pContextMap, pValuesCollection, &pwszContext); + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, pwszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + } + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnGetValuesByObjectListStart. + * + * The driver should: + * - Write the next set of property values, and return the write results in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS. + * If there are no more properties to be written, an empty collection should be returned. + * - It is up to the driver to write as many object property values as it wants. If zero write results are returned + * it is assumed the bulk operation is complete and the WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END + * will be called next. + * + * - S_OK should be returned if the collection can be returned successfully. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + */ +HRESULT WpdObjectPropertiesBulk::OnSetValuesByObjectListNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + BulkPropertiesContext* pContext = NULL; + DWORD cObjects = 0; + CComPtr<IPortableDeviceValues> pEventParams; + CComPtr<IPortableDeviceValuesCollection> pWriteResults; + CComPtr<IPortableDeviceValuesCollection> pValuesCollection; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the bulk property operation context + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pwszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get bulk property context"); + } + + // Make sure the the collection holds a ValuesCollection, then get the number of elements. + if (SUCCEEDED(hr)) + { + if(pContext->ValuesCollection != NULL) + { + hr = pContext->ValuesCollection->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objectIDs from bulk properties context"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Incorrect context specified - this context does not contain a values collection"); + } + } + + // Create the collection to hold the write results + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValuesCollection, + (VOID**) &pWriteResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); + } + + // Create the collection to hold the event parameters + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pEventParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = pContext->NextObject; dwIndex < cObjects; dwIndex++) + { + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceValues> pSetResults; + + bool bObjectChanged = false; + + hr = pContext->ValuesCollection->GetAt(dwIndex, &pValues); + CHECK_HR(hr, "Failed to get next values from bulk properties context"); + + // CoCreate a collection to store the per object results. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pSetResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + LPWSTR pszObjectID = NULL; + + // Get which object this is for + hr = pValues->GetStringValue(WPD_OBJECT_ID, &pszObjectID); + if (SUCCEEDED(hr)) + { + hr = m_pDevice->SetPropertyValues(pContext->Scope, pszObjectID, pValues, pSetResults, pEventParams, &bObjectChanged); + CHECK_HR(hr, "Failed to get count of values"); + } + + if (SUCCEEDED(hr)) + { + // Ensure the write results contain which ObjectID this was for + hr = pSetResults->SetStringValue(WPD_OBJECT_ID, pszObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID in write results"); + + if (SUCCEEDED(hr) && bObjectChanged) + { + // set property values is successful and object has changed, so we post an event. + // This is best effort, so errors are ignored + HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); + CHECK_HR(hrEvent, "Failed post event for updated object [%ws] (errors ignored)", pszObjectID); + } + pEventParams->Clear(); + } + + CoTaskMemFree(pszObjectID); + } + + if (SUCCEEDED(hr)) + { + hr = pWriteResults->Add(pSetResults); + CHECK_HR(hr, "Failed to add IPortableDeviceValues to IPortableDeviceValuesCollection"); + } + + pContext->NextObject++; + } + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS, pWriteResults); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + + SAFE_RELEASE(pContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to + * the client in OnSetValuesByObjectListStart. + * + * The driver should: + * - Destroy any resources associated with this context. + */ +HRESULT WpdObjectPropertiesBulk::OnSetValuesByObjectListEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pwszContext = NULL; + ContextMap* pContextMap = NULL; + UNREFERENCED_PARAMETER(pResults); + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); + + // Get the context map which the driver stored in pParams for convenience + 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 = DestroyBulkPropertiesContext(pContextMap, pwszContext); + CHECK_HR(hr, "Failed to destroy bulk property context %ws", pwszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pwszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ IPortableDevicePropVariantCollection* pObjectIDs, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + BulkPropertiesContext* pContext = NULL; + CAtlStringW strKey; + + if((pContextMap == NULL) || + (pObjectIDs == NULL) || + (ppszBulkPropertiesContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszBulkPropertiesContext = NULL; + + pContext = new BulkPropertiesContext(); + if(pContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new bulk properties context"); + } + + if (SUCCEEDED(hr)) + { + pContext->ObjectIDs = pObjectIDs; + pContext->Properties = pProperties; + pContext->Scope = Scope; + + hr = pContextMap->Add(pContext, strKey); + CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map"); + } + + if (SUCCEEDED(hr)) + { + *ppszBulkPropertiesContext = AtlAllocTaskWideString(strKey); + if (*ppszBulkPropertiesContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate bulk properties context"); + } + } + + SAFE_RELEASE(pContext); + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR pszParentObjectID, + _In_ DWORD dwDepth, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + + if((pContextMap == NULL) || + (pszParentObjectID == NULL) || + (ppszBulkPropertiesContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszBulkPropertiesContext = NULL; + + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDs); + CHECK_HR(hr, "Failed to CoCreate CLSID_IPortableDevicePropVariantCollection"); + + if (SUCCEEDED(hr)) + { + hr = m_pDevice->GetObjectIDsByFormat(Scope, guidObjectFormat, pszParentObjectID, dwDepth, pObjectIDs); + CHECK_HR(hr, "Faield to get list of object ids by format"); + } + + if (SUCCEEDED(hr)) + { + hr = CreateBulkPropertiesContext(Scope, pContextMap, pObjectIDs, pProperties, ppszBulkPropertiesContext); + } + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ IPortableDeviceValuesCollection* pValuesCollection, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + BulkPropertiesContext* pContext = NULL; + CAtlStringW strKey; + + if((pContextMap == NULL) || + (pValuesCollection == NULL) || + (ppszBulkPropertiesContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszBulkPropertiesContext = NULL; + + pContext = new BulkPropertiesContext(); + if(pContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new bulk properties context"); + } + + if (SUCCEEDED(hr)) + { + pContext->ValuesCollection = pValuesCollection; + pContext->Scope = Scope; + + hr = pContextMap->Add(pContext, strKey); + CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map"); + } + + if (SUCCEEDED(hr)) + { + *ppszBulkPropertiesContext = AtlAllocTaskWideString(strKey); + if (*ppszBulkPropertiesContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate bulk properties context"); + } + } + + SAFE_RELEASE(pContext); + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::DestroyBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + + CAtlStringW strKey = pszBulkPropertiesContext; + pContextMap->Remove(strKey); + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.h b/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.h new file mode 100644 index 00000000..52ec7ed1 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectPropertiesBulk.h @@ -0,0 +1,148 @@ +#pragma once + +// This class is used to store the context for a specific enumeration. +// Currently, this is done by storing the object index. +class BulkPropertiesContext : public IUnknown +{ +public: + BulkPropertiesContext() : + Scope(FULL_DEVICE_ACCESS), + NextObject(0), + m_cRef(1) + { + + } + + ~BulkPropertiesContext() + { + + } + + ACCESS_SCOPE Scope; + CComPtr<IPortableDevicePropVariantCollection> ObjectIDs; + DWORD NextObject; + CComPtr<IPortableDeviceKeyCollection> Properties; + CComPtr<IPortableDeviceValuesCollection> ValuesCollection; + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +private: + DWORD m_cRef; +}; + +class WpdObjectPropertiesBulk +{ +public: + WpdObjectPropertiesBulk(); + ~WpdObjectPropertiesBulk(); + + HRESULT Initialize(_In_ FakeDevice *pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectListStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectListNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectListEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectFormatStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectFormatNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValuesByObjectFormatEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSetValuesByObjectListStart( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSetValuesByObjectListNext( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSetValuesByObjectListEnd( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ IPortableDevicePropVariantCollection* pObjectIDs, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR pszParentObjectID, + _In_ DWORD dwDepth, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT CreateBulkPropertiesContext( + _In_ ACCESS_SCOPE Scope, + _In_ ContextMap* pContextMap, + _In_ IPortableDeviceValuesCollection* pValuesCollection, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT DestroyBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszBulkPropertiesContext); + + FakeDevice* m_pDevice; +}; diff --git a/wpd/WpdServiceSampleDriver/WpdObjectResources.cpp b/wpd/WpdServiceSampleDriver/WpdObjectResources.cpp new file mode 100644 index 00000000..4f6341ac --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectResources.cpp @@ -0,0 +1,465 @@ +#include "stdafx.h" + +#include "WpdObjectResources.tmh" + +WpdObjectResources::WpdObjectResources() : m_pDevice(NULL) +{ + +} + +WpdObjectResources::~WpdObjectResources() +{ + +} + +HRESULT WpdObjectResources::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pDevice = pDevice; + return S_OK; +} + +HRESULT WpdObjectResources::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_OBJECT_RESOURCES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + } + } + + if (hr == S_OK) + { + if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED)) + { + hr = OnGetSupportedResources(pParams, pResults); + CHECK_HR(hr, "Failed to get supported resources"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_OPEN)) + { + hr = OnOpenResource(pParams, pResults); + CHECK_HR(hr, "Failed to open resource"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_READ)) + { + hr = OnReadResource(pParams, pResults); + CHECK_HR(hr, "Failed to read resource"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CLOSE)) + { + hr = OnCloseResource(pParams, pResults); + CHECK_HR(hr, "Failed to close resource"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES)) + { + hr = OnGetResourceAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get resource attributes"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + } + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resources we want to return. + * + * The driver should: + * - Return all resources for this object in WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS. + */ +HRESULT WpdObjectResources::OnGetSupportedResources( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID"); + } + + // Create the collection to hold the resource keys + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetSupportedResources(Scope, wszObjectID, pKeys); + CHECK_HR(hr, "Failed to get supported resources for object '%ws'", wszObjectID); + } + + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, pKeys); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resource attributes we want to return. + * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of property keys containing a single value, + * which is the key identifying the specific resource we are requested to return attributes for. + * + * The driver should: + * - Return the requested property attributes in WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES. + * If any resource attributes failed to be retrieved, + * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the + * HRESULT reason for the failure. + * - S_OK should be returned if all resource attributes were read successfully. + * - S_FALSE should be returned if any resource attribute failed. + * - Any error return indicates that the driver did not fill in any results, and the caller will + * not attempt to unpack any property values. + * + */ +HRESULT WpdObjectResources::OnGetResourceAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + PROPERTYKEY Key = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pAttributes; + + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID"); + } + + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, &Key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->GetResourceAttributes(Scope, wszObjectID, Key, pAttributes); + CHECK_HR(hr, "Failed to get resource attributes"); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, pAttributes); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_OPEN + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: the object identifier of the + * object which contains the specified resource + * + * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: the specified resource + * to open + * + * - WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE: the access mode to which to + * open the specified resource + * + * The driver should: + * - Create a new context for this resource operation. + * - Return an identifier for the context in WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT. + * - Set the optimal transfer size in WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE + * + */ +HRESULT WpdObjectResources::OnOpenResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + PROPERTYKEY Key = WPD_PROPERTY_NULL; + DWORD dwMode = STGM_READ; + CAtlStringW strStrObjectID; + CAtlStringW strResourceContext; + ContextMap* pContextMap = NULL; + + // Get the Object identifier of the object which contains the specified resource + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID"); + } + + // Get the resource key + if (SUCCEEDED(hr)) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, &Key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS"); + } + + // Get the access mode + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE, &dwMode); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE"); + } + + // Get the context map which the driver stored in pParams for convenience + if (SUCCEEDED(hr)) + { + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, "Failed to get the client context map"); + } + + // Create a new resource operation context, initialize it, and add it to the client context map. + if (SUCCEEDED(hr)) + { + WpdObjectResourceContext* pResourceContext = new WpdObjectResourceContext(); + if (pResourceContext != NULL) + { + ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); + hr = m_pDevice->OpenResource(Scope, wszObjectID, Key, dwMode, pResourceContext); + CHECK_HR(hr, "Failed to open resource"); + + if (SUCCEEDED(hr)) + { + // Add the resource context to the context map, this calls AddRef() on pResourceContext + hr = pContextMap->Add(pResourceContext, strResourceContext); + CHECK_HR(hr, "Failed to add the resource context"); + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate resource context"); + } + SAFE_RELEASE(pResourceContext); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, strResourceContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // Set the optimal buffer size + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE, FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE value"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszObjectID); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_READ + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: the context the driver returned to + * the client in OnOpenResource. + * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ: the number of bytes to + * read from the resource. + * + * The driver should: + * - Read data associated with the resource and return it back to the caller in + * WPD_PROPERTY_OBJECT_RESOURCES_DATA. + * - Report the number of bytes actually read from the resource in + * WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ. This number may be smaller + * than WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ when reading the last + * chunk of data from the resource. + */ +HRESULT WpdObjectResources::OnReadResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszResourceContext = NULL; + DWORD dwNumBytesToRead = 0; + DWORD dwNumBytesRead = 0; + BYTE* pBuffer = NULL; + WpdObjectResourceContext* pResourceContext = NULL; + + // Get the enumeration context identifier for this enumeration operation. We will + // need this to lookup the specific enumeration context in the client context map. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &wszResourceContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // Get the number of bytes to read + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ, &dwNumBytesToRead); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ"); + } + + // Allocate the destination buffer + if (SUCCEEDED(hr)) + { + pBuffer = reinterpret_cast<BYTE *>(CoTaskMemAlloc(dwNumBytesToRead)); + if (pBuffer == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate the destination buffer"); + } + } + + // Get the client context map so we can retrieve the resource context for this resource + // operation using the WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT property value obtained above. + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, wszResourceContext, (IUnknown**)&pResourceContext); + CHECK_HR(hr, "Failed to get the resource context"); + } + + // Read the next chunk of data for this request + if (SUCCEEDED(hr)) + { + hr = m_pDevice->ReadResourceData(pResourceContext, pBuffer, dwNumBytesToRead, &dwNumBytesRead); + CHECK_HR(hr, "Failed to read %d bytes from resource", dwNumBytesToRead); + } + + if (SUCCEEDED(hr)) + { + pResourceContext->m_BytesTransferred += dwNumBytesRead; + hr = pResults->SetBufferValue(WPD_PROPERTY_OBJECT_RESOURCES_DATA, pBuffer, dwNumBytesRead); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_DATA"); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ, dwNumBytesRead); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszResourceContext); + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pBuffer); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_CLOSE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: the context the driver returned to + * the client in OnOpenResource. + * + * The driver should: + * - Destroy any data associated with this context. + */ +HRESULT WpdObjectResources::OnCloseResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszResourceContext = NULL; + ContextMap* pContextMap = NULL; + + UNREFERENCED_PARAMETER(pResults); + + // First get ALL parameters for this command. If we cannot get ALL parameters + // then E_INVALIDARG should be returned and no further processing should occur. + + // Get the resource context identifier for this resource operation. We will + // need this to lookup the specific resource context in the client context map. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &wszResourceContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // Get the client context map so we can retrieve the resource context for this resource + // operation using the WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT property value obtained above. + if (SUCCEEDED(hr)) + { + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, "Failed to get the client context map"); + } + + // Destroy any data allocated/associated with the resource context and then remove it from the context map. + // We no longer need to keep this context around because the resource operation has been ended. + if (SUCCEEDED(hr)) + { + pContextMap->Remove(wszResourceContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(wszResourceContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdObjectResources.h b/wpd/WpdServiceSampleDriver/WpdObjectResources.h new file mode 100644 index 00000000..1f8c4a0a --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdObjectResources.h @@ -0,0 +1,112 @@ +#pragma once + +#define FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE (2 * 1024 * 1024) +#define FILE_OPTIMAL_WRITE_BUFFER_SIZE_VALUE (2 * 1024 * 1024) + +// This class is used to store the context for a specific resource operation. +class WpdObjectResourceContext : public IUnknown +{ +public: + WpdObjectResourceContext() : + m_cRef(1), + m_Resource(WPD_RESOURCE_DEFAULT), + m_BytesTransferred(0), + m_BytesTotal(0), + m_Scope(FULL_DEVICE_ACCESS) + { + + } + + ~WpdObjectResourceContext() + { + + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +private: + DWORD m_cRef; + +// WpdObjectResourceContext specific data +public: + CAtlStringW m_strObjectID; // object identifier of the object whose resource is being transferred + PROPERTYKEY m_Resource; // the specific resource being transferred + ULONGLONG m_BytesTransferred; // number of bytes transferred from the resource to the caller + ULONGLONG m_BytesTotal; // total number of bytes of the resource data + ACCESS_SCOPE m_Scope; // client access scope +}; + +class WpdObjectResources +{ +public: + WpdObjectResources(); + virtual ~WpdObjectResources(); + + HRESULT Initialize(_In_ FakeDevice* pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedResources( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetResourceAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnOpenResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnReadResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCloseResource( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeDevice* m_pDevice; +}; diff --git a/wpd/WpdServiceSampleDriver/WpdService.cpp b/wpd/WpdServiceSampleDriver/WpdService.cpp new file mode 100644 index 00000000..4a255b0b --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdService.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" + +#include "WpdService.tmh" + +WpdService::WpdService() : m_pContactsService(NULL) +{ +} + +WpdService::~WpdService() +{ + +} + +HRESULT WpdService::Initialize(_In_ FakeDevice* pDevice) +{ + if (pDevice == NULL) + { + return E_POINTER; + } + m_pContactsService = pDevice->GetContactsService(); + m_ServiceMethods.Initialize(m_pContactsService); + m_ServiceCapabilities.Initialize(m_pContactsService); + return S_OK; +} + +HRESULT WpdService::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszRequestFilename = NULL; + + // Get the request filename to process the service message + hr = pParams->GetStringValue(PRIVATE_SAMPLE_DRIVER_REQUEST_FILENAME, &pszRequestFilename); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to get the required requested filename"); + } + + if (hr == S_OK) + { + hr = CheckRequestFilename(pszRequestFilename); + CHECK_HR(hr, "Unknown request filename %ws received", pszRequestFilename); + } + + if (hr == S_OK) + { + if (Command.fmtid == WPD_CATEGORY_SERVICE_CAPABILITIES) + { + hr = m_ServiceCapabilities.DispatchWpdMessage(Command, pParams, pResults); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_METHODS_START_INVOKE)) + { + hr = m_ServiceMethods.OnStartInvoke(pParams, pResults); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_METHODS_END_INVOKE)) + { + hr = m_ServiceMethods.OnEndInvoke(pParams, pResults); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_METHODS_CANCEL_INVOKE)) + { + hr = m_ServiceMethods.OnCancelInvoke(pParams, pResults); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_COMMON_GET_SERVICE_OBJECT_ID)) + { + hr = OnGetServiceObjectID(pszRequestFilename, pParams, pResults); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "Unknown command %ws.%d received",CComBSTR(Command.fmtid), Command.pid); + } + } + + CoTaskMemFree(pszRequestFilename); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_COMMON_GET_SERVICE_OBJECT_ID + * command. + * + * The parameters sent to us are: + * None + * + * The driver should: + * - Return the objectID associated with the filename. + * + */ +HRESULT WpdService::OnGetServiceObjectID( + _In_ LPCWSTR pszRequestFilename, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if((pParams == NULL) || + (pResults == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // For simplicity, the request filename is the same as the service object ID + hr = pResults->SetStringValue(WPD_PROPERTY_SERVICE_OBJECT_ID, pszRequestFilename); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_OBJECT_IDS"); + + return hr; +} + +HRESULT WpdService::CheckRequestFilename( + _In_ LPCWSTR pszRequestFilename) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + CAtlStringW strRequestFilename = pszRequestFilename; + + // For simplicity, the request filename happens to be the same as the service object ID + if (strRequestFilename.CompareNoCase(m_pContactsService->GetRequestFilename()) == 0) + { + hr = S_OK; + } + else + { + CHECK_HR(hr, "Unknown request filename %ws received", pszRequestFilename); + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdService.h b/wpd/WpdServiceSampleDriver/WpdService.h new file mode 100644 index 00000000..0e28eadf --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdService.h @@ -0,0 +1,30 @@ +#pragma once + +class WpdService +{ +public: + WpdService(); + virtual ~WpdService(); + + HRESULT Initialize(_In_ FakeDevice* pDevice); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT OnGetServiceObjectID( + _In_ LPCWSTR pszRequestFilename, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT CheckRequestFilename( + _In_ LPCWSTR pszRequestFilename); + +private: + WpdServiceMethods m_ServiceMethods; + WpdServiceCapabilities m_ServiceCapabilities; + FakeContactsService* m_pContactsService; +}; + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.cpp b/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.cpp new file mode 100644 index 00000000..7f67e199 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.cpp @@ -0,0 +1,832 @@ +#include "stdafx.h" + +#include "WpdServiceCapabilities.tmh" + +WpdServiceCapabilities::WpdServiceCapabilities() : m_pContactsService(NULL) +{ + +} + +WpdServiceCapabilities::~WpdServiceCapabilities() +{ + +} + +HRESULT WpdServiceCapabilities::Initialize(_In_ FakeContactsService* pContactsService) +{ + if (pContactsService == NULL) + { + return E_POINTER; + } + m_pContactsService = pContactsService; + return S_OK; +} + +HRESULT WpdServiceCapabilities::DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + if (Command.fmtid != WPD_CATEGORY_SERVICE_CAPABILITIES) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid)); + return hr; + } + + if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_COMMANDS)) + { + hr = OnGetSupportedCommands(pParams, pResults); + CHECK_HR(hr, "Failed to get supported commands"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_COMMAND_OPTIONS)) + { + hr = OnGetCommandOptions(pParams, pResults); + CHECK_HR(hr, "Failed to get command options"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS)) + { + hr = OnGetSupportedMethods(pParams, pResults); + CHECK_HR(hr, "Failed to get supported methods"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS_BY_FORMAT)) + { + hr = OnGetSupportedMethodsByFormat(pParams, pResults); + CHECK_HR(hr, "Failed to get supported methods by format"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_ATTRIBUTES)) + { + hr = OnGetMethodAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get method attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_PARAMETER_ATTRIBUTES)) + { + hr = OnGetMethodParameterAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get method parameter attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMATS)) + { + hr = OnGetSupportedFormats(pParams, pResults); + CHECK_HR(hr, "Failed to get supported formats"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_ATTRIBUTES)) + { + hr = OnGetFormatAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get format attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES)) + { + hr = OnGetSupportedFormatProperties(pParams, pResults); + CHECK_HR(hr, "Failed to get supported format properties"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_PROPERTY_ATTRIBUTES)) + { + hr = OnGetFormatPropertyAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get format property attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_EVENTS)) + { + hr = OnGetSupportedEvents(pParams, pResults); + CHECK_HR(hr, "Failed to get supported events"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_ATTRIBUTES)) + { + hr = OnGetEventAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get event attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_PARAMETER_ATTRIBUTES)) + { + hr = OnGetEventParameterAttributes(pParams, pResults); + CHECK_HR(hr, "Failed to get event parameter attributes"); + } + else if (IsEqualPropertyKey(Command, WPD_COMMAND_SERVICE_CAPABILITIES_GET_INHERITED_SERVICES)) + { + hr = OnGetInheritedServices(pParams, pResults); + CHECK_HR(hr, "Failed to get inherited services"); + } + else + { + hr = E_NOTIMPL; + CHECK_HR(hr, "This object does not support this command id %d", Command.pid); + } + + return hr; +} + + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_COMMANDS + * command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all commands supported by this service as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_COMMANDS. + * This includes custom commands, if any. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pCommands; + + // CoCreate a collection to store the supported commands. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pCommands); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add the supported commands to the collection. + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedCommands(pCommands); + CHECK_HR(hr, "Failed to get the supported commands"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_COMMANDS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_COMMANDS, pCommands); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_COMMANDS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_COMMAND_OPTIONS + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND: a collection of property keys containing a single value, + * which identifies the specific command options are requested to return. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND_OPTIONS, containing + * the relevant options. If no options are available for this command, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + PROPERTYKEY Command = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pOptions; + + // Get the command whose options have been requested + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND, &Command); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND"); + } + + // CoCreate a collection to store the command options. + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Add command options to the collection + if (hr == S_OK) + { + hr = m_pContactsService->GetCommandOptions(Command, pOptions); + CHECK_HR(hr, "Failed to get the command options"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND_OPTIONS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIUnknownValue(WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND_OPTIONS, pOptions); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_COMMAND_OPTIONS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS command. + * + * The parameters sent to us are: + * - none. + * + * The driver should: + * - Return all methods supported by this service as an + * IPortableDevicePropVariantCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS. + * If no methods are available for this service, the driver should return an IPortableDevicePropVariantCollection + * with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedMethods( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + + CComPtr<IPortableDevicePropVariantCollection> pMethods; + + // CoCreate a collection to store the supported methods. + HRESULT hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pMethods); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedMethods(pMethods); + CHECK_HR(hr, "Failed to get the supported methods"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS, pMethods); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_METHODS_BY_FORMAT command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT: Identifies the format whose methods are being requested + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS, + * containing the supported methods that apply to this format. If no methods are available for this format, + * the driver should return an IPortableDevicePropVariantCollection with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedMethodsByFormat( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Format = GUID_NULL; + + CComPtr<IPortableDevicePropVariantCollection> pMethods; + + // Get the format parameter + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT, &Format); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT"); + + if (hr == S_OK) + { + // CoCreate a collection to store the supported methods. + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pMethods); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedMethodsByFormat(Format, pMethods); + CHECK_HR(hr, "Failed to get the supported methods by format"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS, pMethods); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_ATTRIBUTES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD: Identifies the method whose attributes are being requested + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD_ATTRIBUTES, containing + * the method attributes. If no attributes are available for this method, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetMethodAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Method = GUID_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the method + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD, &Method); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD"); + + if (hr == S_OK) + { + // CoCreate a collection to store the method attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetMethodAttributes(Method, pAttributes); + CHECK_HR(hr, "Failed to add method attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_METHODS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_METHOD_PARAMETER_ATTRIBUTES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER: Identifies the parameter whose attributes are being requested + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_METHOD_PARAMETER_ATTRIBUTES, containing + * the parameter attributes. If no attributes are available for this parameter, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetMethodParameterAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + PROPERTYKEY Parameter = WPD_PROPERTY_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the method + hr = pParams->GetKeyValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER, &Parameter); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER"); + + if (hr == S_OK) + { + // CoCreate a collection to store the parameter attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetMethodParameterAttributes(Parameter, pAttributes); + CHECK_HR(hr, "Failed to get the method parameter attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMATS command. + * + * The parameters sent to us are: + * - None + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_FORMATS, containing + * the supported formats for the service. If no formats are supported by this service, the driver should + * return an IPortableDevicePropVariantCollection with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedFormats( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + + CComPtr<IPortableDevicePropVariantCollection> pFormats; + + // CoCreate a collection to store the formats. + HRESULT hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFormats); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedFormats(pFormats); + CHECK_HR(hr, "Failed to get the supported formats"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_FORMATS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMATS, pFormats); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_FORMATS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_ATTRIBUTES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT: Identifies the format whose attributes are being requested + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT_ATTRIBUTES, containing + * the attributes for the format. If no attributes are supported by the format, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetFormatAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Format = GUID_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the format + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT, &Format); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT"); + + if (hr == S_OK) + { + // CoCreate a collection to store the attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetFormatAttributes(Format, pAttributes); + CHECK_HR(hr, "Failed to add format attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT_ATTRIBUTES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES command. + * This list is the super-set of all properties that will be supported by an object of the given format. + * Individual objects can be queried for their properties using WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED. + * Note that this method is generally much quicker than calling WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED, + * since the driver does not have to perform a dynamic lookup based on a specific object. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT: Identifies the format whose attributes are being requested + * + * The driver should: + * - Return an IPortableDeviceKeyCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS, containing + * the supported properties for the format. If no properties are supported by the format, the driver should + * return an IPortableDeviceKeyCollection with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedFormatProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Format = GUID_NULL; + + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // Get the format + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT, &Format); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT"); + + if (hr == S_OK) + { + // CoCreate a collection to store the attributes. + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedFormatProperties(Format, pKeys); + CHECK_HR(hr, "Failed to add the supported format properties"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS, pKeys); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_FORMAT_PROPERTY_ATTRIBUTES command. + * Often, a driver treats objects of a given format the same. Many properties therefore will have attributes + * that are identical across all objects of that format. These can be returned here. There are some attributes + * which may be differ per object instance, which are not returned here. See WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT: Identifies the format whose property attributes are being requested + * - WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS: An IPortableDeviceKeyCollection containing a single value, + * which is the key identifying the specific property attributes the driver is requested to return. + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_ATTRIBUTES, containing + * the attributes for the property. If no attributes are supported by the property, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetFormatPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Format = GUID_NULL; + PROPERTYKEY Property = WPD_PROPERTY_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the format + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT, &Format); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT"); + + if (hr == S_OK) + { + // Get the property + hr = pParams->GetKeyValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_KEYS, &Property); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_FORMAT"); + } + + if (hr == S_OK) + { + // CoCreate a collection to store the attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetPropertyAttributes(Format, Property, pAttributes); + CHECK_HR(hr, "Failed to get the supported property attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_PROPERTY_ATTRIBUTES"); + } + + return hr; + +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_SUPPORTED_EVENTS command. + * + * The parameters sent to us are: + * - None + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_EVENTS, containing + * the events for the service. If no events are supported by the service, the driver should + * return an IPortableDevicePropVariantCollection with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + UNREFERENCED_PARAMETER(pParams); + + CComPtr<IPortableDevicePropVariantCollection> pEvents; + + // CoCreate a collection to store the supported events. + HRESULT hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pEvents); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + // Add the supported events to the collection. + if (hr == S_OK) + { + hr = m_pContactsService->GetSupportedEvents(pEvents); + CHECK_HR(hr, "Failed to get the supported events"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_EVENTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_EVENTS, pEvents); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_EVENTS"); + } + + return hr; + +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_ATTRIBUTES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT: Indicates the event the caller is interested in + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT_ATTRIBUTES, containing + * the event attributes. If there are no attributes for that event, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetEventAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + GUID Event = GUID_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the format + hr = pParams->GetGuidValue(WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT, &Event); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT"); + + if (hr == S_OK) + { + // CoCreate a collection to store the attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetEventAttributes(Event, pAttributes); + CHECK_HR(hr, "Failed to add event attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_SUPPORTED_EVENTS value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT_ATTRIBUTES"); + } + + return hr; +} + + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_EVENT_PARAMETER_ATTRIBUTES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER: Identifies the parameter whose attributes are being requested + * + * The driver should: + * - Return an IPortableDeviceValues in WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT_PARAMETER_ATTRIBUTES, containing + * the parameter attributes. If no attributes are available for this parameter, the driver should + * return an IPortableDeviceValues with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetEventParameterAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + PROPERTYKEY Parameter = WPD_PROPERTY_NULL; + + CComPtr<IPortableDeviceValues> pAttributes; + + // Get the method + hr = pParams->GetKeyValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER, &Parameter); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER"); + + if (hr == S_OK) + { + // CoCreate a collection to store the parameter attributes. + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetEventParameterAttributes(Parameter, pAttributes); + CHECK_HR(hr, "Failed to get the event parameter attributes"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES, pAttributes); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_PARAMETER_ATTRIBUTES"); + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_SERVICE_CAPABILITIES_GET_INHERITED_SERVICES command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITANCE_TYPE: Indicates the inheritance type the caller is interested in + * Possible values are from the WPD_SERVICE_INHERITANCE_TYPES enumeration + * + * The driver should: + * - Return an IPortableDevicePropVariantCollection in WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITED_SERVICES, containing + * the inherited services. For WPD_SERVICE_INHERITANCE_IMPLEMENTATION, this will be an + * IPortableDevicePropVariantCollection (of type VT_CLSID) containing the inherited service type GUIDs. + * If there are no inherited services, the driver should return an IPortableDevicePropVariantCollection with no elements in it. + */ +HRESULT WpdServiceCapabilities::OnGetInheritedServices( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + DWORD dwInheritanceType = 0; + + CComPtr<IPortableDevicePropVariantCollection> pServices; + + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITANCE_TYPE, &dwInheritanceType); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITANCE_TYPE"); + + if (hr == S_OK) + { + // CoCreate a collection to store the attributes. + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pServices); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + hr = m_pContactsService->GetInheritedServices(dwInheritanceType, pServices); + CHECK_HR(hr, "Failed to add inherited services"); + } + + // Set the WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITED_SERVICES value in the results. + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_SERVICE_CAPABILITIES_INHERITED_SERVICES, pServices); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_SERVICE_CAPABILITIES_EVENT_ATTRIBUTES"); + } + + return hr; +} + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.h b/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.h new file mode 100644 index 00000000..cffa2361 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceCapabilities.h @@ -0,0 +1,76 @@ +#pragma once + +class WpdServiceCapabilities +{ +public: + WpdServiceCapabilities(); + virtual ~WpdServiceCapabilities(); + + HRESULT Initialize(_In_ FakeContactsService* pContactsService); + + HRESULT DispatchWpdMessage( + _In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT OnGetSupportedCommands( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetCommandOptions( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedMethods( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedMethodsByFormat( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetMethodAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetMethodParameterAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedFormats( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFormatAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedFormatProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetFormatPropertyAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedEvents( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetEventAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetEventParameterAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetInheritedServices( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + FakeContactsService* m_pContactsService; +}; + 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; +} diff --git a/wpd/WpdServiceSampleDriver/WpdServiceMethods.h b/wpd/WpdServiceSampleDriver/WpdServiceMethods.h new file mode 100644 index 00000000..0e607331 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceMethods.h @@ -0,0 +1,151 @@ +#pragma once + +class CMethodTask; + +// This class is used to store the context for a specific method invocation +class ServiceMethodContext : public IUnknown +{ +public: + ServiceMethodContext(); + ~ServiceMethodContext(); + + HRESULT Initialize( + _In_ WpdServiceMethods* pServiceMethods, + _In_ IPortableDeviceValues* pStartParams, + _In_ LPCWSTR pwszContext); + + VOID InvokeMethod(); + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +public: + HRESULT m_hrStatus; + CComPtr<IPortableDeviceValues> m_pResults; + +private: + DWORD m_cRef; + CAtlStringW m_strContext; + CMethodTask* m_pTask; + CComPtr<IPortableDeviceValues> m_pStartParameters; + WpdServiceMethods* m_pServiceMethods; +}; + + +class CMethodTask +{ +public: + CMethodTask(_In_ ServiceMethodContext* pContext); + + ~CMethodTask(); + + HRESULT Run(); + + static DWORD ThreadProc(LPVOID pData) + { + // Initialize COM + if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + { + ServiceMethodContext* pContext = (ServiceMethodContext*) pData; + if (pContext != NULL) + { + pContext->AddRef(); + pContext->InvokeMethod(); + pContext->Release(); + } + + // Uninitialize COM + CoUninitialize(); + } + return 0; + } +private: + HANDLE m_hThread; + ServiceMethodContext* m_pContext; +}; + +class WpdServiceMethods +{ +public: + WpdServiceMethods(); + virtual ~WpdServiceMethods(); + + HRESULT Initialize( + _In_ FakeContactsService* pContactsService); + + // Handler for WPD_COMMAND_SERVICE_METHODS_START_INVOKE + HRESULT OnStartInvoke( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + // Handler for WPD_COMMAND_SERVICE_METHODS_END_INVOKE + HRESULT OnEndInvoke( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + // Handler for WPD_COMMAND_SERVICE_METHODS_CANCEL_INVOKE + HRESULT OnCancelInvoke( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT DispatchMethod( + _In_ LPCWSTR pwszContext, + _In_ IPortableDeviceValues* pStartParams, + _COM_Outptr_ IPortableDeviceValues** ppResults); + +private: + HRESULT StartMethod( + _In_ IPortableDeviceValues* pCommandParams, + _Outptr_result_nullonfailure_ LPWSTR* ppwszMethodContext); + + HRESULT EndMethod( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pwszMethodContext, + _COM_Outptr_result_maybenull_ IPortableDeviceValues** ppResults, + _Out_ HRESULT* phrStatus); + + HRESULT CancelMethod( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pwszMethodContext); + +private: + FakeContactsService* m_pContactsService; +}; + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.cpp b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.cpp new file mode 100644 index 00000000..d2bf9d73 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "resource.h" +#include "WpdServiceSampleDriver.h" + +#include "WpdServiceSampleDriver.tmh" + +HINSTANCE g_hInstance = NULL; + +class CWpdServiceSampleDriverModule : public CAtlDllModuleT< CWpdServiceSampleDriverModule > +{ +public : + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_WpdServiceSampleDriver, "{95B558CB-F6B1-4B37-A105-3B7B6A196FB5}") + DECLARE_LIBID(LIBID_WpdServiceSampleDriverLib) +}; + +CWpdServiceSampleDriverModule _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + if(dwReason == DLL_PROCESS_ATTACH) + { + g_hInstance = hInstance; + + // Initialize tracing. + WPP_INIT_TRACING(MYDRIVER_TRACING_ID); + } + else if (dwReason == DLL_PROCESS_DETACH) + { + // Cleanup tracing. + WPP_CLEANUP(); + } + + return _AtlModule.DllMain(dwReason, lpReserved); +} + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +// DllRegisterServer - Adds entries to the system registry +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.def b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.def new file mode 100644 index 00000000..47cf750d --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.def @@ -0,0 +1,9 @@ +; WpdServiceSampleDriver.def : Declares the module parameters. + +LIBRARY "WpdServiceSampleDriver.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.idl b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.idl new file mode 100644 index 00000000..75102566 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.idl @@ -0,0 +1,24 @@ + +import "oaidl.idl"; +import "ocidl.idl"; + +import "wudfddi.idl"; + +[ + uuid(74FF6859-73D1-488C-B2EA-88BD6DA31A17), + version(1.0), + helpstring("Windows Portable Device Services Sample Driver Type Library") +] +library WpdServiceSampleDriverLib +{ + importlib("stdole2.tlb"); + [ + uuid(CFC0AF02-CE72-4717-83E7-D51BCFCBE87B), + helpstring("WpdServiceSampleDriver Class") + ] + coclass WpdServiceSampleDriver + { + [default] interface IDriverEntry; + }; +}; + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx new file mode 100644 index 00000000..6b9990c8 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.inx @@ -0,0 +1,79 @@ +; +; WpdServiceSampleDriver.inf +; + +[Version] +Signature="$Windows NT$" +Class=WPD +ClassGuid={EEC5AD98-8080-425f-922A-DABF3DE3F69A} +Provider=%Provider% +CatalogFile=WpdServiceSampleDriver.cat +DriverVer=01/24/2007,1.1.1.1 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%BasicDeviceName%=Basic_Install,WUDF\WpdService + +[SourceDisksFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 +WpdServiceSampleDriver.dll=1 + +[SourceDisksNames] +1 = %MediaDescription% + +; =================== WPD Sample Device ================================== + +[Basic_Install] +CopyFiles=System32Copy + +[Basic_Install.hw] +AddReg=Device_AddReg + +[Basic_Install.Services] +AddService=WUDFRd,0x000001fa,WUDFRD_ServiceInstall + +[Basic_Install.CoInstallers] +AddReg=Basic_Install.CoInstallers_AddReg +CopyFiles = CoInstallers_CopyFiles + +[Basic_Install.CoInstallers_AddReg] +HKR,,CoInstallers32,0x00010000,"WudfUpdate_$UMDFCOINSTALLERVERSION$.dll" + +[Basic_Install.Wdf] +UmdfService=WpdServiceSampleDriver, WpdServiceSampleDriver_Install +UmdfServiceOrder=WpdServiceSampleDriver + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[WpdServiceSampleDriver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{CFC0AF02-CE72-4717-83E7-D51BCFCBE87B}" +ServiceBinary=%12%\UMDF\WpdServiceSampleDriver.dll + +[Device_AddReg] +; Enable default AutoPlay support +HKR,,"EnableDefaultAutoPlaySupport",0x10001,1 + +[WUDFRD_ServiceInstall] +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\WUDFRd.sys + +[DestinationDirs] +System32Copy=12,UMDF ; copy to system32\drivers\umdf + +[System32Copy] +WpdServiceSampleDriver.dll + + +; =================== Generic ================================== + +[Strings] +MSFTWUDF="Microsoft Windows Portable Devices" +Provider="Microsoft WPD" +MediaDescription="Windows Portable Device Services Sample Driver Installation Media" +BasicDeviceName="Windows Portable Device Services Sample Driver" diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rc b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rc new file mode 100644 index 00000000..02cbfa08 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rc @@ -0,0 +1,18 @@ +#include "resource.h" +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Windows Portable Device Services Sample Driver" +#define VER_INTERNALNAME_STR "WpdServiceSampleDriver.dll" + +#include <common.ver> + +IDR_WPD_SAMPLEDRIVER_DEVICE_ICON DATA_FILE "SampleDeviceIcon.ico" +IDR_WPD_SAMPLEDRIVER_SERVICE_ICON DATA_FILE "SampleContactsServiceIcon.ico" + +1 TYPELIB "WpdServiceSampleDriver.tlb" + +IDR_WpdServiceSampleDriver REGISTRY "WpdServiceSampleDriver.rgs" + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rgs b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rgs new file mode 100644 index 00000000..d0de4d3b --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.rgs @@ -0,0 +1,26 @@ +HKCR +{ + WpdServiceSampleDriver.WpdServiceSampleDriver.1 = s 'WpdServiceSampleDriver Class' + { + CLSID = s '{CFC0AF02-CE72-4717-83E7-D51BCFCBE87B}' + } + WpdServiceSampleDriver.WpdServiceSampleDriver = s 'WpdServiceSampleDriver Class' + { + CLSID = s '{CFC0AF02-CE72-4717-83E7-D51BCFCBE87B}' + CurVer = s 'WpdServiceSampleDriver.WpdServiceSampleDriver.1' + } + NoRemove CLSID + { + ForceRemove {CFC0AF02-CE72-4717-83E7-D51BCFCBE87B} = s 'WpdServiceSampleDriver Class' + { + ProgID = s 'WpdServiceSampleDriver.WpdServiceSampleDriver.1' + VersionIndependentProgID = s 'WpdServiceSampleDriver.WpdServiceSampleDriver.1' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Free' + } + 'TypeLib' = s '{74FF6859-73D1-488C-B2EA-88BD6DA31A17}' + } + } +} + diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln new file mode 100644 index 00000000..5106e4d4 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0 +MinimumVisualStudioVersion = 12.0 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WpdServiceSampleDriver", "WpdServiceSampleDriver.vcxproj", "{31C8E589-7E7D-4F83-BE09-7831A94E3D07}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Debug|Win32.ActiveCfg = Debug|Win32 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Debug|Win32.Build.0 = Debug|Win32 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Release|Win32.ActiveCfg = Release|Win32 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Release|Win32.Build.0 = Release|Win32 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Debug|x64.ActiveCfg = Debug|x64 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Debug|x64.Build.0 = Debug|x64 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Release|x64.ActiveCfg = Release|x64 + {31C8E589-7E7D-4F83-BE09-7831A94E3D07}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj new file mode 100644 index 00000000..fc4d1554 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj @@ -0,0 +1,449 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{31C8E589-7E7D-4F83-BE09-7831A94E3D07}</ProjectGuid> + <RootNamespace>$(MSBuildProjectName)</RootNamespace> + <UMDF_VERSION_MAJOR>1</UMDF_VERSION_MAJOR> + <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> + <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> + <Platform Condition="'$(Platform)' == ''">Win32</Platform> + <SampleGuid>{1777FF3E-D86C-4053-B3FE-DB0C55459BAA}</SampleGuid> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>False</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetVersion>Windows10</TargetVersion> + <UseDebugLibraries>True</UseDebugLibraries> + <DriverTargetPlatform>Desktop</DriverTargetPlatform> + <DriverType>UMDF</DriverType> + <PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset> + <ConfigurationType>DynamicLibrary</ConfigurationType> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <PropertyGroup> + <OutDir>$(IntDir)</OutDir> + </PropertyGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" /> + </ImportGroup> + <ItemGroup Label="WrappedTaskItems"> + <ClCompile Include="WpdServiceSampleDriver.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="Driver.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="Device.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="Queue.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdBaseDriver.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdObjectEnum.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdObjectManagement.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdObjectProperties.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdObjectPropertiesBulk.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdObjectResources.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdCapabilities.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdService.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdServiceMethods.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdServiceCapabilities.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="helpers.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeContent.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeContactsService.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeContactsServiceContent.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeStorage.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeContactContent.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeDeviceContent.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="FakeDevice.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <Inf Include="WpdServiceSampleDriver.inx"> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\WpdServiceSampleDriver.inf</CopyOutput> + </Inf> + <OtherWpp Include="WpdServiceSampleDriver.rc; WpdServiceSampleDriver.idl"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + </OtherWpp> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>WpdServiceSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>WpdServiceSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>WpdServiceSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>WpdServiceSampleDriver</TargetName> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <EntryPointSymbol Condition="'$(Platform)'=='win32'">_DllMainCRTStartup@12</EntryPointSymbol> + <EntryPointSymbol Condition="'$(Platform)'!='win32'">_DllMainCRTStartup</EntryPointSymbol> + </Link> + </ItemDefinitionGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <UseOfAtl>Dynamic</UseOfAtl> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling>Sync</ExceptionHandling> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling>Sync</ExceptionHandling> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling>Sync</ExceptionHandling> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <TreatWarningAsError>true</TreatWarningAsError> + <WarningLevel>Level4</WarningLevel> + <ExceptionHandling>Sync</ExceptionHandling> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories> + </Midl> + <Link> + <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Link> + <ModuleDefinitionFile>WpdServiceSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <ModuleDefinitionFile>WpdServiceSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <ModuleDefinitionFile>WpdServiceSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <ModuleDefinitionFile>WpdServiceSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="Stdafxsrc.cpp"> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Create</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <Midl Include="WpdServiceSampleDriver.idl" /> + <ResourceCompile Include="WpdServiceSampleDriver.rc" /> + </ItemGroup> + <ItemGroup> + <Inf Exclude="@(Inf)" Include="*.inf" /> + <FilesToPackage Include="$(TargetPath)" Condition="'$(ConfigurationType)'=='Driver' or '$(ConfigurationType)'=='DynamicLibrary'" /> + <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" /> + </ItemGroup> + <ItemGroup> + <None Exclude="@(None)" Include="*.txt;*.htm;*.html" /> + <None Exclude="@(None)" Include="*.ico;*.cur;*.bmp;*.dlg;*.rct;*.gif;*.jpg;*.jpeg;*.wav;*.jpe;*.tiff;*.tif;*.png;*.rc2" /> + <None Exclude="@(None)" Include="*.def;*.bat;*.hpj;*.asmx" /> + </ItemGroup> + <ItemGroup> + <ClInclude Exclude="@(ClInclude)" Include="*.h;*.hpp;*.hxx;*.hm;*.inl;*.xsd" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> +</Project>
\ No newline at end of file diff --git a/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters new file mode 100644 index 00000000..bae147dc --- /dev/null +++ b/wpd/WpdServiceSampleDriver/WpdServiceSampleDriver.vcxproj.Filters @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> + <UniqueIdentifier>{EE35ADBF-437F-44A1-AEB4-3D19EF5C2780}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{410E5228-572D-4CA9-9BE0-7E6253581D2C}</UniqueIdentifier> + </Filter> + <Filter Include="Resource Files"> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> + <UniqueIdentifier>{9CC2DED3-ECF7-44E8-9B14-0E15C5605D38}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{EFFDD7BB-33E7-46CF-970E-ECC2FEA081D5}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="Device.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Driver.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeContactContent.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeContactsService.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeContactsServiceContent.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeContent.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeDevice.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeDeviceContent.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="FakeStorage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="helpers.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Queue.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Stdafxsrc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdBaseDriver.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdCapabilities.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdObjectEnum.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdObjectManagement.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdObjectProperties.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdObjectPropertiesBulk.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdObjectResources.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdService.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdServiceCapabilities.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdServiceMethods.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdServiceSampleDriver.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <Midl Include="WpdServiceSampleDriver.idl"> + <Filter>Source Files</Filter> + </Midl> + <None Include="WpdServiceSampleDriver.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\WpdServiceSampleDriver.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include="WpdServiceSampleDriver.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="WpdServiceSampleDriver.rc"> + <Filter>Resource Files</Filter> + </ResourceCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="WpdObjectResources.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/wpd/WpdServiceSampleDriver/helpers.cpp b/wpd/WpdServiceSampleDriver/helpers.cpp new file mode 100644 index 00000000..49ed66fc --- /dev/null +++ b/wpd/WpdServiceSampleDriver/helpers.cpp @@ -0,0 +1,700 @@ +#include "stdafx.h" + +#include "helpers.tmh" + +HRESULT UpdateDeviceFriendlyName( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + _In_ LPCWSTR wszDeviceFriendlyName) +{ + HRESULT hr = S_OK; + + // If we were passed NULL parameters we have nothing to do, return S_OK. + if ((pPortableDeviceClassExtension == NULL) || + (wszDeviceFriendlyName == NULL)) + { + return S_OK; + } + + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + CComPtr<IPortableDeviceValues> pValues; + + // Prepare to make a call to set the device information + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + // Get the information values to update and set them in WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES + if (hr == S_OK) + { + hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, wszDeviceFriendlyName); + CHECK_HR(hr, ("Failed to set WPD_DEVICE_FRIENDLY_NAME")); + } + + // Set the params + if (hr == S_OK) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_CLASS_EXTENSION_WRITE_DEVICE_INFORMATION.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + if (hr == S_OK) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_CLASS_EXTENSION_WRITE_DEVICE_INFORMATION.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + if (hr == S_OK) + { + hr = pParams->SetIPortableDeviceValuesValue(WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES, pValues); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CLASS_EXTENSION_DEVICE_INFORMATION_VALUES")); + } + + // Make the call + if (hr == S_OK) + { + hr = pPortableDeviceClassExtension->ProcessLibraryMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to process update device information message")); + } + + // A Failed ProcessLibraryMessage operation for updating this value is not considered + // fatal and should return S_OK. + + return S_OK; +} + +HRESULT RegisterServices( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + const bool bUnregister) +{ + // If we were passed NULL parameters we have nothing to do, return S_OK. + if (pPortableDeviceClassExtension == NULL) + { + return S_OK; + } + + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + CComPtr<IPortableDevicePropVariantCollection> pInterfaces; + + PROPERTYKEY commandToUse = bUnregister? + WPD_COMMAND_CLASS_EXTENSION_UNREGISTER_SERVICE_INTERFACES: + WPD_COMMAND_CLASS_EXTENSION_REGISTER_SERVICE_INTERFACES; + + // Prepare to make a call to register the services + HRESULT hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**)&pInterfaces); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection for interfaces"); + } + + // Get the interfaces values to register and set them in WPD_PROPERTY_CLASS_EXTENSION_SERVICE_INTERFACES + if (hr == S_OK) + { + PROPVARIANT pv; + PropVariantInit(&pv); + pv.vt = VT_CLSID; + + pv.puuid = (CLSID*)&SERVICE_FullEnumSync; + hr = pInterfaces->Add(&pv); + CHECK_HR(hr, "Failed to add EnumerationSyncService to the list of requested interfaces"); + + pv.puuid = (CLSID*)&SERVICE_Contacts; + hr = pInterfaces->Add(&pv); + CHECK_HR(hr, "Failed to add ContactsSyncService to the list of requested interfaces"); + + // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs + } + + // Set the params + if (hr == S_OK) + { + hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, commandToUse.fmtid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY")); + } + if (hr == S_OK) + { + hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, commandToUse.pid); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID")); + } + + if (hr == S_OK) + { + hr = pParams->SetStringValue(WPD_PROPERTY_CLASS_EXTENSION_SERVICE_OBJECT_ID, CONTACTS_SERVICE_OBJECT_ID); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CLASS_EXTENSION_SERVICE_OBJECT_ID")); + } + + if (hr == S_OK) + { + hr = pParams->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_CLASS_EXTENSION_SERVICE_INTERFACES, pInterfaces); + CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CLASS_EXTENSION_SERVICE_INTERFACES")); + } + + // Make the call + if (hr == S_OK) + { + hr = pPortableDeviceClassExtension->ProcessLibraryMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to process update device information message")); + } + + return hr; +} + +DWORD GetResourceSize( + const UINT uiResource) +{ + HRESULT hr = S_OK; + LONG lError = ERROR_SUCCESS; + DWORD dwResourceSize = 0; + + HRSRC hResource = FindResource(g_hInstance, MAKEINTRESOURCE(uiResource), TEXT("DATA_FILE")); + if (hResource) + { + HGLOBAL hGlobal = LoadResource(g_hInstance, hResource); + if (hGlobal) + { + dwResourceSize = SizeofResource(g_hInstance, hResource); + } + else + { + lError = GetLastError(); + hr = HRESULT_FROM_WIN32(lError); + } + } + else + { + lError = GetLastError(); + hr = HRESULT_FROM_WIN32(lError); + } + + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get resource size for '%d'", uiResource); + } + + return dwResourceSize; +} + +PBYTE GetResourceData( + const UINT uiResource) +{ + HRESULT hr = S_OK; + LONG lError = ERROR_SUCCESS; + PBYTE pData = NULL; + + HRSRC hResource = FindResource(g_hInstance, MAKEINTRESOURCE(uiResource), TEXT("DATA_FILE")); + if (hResource) + { + HGLOBAL hGlobal = LoadResource(g_hInstance, hResource); + if (hGlobal) + { + pData = static_cast<BYTE*>(LockResource(hGlobal)); + } + else + { + lError = GetLastError(); + hr = HRESULT_FROM_WIN32(lError); + } + } + else + { + lError = GetLastError(); + hr = HRESULT_FROM_WIN32(lError); + } + + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get resource data pointer for '%d'", uiResource); + } + + return pData; +} + + +HRESULT AddStringValueToPropVariantCollection( + _In_ IPortableDevicePropVariantCollection* pCollection, + _In_ LPCWSTR wszValue) +{ + HRESULT hr = S_OK; + + if ((pCollection == NULL) || + (wszValue == NULL)) + { + hr = E_INVALIDARG; + return hr; + } + + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + + pv.vt = VT_LPWSTR; + pv.pwszVal = (LPWSTR)wszValue; + + // The wszValue will be copied into the collection, keeping the ownership + // of the string belonging to the caller. + // Don't call PropVariantClear, since we did not allocate the memory for these string values + + hr = pCollection->Add(&pv); + + return hr; +} + +HRESULT GetClientContextMap( + _In_ IPortableDeviceValues* pParams, + _Outptr_ ContextMap** ppContextMap) +{ + HRESULT hr = S_OK; + + if(ppContextMap == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**) ppContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + + return hr; +} + +HRESULT GetClientContext( + _In_ IPortableDeviceValues* pParams, + _In_ LPCWSTR pszContextKey, + _COM_Outptr_ IUnknown** ppContext) +{ + HRESULT hr = S_OK; + ContextMap* pContextMap = NULL; + + if(ppContext == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppContext = NULL; + + hr = GetClientContextMap(pParams, &pContextMap); + CHECK_HR(hr, ("Failed to get the client context map")); + + if (SUCCEEDED(hr) && pContextMap == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Client context map is NULL")); + } + + if (SUCCEEDED(hr)) + { + *ppContext = pContextMap->GetContext(pszContextKey); + if(*ppContext == NULL) + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + CHECK_HR(hr, "Failed to find context %ws for this client", pszContextKey); + } + } + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT GetClientEventCookie( + _In_ IPortableDeviceValues* pParams, + _Outptr_result_maybenull_ LPWSTR* ppszEventCookie) +{ + HRESULT hr = S_OK; + LPWSTR pszClientContext = NULL; + ClientContext* pClientContext = NULL; + + if ((pParams == NULL) || + (ppszEventCookie == NULL)) + { + return E_POINTER; + } + + *ppszEventCookie = NULL; + + hr = pParams->GetStringValue(WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT, &pszClientContext); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT"); + + if (SUCCEEDED(hr)) + { + // Get the client context for this request. + hr = GetClientContext(pParams, pszClientContext, (IUnknown**)&pClientContext); + CHECK_HR(hr, "Failed to get the client context"); + } + + if (SUCCEEDED(hr) && (pClientContext->EventCookie.GetLength() > 0)) + { + // Get the event cookie only if it has been set + *ppszEventCookie = AtlAllocTaskWideString(pClientContext->EventCookie); + if (*ppszEventCookie == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate the client event cookie"); + } + } + + // We're done with the context + SAFE_RELEASE(pClientContext); + + CoTaskMemFree(pszClientContext); + pszClientContext = NULL; + + return hr; +} + + +HRESULT PostWpdEvent( + _In_ IPortableDeviceValues* pCommandParams, + _In_ IPortableDeviceValues* pEventParams) +{ + HRESULT hr = S_OK; + BYTE* pBuffer = NULL; + DWORD cbBuffer = 0; + LPWSTR pszEventCookie = NULL; + + CComPtr<IWDFDevice> pDevice; + CComPtr<IWpdSerializer> pSerializer; + + // Get the WUDF Device Object + hr = pCommandParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, (IUnknown**) &pDevice); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT"); + + // Get the WpdSerializer Object + if (hr == S_OK) + { + hr = pCommandParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, (IUnknown**) &pSerializer); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT"); + } + + if (hr == S_OK) + { + // Set the client event cookie if available. This is benign, as some clients may not provide a cookie. + HRESULT hrEventCookie = GetClientEventCookie(pCommandParams, &pszEventCookie); + if ((hrEventCookie == S_OK) && (pszEventCookie != NULL)) + { + hrEventCookie = pEventParams->SetStringValue(WPD_CLIENT_EVENT_COOKIE, pszEventCookie); + CHECK_HR(hrEventCookie, "Failed to set WPD_CLIENT_EVENT_COOKIE (error ignored)"); + } + } + + if (hr == S_OK) + { + // Create a buffer with the serialized parameters + hr = pSerializer->GetBufferFromIPortableDeviceValues(pEventParams, &pBuffer, &cbBuffer); + CHECK_HR(hr, "Failed to get buffer from IPortableDeviceValues"); + } + + // Send the event + if (hr == S_OK && pBuffer != NULL) + { + hr = pDevice->PostEvent(WPD_EVENT_NOTIFICATION, WdfEventBroadcast, pBuffer, cbBuffer); + CHECK_HR(hr, "Failed to post WPD (broadcast) event"); + } + + // Free the memory + CoTaskMemFree(pBuffer); + pBuffer = NULL; + + CoTaskMemFree(pszEventCookie); + pszEventCookie = NULL; + + return hr; +} + +HRESULT AddPropertyAttributesByType( + const FakeDevicePropertyAttributesType type, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = S_OK; + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (type == UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + else + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + } + else + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + + } + + return hr; +} + +#define WPD_PROPERTY_ATTRIBUTE_MAX_SIZE_VALUE 1024 +HRESULT SetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_reads_(cAttributeInfo) const PropertyAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex=0; dwIndex<cAttributeInfo; dwIndex++) + { + if (IsEqualPropertyKey(Key, *(AttributeInfo[dwIndex].pKey))) + { + // Set vartype + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_VARTYPE, AttributeInfo[dwIndex].Vartype); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_VARTYPE"); + + // Set name + if (hr == S_OK && AttributeInfo[dwIndex].wszName != NULL) + { + hr = pAttributes->SetStringValue(WPD_PROPERTY_ATTRIBUTE_NAME, AttributeInfo[dwIndex].wszName); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_NAME"); + } + + // Set max size for string properties + if (hr == S_OK && AttributeInfo[dwIndex].Vartype == VT_LPWSTR) + { + hr = pAttributes->SetUnsignedLargeIntegerValue(WPD_PROPERTY_ATTRIBUTE_MAX_SIZE, WPD_PROPERTY_ATTRIBUTE_MAX_SIZE_VALUE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_MAX_SIZE"); + } + + // Set access attributes + if (hr == S_OK) + { + hr = AddPropertyAttributesByType(AttributeInfo[dwIndex].AttributesType, pAttributes); + CHECK_HR(hr, "Failed to set common property attributes"); + } + + break; + } + } + + return hr; +} + +HRESULT SetMethodParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_reads_(cAttributeInfo) const MethodParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex=0; dwIndex<cAttributeInfo; dwIndex++) + { + if (IsEqualPropertyKey(Parameter, *AttributeInfo[dwIndex].pKey)) + { + // Set vartype + hr = pAttributes->SetUnsignedIntegerValue(WPD_PARAMETER_ATTRIBUTE_VARTYPE, AttributeInfo[dwIndex].Vartype); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_VARTYPE"); + + // Set form + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PARAMETER_ATTRIBUTE_FORM, (DWORD)AttributeInfo[dwIndex].Form); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_FORM"); + } + + // Set order + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PARAMETER_ATTRIBUTE_ORDER, AttributeInfo[dwIndex].Order); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_ORDER"); + } + + // Set usage + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PARAMETER_ATTRIBUTE_USAGE, AttributeInfo[dwIndex].UsageType); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_USAGE"); + } + + // Set name + if (hr == S_OK) + { + hr = pAttributes->SetStringValue(WPD_PARAMETER_ATTRIBUTE_NAME, AttributeInfo[dwIndex].wszName); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_NAME"); + } + + break; + } + } + + return hr; +} + +HRESULT SetEventParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_reads_(cAttributeInfo) const EventParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex=0; dwIndex<cAttributeInfo; dwIndex++) + { + if (IsEqualPropertyKey(Parameter, *AttributeInfo[dwIndex].pParameter)) + { + // Set vartype + hr = pAttributes->SetUnsignedIntegerValue(WPD_PARAMETER_ATTRIBUTE_VARTYPE, AttributeInfo[dwIndex].Vartype); + CHECK_HR(hr, "Failed to set WPD_PARAMETER_ATTRIBUTE_VARTYPE"); + break; + } + } + + return hr; + +} + +HRESULT SetEventParameters( + _In_ REFGUID Event, + _In_reads_(cAttributeInfo) const EventParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceKeyCollection* pParameters) +{ + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (pParameters == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + for (DWORD dwIndex=0; dwIndex<cAttributeInfo; dwIndex++) + { + GUID guidEvent = *AttributeInfo[dwIndex].pEventGuid; + PROPERTYKEY param = *AttributeInfo[dwIndex].pParameter; + + if (guidEvent == Event) + { + hr = pParameters->Add(param); + CHECK_HR(hr, "Failed to add event parameter to collection"); + } + } + + return hr; +} diff --git a/wpd/WpdServiceSampleDriver/helpers.h b/wpd/WpdServiceSampleDriver/helpers.h new file mode 100644 index 00000000..5ea3967b --- /dev/null +++ b/wpd/WpdServiceSampleDriver/helpers.h @@ -0,0 +1,459 @@ +#pragma once + +#ifndef SAFE_RELEASE + #define SAFE_RELEASE(p) if( NULL != p ) { ( p )->Release(); p = NULL; } +#endif + +// {CDD18979-A7B0-4D5E-9EB2-0A826805CBBD} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, 0xCDD18979, 0xA7B0, 0x4D5E, 0x9E, 0xB2, 0x0A, 0x82, 0x68, 0x05, 0xCB, 0xBD, 2); +// {9BD949E5-59CF-41AE-90A9-BE1D044F578F} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, 0x9BD949E5, 0x59CF, 0x41AE, 0x90, 0xA9, 0xBE, 0x1D, 0x04, 0x4F, 0x57, 0x8F, 2); +// {4DF6C8C7-2CE5-457C-9F53-EFCECAA95C04} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, 0x4DF6C8C7, 0x2CE5, 0x457C, 0x9F, 0x53, 0xEF, 0xCE, 0xCA, 0xA9, 0x5C, 0x04, 2); +// {67BA8D9E-1DC4-431C-B89C-9D03F7D8C223} +DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_REQUEST_FILENAME, 0x67BA8D9E, 0x1DC4, 0x431C, 0xB8, 0x9C, 0x9D, 0x03, 0xF7, 0xD8, 0xC2, 0x23, 2); + +// Service event and parameters +// {D93102D5-8FED-4A39-AF84-228FE15888D0} +DEFINE_GUID(MyCustomEvent, 0xD93102D5, 0x8FED, 0x4A39, 0xAF, 0x84, 0x22, 0x8F, 0xE1, 0x58, 0x88, 0xD0); +// {D93102D5-8FED-4A39-AF84-228FE15888D0}.2 +DEFINE_PROPERTYKEY(MyCustomEventParam0, 0xD93102D5, 0x8FED, 0x4A39, 0xAF, 0x84, 0x22, 0x8F, 0xE1, 0x58, 0x88, 0xD0, 2); +// {D93102D5-8FED-4A39-AF84-228FE15888D0}.3 +DEFINE_PROPERTYKEY(MyCustomEventParam1, 0xD93102D5, 0x8FED, 0x4A39, 0xAF, 0x84, 0x22, 0x8F, 0xE1, 0x58, 0x88, 0xD0, 3); + +// Service method and parameters +// {ECFC865F-7B43-4D76-9C6B-D67309A3F6F7} +DEFINE_GUID(MyCustomMethod, 0xECFC865F, 0x7B43, 0x4D76, 0x9C, 0x6B, 0xD6, 0x73, 0x09, 0xA3, 0xF6, 0xF7); +// {ECFC865F-7B43-4D76-9C6B-D67309A3F6F7}.2 +DEFINE_PROPERTYKEY(MyCustomMethodResult, 0xECFC865F, 0x7B43, 0x4D76, 0x9C, 0x6B, 0xD6, 0x73, 0x09, 0xA3, 0xF6, 0xF7, 2); +// {ECFC865F-7B43-4D76-9C6B-D67309A3F6F7}.3 +DEFINE_PROPERTYKEY(MyCustomMethodParam, 0xECFC865F, 0x7B43, 0x4D76, 0x9C, 0x6B, 0xD6, 0x73, 0x09, 0xA3, 0xF6, 0xF7, 3); +// {ECFC865F-7B43-4D76-9C6B-D67309A3F6F7}.4 +DEFINE_PROPERTYKEY(MyCustomMethodParamInOut, 0xECFC865F, 0x7B43, 0x4D76, 0x9C, 0x6B, 0xD6, 0x73, 0x09, 0xA3, 0xF6, 0xF7, 4); + +// Contact versioning property +// {2B0D5AA4-7EB3-4674-BD36-23FE4C39A2C2}.2 +DEFINE_PROPERTYKEY(MyContactVersionIdentifier, 0x2B0D5AA4, 0x7EB3, 0x4674, 0xBD, 0x36, 0x23, 0xFE, 0x4C, 0x39, 0xA2, 0xC2, 2); + +// Full Enumeration Sync Replica ID +// {81176f1e-2c42-4b4e-8f79-bc1a7f3da046} +DEFINE_GUID(MyFullEnumSyncReplicaId, 0x81176f1e, 0x2c42, 0x4b4e, 0x8f, 0x79, 0xbc, 0x1a, 0x7f, 0x3d, 0xa0, 0x46); + +// Access Scope is a bit mask, where each bit enables access to a particular scope +// for example, contacts service is bit 1. +// The next scope, if any, will be in bit 2 +// Full device access is a combination of all, requires all bits to be set +typedef enum tagACCESS_SCOPE +{ + CONTACTS_SERVICE_ACCESS = 1, + FULL_DEVICE_ACCESS = 0xFFFFFFFF +}ACCESS_SCOPE; + +typedef enum tagFakeDevicePropertyAttributesType +{ + UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, + UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, +} FakeDevicePropertyAttributesType; + +typedef struct tagPropertyAttributeInfo +{ + const PROPERTYKEY* pKey; + VARTYPE Vartype; + FakeDevicePropertyAttributesType AttributesType; + PCWSTR wszName; +} PropertyAttributeInfo; + +typedef struct tagMethodParameterAttributeInfo +{ + const PROPERTYKEY* pKey; + VARTYPE Vartype; + WPD_PARAMETER_USAGE_TYPES UsageType; + WpdParameterAttributeForm Form; + DWORD Order; + PCWSTR wszName; +} MethodParameterAttributeInfo; + +typedef struct tagEventParameterAttributeInfo +{ + const GUID* pEventGuid; + const PROPERTYKEY* pParameter; + VARTYPE Vartype; +} EventParameterAttributeInfo; + +typedef struct tagFormatAttributeInfo +{ + const GUID* pFormatGuid; + PCWSTR wszName; +} FormatAttributeInfo; + + +class ContextMap : public IUnknown +{ +public: + ContextMap() : + m_cRef(1) + { + + } + + ~ContextMap() + { + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + + IUnknown* pUnk = NULL; + POSITION elementPosition = NULL; + + elementPosition = m_Map.GetStartPosition(); + while(elementPosition != NULL) + { + pUnk = m_Map.GetNextValue(elementPosition); + if(pUnk != NULL) + { + pUnk->Release(); + } + } + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + return hr; + } + + +public: // Context accessor methods + + // If successful, this method AddRef's the context and returns + // a context key + HRESULT Add( + _In_ IUnknown* pContext, + _Out_ CAtlStringW& key) + { + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + key = L""; + + // Create a unique context key + hr = CoCreateGuid(&guidContext); + if (hr == S_OK) + { + bstrContext = guidContext; + if(bstrContext.Length() > 0) + { + key = bstrContext; + } + else + { + hr = E_OUTOFMEMORY; + } + } + + if (hr == S_OK) + { + // Insert this into the map + POSITION elementPosition = m_Map.SetAt(key, pContext); + if(elementPosition != NULL) + { + // AddRef since we are holding onto it + pContext->AddRef(); + } + else + { + hr = E_OUTOFMEMORY; + } + } + return hr; + } + + void Remove( + const CAtlStringW& key) + { + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + // Get the element + IUnknown* pContext = NULL; + + if (m_Map.Lookup(key, pContext) == true) + { + // Remove the entry for it + m_Map.RemoveKey(key); + + // Release it + pContext->Release(); + } + } + + // Returns the context pointer. If not found, return value is NULL. + // If non-NULL, caller is responsible for Releasing when it is done, + // since this method will AddRef the context. + IUnknown* GetContext( + const CAtlStringW& key) + { + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + // Get the element + IUnknown* pContext = NULL; + + if (m_Map.Lookup(key, pContext) == true) + { + // AddRef + pContext->AddRef(); + } + return pContext; + } + +private: + CComAutoCriticalSection m_CriticalSection; + CAtlMap<CAtlStringW, IUnknown*> m_Map; + DWORD m_cRef; +}; + + +// This class is used to store the connected client information. +class ClientContext : public IUnknown +{ +public: + ClientContext() : + MajorVersion(0), + MinorVersion(0), + Revision(0), + m_cRef(1) + { + } + + ~ClientContext() + { + } + +public: // IUnknown + ULONG __stdcall AddRef() + { + InterlockedIncrement((long*) &m_cRef); + return m_cRef; + } + + _At_(this, __drv_freesMem(Mem)) + ULONG __stdcall Release() + { + ULONG ulRefCount = m_cRef - 1; + + if (InterlockedDecrement((long*) &m_cRef) == 0) + { + delete this; + return 0; + } + return ulRefCount; + } + + HRESULT __stdcall QueryInterface( + REFIID riid, + void** ppv) + { + HRESULT hr = S_OK; + + if(riid == IID_IUnknown) + { + *ppv = static_cast<IUnknown*>(this); + AddRef(); + } + else + { + *ppv = NULL; + hr = E_NOINTERFACE; + } + + return hr; + } + +private: + DWORD m_cRef; + +public: + CAtlStringW ClientName; + CAtlStringW EventCookie; + DWORD MajorVersion; + DWORD MinorVersion; + DWORD Revision; +}; + + +class PropVariantWrapper : public tagPROPVARIANT +{ +public: + PropVariantWrapper() + { + PropVariantInit(this); + } + + PropVariantWrapper(LPCWSTR pszSrc) + { + PropVariantInit(this); + + *this = pszSrc; + } + + virtual ~PropVariantWrapper() + { + Clear(); + } + + void Clear() + { + PropVariantClear(this); + } + + PropVariantWrapper& operator= (const ULONG ulValue) + { + Clear(); + vt = VT_UI4; + ulVal = ulValue; + + return *this; + } + + PropVariantWrapper& operator= (_In_ LPCWSTR pszSrc) + { + Clear(); + + pwszVal = AtlAllocTaskWideString(pszSrc); + if(pwszVal != NULL) + { + vt = VT_LPWSTR; + } + return *this; + } + + PropVariantWrapper& operator= (_In_ IUnknown* punkSrc) + { + Clear(); + + // Need to AddRef as PropVariantClear will Release + if (punkSrc != NULL) + { + vt = VT_UNKNOWN; + punkVal = punkSrc; + punkVal->AddRef(); + } + return *this; + } + + void SetErrorValue(const HRESULT hr) + { + Clear(); + vt = VT_ERROR; + scode = hr; + } + + void SetBoolValue(const bool bValue) + { + Clear(); + vt = VT_BOOL; + if(bValue) + { + boolVal = VARIANT_TRUE; + } + else + { + boolVal = VARIANT_FALSE; + } + } +}; + +HRESULT UpdateDeviceFriendlyName( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + _In_ LPCWSTR wszDeviceFriendlyName); + +HRESULT RegisterServices( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + const bool bUnregister); + +HRESULT CheckRequestFilename( + _In_ LPCWSTR pszRequestFilename); + +DWORD GetResourceSize( + const UINT uiResource); + +PBYTE GetResourceData( + const UINT uiResource); + +HRESULT AddStringValueToPropVariantCollection( + _In_ IPortableDevicePropVariantCollection* pCollection, + _In_ LPCWSTR wszValue); + +HRESULT PostWpdEvent( + _In_ IPortableDeviceValues* pCommandParams, + _In_ IPortableDeviceValues* pEventParams); + +HRESULT GetClientContextMap( + _In_ IPortableDeviceValues* pParams, + _Outptr_ ContextMap** ppContextMap); + +HRESULT GetClientContext( + _In_ IPortableDeviceValues* pParams, + _In_ LPCWSTR pszContextKey, + _COM_Outptr_ IUnknown** ppContext); + +HRESULT GetClientEventCookie( + _In_ IPortableDeviceValues* pParams, + _Outptr_result_maybenull_ LPWSTR* ppszEventCookie); + +HRESULT AddPropertyAttributesByType( + const FakeDevicePropertyAttributesType type, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT SetPropertyAttributes( + _In_ REFPROPERTYKEY Key, + _In_reads_(cAttributeInfo) const PropertyAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT SetMethodParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_reads_(cAttributeInfo) const MethodParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT SetEventParameterAttributes( + _In_ REFPROPERTYKEY Parameter, + _In_reads_(cAttributeInfo) const EventParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT SetEventParameters( + _In_ REFGUID Event, + _In_reads_(cAttributeInfo) const EventParameterAttributeInfo* AttributeInfo, + _In_ DWORD cAttributeInfo, + _In_ IPortableDeviceKeyCollection* pParameters); diff --git a/wpd/WpdServiceSampleDriver/resource.h b/wpd/WpdServiceSampleDriver/resource.h new file mode 100644 index 00000000..00789508 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/resource.h @@ -0,0 +1,5 @@ +#pragma once +#define IDR_WpdServiceSampleDriver 101 + +#define IDR_WPD_SAMPLEDRIVER_SERVICE_ICON 3000 +#define IDR_WPD_SAMPLEDRIVER_DEVICE_ICON 3001 diff --git a/wpd/WpdServiceSampleDriver/stdafx.h b/wpd/WpdServiceSampleDriver/stdafx.h new file mode 100644 index 00000000..359c8990 --- /dev/null +++ b/wpd/WpdServiceSampleDriver/stdafx.h @@ -0,0 +1,123 @@ +#pragma once + +#include "resource.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#define STRSAFE_NO_DEPRECATE + +#include <stdio.h> +#include <tchar.h> + +#include <atlbase.h> +#include <atlcom.h> +#include <atlcoll.h> +#include <atlstr.h> + +// This driver is entirely user-mode +_Analysis_mode_(_Analysis_code_type_user_code_); + +// Driver specific tracing #defines +// +// TODO: Change these values to be appropriate for your driver. +// +#define MYDRIVER_TRACING_ID L"Microsoft\\WPD\\ServiceSampleDriver" + +// +// TODO: Choose a different trace control GUID +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(ServiceSampleDriverCtlGuid,(f0cc34b3,a482,4dc0,b978,b5cf42aec4fd), \ + WPP_DEFINE_BIT(TRACE_FLAG_ALL) \ + WPP_DEFINE_BIT(TRACE_FLAG_DEVICE) \ + WPP_DEFINE_BIT(TRACE_FLAG_DRIVER) \ + WPP_DEFINE_BIT(TRACE_FLAG_QUEUE) \ + ) + +#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ + WPP_LEVEL_LOGGER(flags) + +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ + (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) + +// +// This comment block is scanned by the trace preprocessor to define our +// TraceEvents function. +// +// begin_wpp config +// FUNC Trace{FLAG=TRACE_FLAG_ALL}(LEVEL, MSG, ...); +// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); +// end_wpp + +// +// This comment block is scanned by the trace preprocessor to define our +// CHECK_HR function. +// +// +// begin_wpp config +// USEPREFIX (CHECK_HR,"%!STDPREFIX!"); +// FUNC CHECK_HR{FLAG=TRACE_FLAG_ALL}(hrCheck, MSG, ...); +// USESUFFIX (CHECK_HR, " hr= %!HRESULT!", hrCheck); +// end_wpp + +// +// PRE macro: The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +// +#define WPP_FLAG_hrCheck_PRE(FLAGS, hrCheck) {if(hrCheck != S_OK) { + +// +// POST macro +// The name of the macro includes the condition arguments FLAGS and EXP +// define in FUNC above +#define WPP_FLAG_hrCheck_POST(FLAGS, hrCheck) ; } } + +// +// The two macros below are for checking if the event should be logged and for +// choosing the logger handle to use when calling the ETW trace API +// +#define WPP_FLAG_hrCheck_ENABLED(FLAGS, hrCheck) WPP_FLAG_ENABLED(FLAGS) +#define WPP_FLAG_hrCheck_LOGGER(FLAGS, hrCheck) WPP_FLAG_LOGGER(FLAGS) + +#include <PortableDeviceTypes.h> +#include <PortableDeviceClassExtension.h> +#include <PortableDevice.h> + +// Service GUID definitions +#include <initguid.h> +#include <propkeydef.h> +#define DEFINE_DEVSVCGUID DEFINE_GUID +#define DEFINE_DEVSVCPROPKEY DEFINE_PROPERTYKEY +#include <DeviceServices.h> +#include <FullEnumSyncDeviceService.h> +#include <ContactDeviceService.h> + +// Forward class declarations +class WpdObjectResourceContext; +class WpdObjectEnumeratorContext; +class WpdServiceMethods; + +#include "helpers.h" +#include "FakeContent.h" +#include "FakeContactContent.h" +#include "FakeContactsServiceContent.h" +#include "FakeContactsService.h" +#include "FakeStorage.h" +#include "FakeDeviceContent.h" +#include "FakeDevice.h" + +#include "WpdServiceSampleDriver.h" +#include "WpdObjectEnum.h" +#include "WpdObjectManagement.h" +#include "WpdObjectProperties.h" +#include "WpdObjectPropertiesBulk.h" +#include "WpdObjectResources.h" +#include "WpdCapabilities.h" +#include "WpdServiceCapabilities.h" +#include "WpdServiceMethods.h" +#include "WpdService.h" +#include "WpdBaseDriver.h" + +extern HINSTANCE g_hInstance; + |
