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/WpdWudfSampleDriver | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'wpd/WpdWudfSampleDriver')
64 files changed, 16689 insertions, 0 deletions
diff --git a/wpd/WpdWudfSampleDriver/ContextMap.h b/wpd/WpdWudfSampleDriver/ContextMap.h new file mode 100644 index 00000000..63127d4d --- /dev/null +++ b/wpd/WpdWudfSampleDriver/ContextMap.h @@ -0,0 +1,135 @@ +#pragma once + +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 successfull, this method AddRef's the context + HRESULT Add( + _In_ const CAtlStringW& key, + _In_ IUnknown* pContext) + { + CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection); + HRESULT 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( + _In_ 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( + _In_ 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; +}; diff --git a/wpd/WpdWudfSampleDriver/Device.cpp b/wpd/WpdWudfSampleDriver/Device.cpp new file mode 100644 index 00000000..5887e5ed --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Device.cpp @@ -0,0 +1,365 @@ +// Device.cpp : Implementation of CDevice + +#include "stdafx.h" +#include "Device.h" +#include "WpdWudfSampleDriver_i.c" + +#include "Device.tmh" + +// CDevice +STDMETHODIMP_(HRESULT) +CDevice::OnD0Entry(_In_ IWDFDevice* /*pDevice*/, + WDF_POWER_DEVICE_STATE /*previousState*/) +{ + HRESULT hr = S_OK; + return hr; +} + +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_pPortableDeviceClassExtension == NULL) + { + hr = CoCreateInstance(CLSID_PortableDeviceClassExtension, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceClassExtension, + (VOID**)&m_pPortableDeviceClassExtension); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceClassLibrary"); + + // 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 requsted support in their INF). + if (hr == S_OK) + { + CComPtr<IPortableDeviceValues> pOptions; + CComPtr<IPortableDevicePropVariantCollection> pContentTypes; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pOptions); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceClassLibrary"); + + 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"); + } + + if (hr == S_OK) + { + hr = m_pPortableDeviceClassExtension->Initialize(pDevice, pOptions); + CHECK_HR(hr, "Failed to Initialize portable device class extension object"); + } + } + } + + if (hr == S_OK) + { + DWORD dwLength = 0; + WCHAR* pszDeviceName = NULL; + hr = pDevice->RetrieveDeviceName(NULL, &dwLength); + if(dwLength > 0) + { + pszDeviceName = new WCHAR[dwLength + 1]; + if(pszDeviceName) + { + DWORD dwLengthTemp = dwLength; + hr = pDevice->RetrieveDeviceName(pszDeviceName, &dwLengthTemp); + CHECK_HR(hr, "Failed to get device name"); + if (hr == S_OK) + { + pszDeviceName[dwLength] = L'\0'; + if (m_pWpdBaseDriver != NULL) + { + hr = m_pWpdBaseDriver->Initialize(pszDeviceName, m_pPortableDeviceClassExtension); + CHECK_HR(hr, "Failed to initialize the fake device"); + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "NULL driver class used, Driver may not be initialized"); + } + } + delete[] pszDeviceName; + pszDeviceName = NULL; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate memory for device name"); + } + } + else + { + CHECK_HR(hr, "Failed to get device name length"); + } + } + + // Send the latest device friendly name to the Portable Device class extension library to process + if (hr == S_OK) + { + LPWSTR pwszDeviceFriendlyName = NULL; + + HRESULT hrTemp = GetDeviceFriendlyName(&pwszDeviceFriendlyName); + CHECK_HR(hrTemp, "Failed to get the device friendly name"); + + if (hrTemp == S_OK && pwszDeviceFriendlyName != NULL) + { + hrTemp = UpdateDeviceFriendlyName(m_pPortableDeviceClassExtension, pwszDeviceFriendlyName); + CHECK_HR(hrTemp, "Failed to update device friendly name information"); + } + + CoTaskMemFree(pwszDeviceFriendlyName); + pwszDeviceFriendlyName = NULL; + } + } + return hr; +} + +STDMETHODIMP_(HRESULT) +CDevice::OnReleaseHardware(_In_ IWDFDevice* pDevice) +{ + UNREFERENCED_PARAMETER(pDevice); + 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_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppContentTypes = NULL; + + // Prepare to make a call to query for the content types + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pParams); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if(SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues for results"); + } + + // 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")); + } + + 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_result_maybenull_ LPWSTR* pwszDeviceFriendlyName) +{ + HRESULT hr = S_OK; + + CComPtr<IPortableDeviceValues> pParams; + CComPtr<IPortableDeviceValues> pResults; + CComPtr<IPortableDeviceKeyCollection> pKeys; + CComPtr<IPortableDeviceValues> pValues; + + if (pwszDeviceFriendlyName == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pwszDeviceFriendlyName = NULL; + + // Prepare to make a call to query for the device friendly name + 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"); + } + + if (hr == S_OK) + { + 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 (hr == S_OK) + { + 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 (hr == S_OK) + { + 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 (hr == S_OK) + { + 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 (hr == S_OK) + { + hr = pKeys->Add(WPD_DEVICE_FRIENDLY_NAME); + CHECK_HR(hr, ("Failed to add WPD_DEVICE_FRIENDLY_NAME to key collection")); + } + + if (hr == S_OK) + { + 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 (hr == S_OK) + { + hr = m_pWpdBaseDriver->DispatchWpdMessage(pParams, pResults); + CHECK_HR(hr, ("Failed to dispatch message to get supported content types")); + } + + if (hr == S_OK) + { + hr = pResults->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); + CHECK_HR(hr, ("Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + } + + if (hr == S_OK) + { + hr = pValues->GetStringValue(WPD_DEVICE_FRIENDLY_NAME, pwszDeviceFriendlyName); + CHECK_HR(hr, ("Failed to get WPD_DEVICE_FRIENDLY_NAME")); + } + + return hr; +} + diff --git a/wpd/WpdWudfSampleDriver/Device.h b/wpd/WpdWudfSampleDriver/Device.h new file mode 100644 index 00000000..1d4ce01c --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Device.h @@ -0,0 +1,119 @@ +// Device.h : Declaration of the CDevice + +/*++ + +Copyright (c) 2004 Microsoft Corporation + +Module Name: + + Device.h + +Abstract: + + CWudf class declaration. This class provides the entry points for the + host process to call into. + +Author: + + Ray Patrick (raypat) + +Environment: + + User mode only + +Revision History: + + Created - 09/07/2004. + +--*/ + +#pragma once +#include "resource.h" +#include "WpdWudfSampleDriver.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_result_maybenull_ LPWSTR* pwszDeviceFriendlyName); + + WpdBaseDriver* m_pWpdBaseDriver; + CComPtr<IPortableDeviceClassExtension> m_pPortableDeviceClassExtension; +}; + diff --git a/wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h b/wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h new file mode 100644 index 00000000..884d078b --- /dev/null +++ b/wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h @@ -0,0 +1,410 @@ +#include "DeviceObjectFakeContent.h.tmh" + +#define DEVICE_PROTOCOL_VALUE L"WPD Sample Driver Protocol ver 1.00" +#define DEVICE_FIRMWARE_VERSION_VALUE L"1.0.0.0" +#define DEVICE_MODEL_VALUE L"Sample Device 1000" +#define DEVICE_MANUFACTURER_VALUE L"Windows Portable Devices Group" +#define DEVICE_SERIAL_NUMBER_VALUE L"12309342465230-12390123432111" +#define DEVICE_POWER_LEVEL_VALUE 100 +#define DEVICE_FRIENDLY_NAME L"My Sample Device 1000" + +__declspec(selectany) BYTE g_NetworkIdentifier[] = { 0x01, 0x02, 0x03, 0xff, 0xff, 0x04, 0x05, 0x06 }; + +class DeviceObjectFakeContent : public FakeContent +{ +public: + DeviceObjectFakeContent( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension) : FakeContent() + { + FriendlyName = DEVICE_FRIENDLY_NAME; + SyncPartner = L""; + m_pPortableDeviceClassExtension = pPortableDeviceClassExtension; + } + + DeviceObjectFakeContent(const FakeContent& src) + { + *this = src; + } + + virtual ~DeviceObjectFakeContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_FUNCTIONAL_CATEGORY_DEVICE, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for DeviceObjectFakeContent"); + } + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pValues) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + + if(pValues == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pValues); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_DEVICE_SUPPORTS_NON_CONSUMABLE + hrSetValue = pValues->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, TRUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_SUPPORTS_NON_CONSUMABLE"); + return hrSetValue; + } + + // Add WPD_OBJECT_CONTENT_TYPE + hrSetValue = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_OBJECT_CONTENT_TYPE"); + return hrSetValue; + } + + // Add WPD_FUNCTIONAL_OBJECT_CATEGORY + hrSetValue = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_DEVICE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + return hrSetValue; + } + + // Add WPD_DEVICE_FIRMWARE_VERSION + hrSetValue = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, DEVICE_FIRMWARE_VERSION_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_FIRMWARE_VERSION"); + return hrSetValue; + } + + // Add WPD_DEVICE_POWER_LEVEL + hrSetValue = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, DEVICE_POWER_LEVEL_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_POWER_LEVEL"); + return hrSetValue; + } + + // Add WPD_DEVICE_POWER_SOURCE + hrSetValue = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, WPD_POWER_SOURCE_EXTERNAL); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_POWER_SOURCE"); + return hrSetValue; + } + + // Add WPD_DEVICE_PROTOCOL + hrSetValue = pValues->SetStringValue(WPD_DEVICE_PROTOCOL, DEVICE_PROTOCOL_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_PROTOCOL"); + return hrSetValue; + } + + // Add WPD_DEVICE_MODEL + hrSetValue = pValues->SetStringValue(WPD_DEVICE_MODEL, DEVICE_MODEL_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_MODEL"); + return hrSetValue; + } + + // Add WPD_DEVICE_FRIENDLY_NAME + hrSetValue = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, FriendlyName); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_FRIENDLY_NAME"); + return hrSetValue; + } + + // Add WPD_DEVICE_SERIAL_NUMBER + hrSetValue = pValues->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_SERIAL_NUMBER"); + return hrSetValue; + } + + // Add WPD_DEVICE_MANUFACTURER + hrSetValue = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, DEVICE_MANUFACTURER_VALUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_MANUFACTURER"); + return hrSetValue; + } + + // Add WPD_DEVICE_TYPE + hrSetValue = pValues->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, WPD_DEVICE_TYPE_PHONE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_TYPE"); + return hrSetValue; + } + + // Add WPD_DEVICE_SYNC_PARTNER + hrSetValue = pValues->SetStringValue(WPD_DEVICE_SYNC_PARTNER, SyncPartner); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_SYNC_PARTNER"); + return hrSetValue; + } + + // Add WPD_DEVICE_NETWORK_IDENTIFIER + if (sizeof(ULONGLONG) == sizeof(g_NetworkIdentifier)) + { + ULONGLONG ullTemp; + + CopyMemory(&ullTemp, g_NetworkIdentifier, sizeof(ULONGLONG)); + hrSetValue = pValues->SetUnsignedLargeIntegerValue(WPD_DEVICE_NETWORK_IDENTIFIER, ullTemp); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_DEVICE_NETWORK_IDENTIFIER"); + return hrSetValue; + } + } + else + { + hr = E_UNEXPECTED; // This is a coding error + CHECK_HR(hr, "Failed to set WPD_DEVICE_NETWORK_IDENTIFIER"); + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_DEVICE_FRIENDLY_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + FriendlyName = Value.pwszVal; + // Let the Class Extension know about the change in Friendly Name so it can update the registry value of the same name + HRESULT hrTemp = UpdateDeviceFriendlyName(m_pPortableDeviceClassExtension, Value.pwszVal); + CHECK_HR(hrTemp, "Failed to update device friendly name"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_DEVICE_FRIENDLY_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_DEVICE_FRIENDLY_NAME because type was not VT_LPWSTR"); + } + } + else if(IsEqualPropertyKey(key, WPD_DEVICE_SYNC_PARTNER)) + { + if (Value.vt == VT_LPWSTR) + { + SyncPartner = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_DEVICE_SYNC_PARTNER because type was not VT_LPWSTR"); + } + } + else + { + // Let the base class take care of any other property writes + hr = FakeContent::WriteValue(key, Value); + // No need to log the error since the base class already does it. + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add the icon resource + hr = pKeys->Add(WPD_RESOURCE_ICON); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_ICON to supported resource list"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(Key, WPD_RESOURCE_ICON)) + { + // Override the size attribute for this resource. + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + + // Override the format attribute for this resource. + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded image file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_DEFAULT)) + { + return FakeContent::ReadData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToRead, pdwNumBytesRead); + } + + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_DEVICE_ICON); + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing the device icon data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded image file data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return FakeDeviceContent_Format; + } + +private: + CAtlStringW FriendlyName; + CAtlStringW SyncPartner; + CComPtr<IPortableDeviceClassExtension> m_pPortableDeviceClassExtension; +}; + diff --git a/wpd/WpdWudfSampleDriver/Driver.cpp b/wpd/WpdWudfSampleDriver/Driver.cpp new file mode 100644 index 00000000..49edc789 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Driver.cpp @@ -0,0 +1,199 @@ + +#include "stdafx.h" +#include "Driver.h" +#include "Device.h" +#include "Queue.h" + +CDriver::CDriver() +{ + + +} + +// +// The framework call this function when device is detected. This driver +// creates a device callback object and +// +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; + + 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/WpdWudfSampleDriver/Driver.h b/wpd/WpdWudfSampleDriver/Driver.h new file mode 100644 index 00000000..8b754ed7 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Driver.h @@ -0,0 +1,48 @@ + +#pragma once +#include "resource.h" +#include "WpdWudfSampleDriver.h" + + +class ATL_NO_VTABLE CDriver : + public CComObjectRootEx<CComMultiThreadModel>, + public CComCoClass<CDriver, &CLSID_WpdWudfSampleDriver>, + public IDriverEntry, + public IObjectCleanup +{ +public: + CDriver(); + + DECLARE_REGISTRY_RESOURCEID(IDR_WpdWudfSampleDriver) + + 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(WpdWudfSampleDriver), CDriver) + diff --git a/wpd/WpdWudfSampleDriver/FakeContactContent.h b/wpd/WpdWudfSampleDriver/FakeContactContent.h new file mode 100644 index 00000000..2f5ea76a --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeContactContent.h @@ -0,0 +1,442 @@ +#include "FakeContactContent.h.tmh" + +class FakeContactContent : public FakeContent +{ +public: + FakeContactContent() : + bHasContactPhoto(FALSE) + { + } + + FakeContactContent(BOOL bHasPhoto) + { + bHasContactPhoto = bHasPhoto; + } + + FakeContactContent(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeContactContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_OBJECT_FORMAT_VCARD2, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for FakeContactContent"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property values"); + return hr; + } + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME. We construct the original filename from the display name plus the ".vcf" extension, + // since this object's default resource is a VCARD. + CAtlStringW strFileName; + strFileName.Format(L"%ws.vcf", DisplayName.GetString()); + hrSetValue = pStore->SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, strFileName); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_CONTACT_DISPLAY_NAME")); + return hrSetValue; + } + + // Add WPD_CONTACT_DISPLAY_NAME + hrSetValue = pStore->SetStringValue(WPD_CONTACT_DISPLAY_NAME, DisplayName); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_CONTACT_DISPLAY_NAME")); + return hrSetValue; + } + + // Add WPD_CONTACT_PRIMARY_PHONE + hrSetValue = pStore->SetStringValue(WPD_CONTACT_PRIMARY_PHONE, PrimaryPhone); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_CONTACT_PRIMARY_PHONE")); + return hrSetValue; + } + + // Add WPD_CONTACT_BUSINESS_PHONE + hrSetValue = pStore->SetStringValue(WPD_CONTACT_BUSINESS_PHONE, WorkPhone); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_CONTACT_BUSINESS_PHONE")); + return hrSetValue; + } + + // Add WPD_CONTACT_MOBILE_PHONE + hrSetValue = pStore->SetStringValue(WPD_CONTACT_MOBILE_PHONE, CellPhone); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_CONTACT_MOBILE_PHONE")); + return hrSetValue; + } + + // Add WPD_OBJECT_SIZE. Object Size is defined to be the same size as the default resource. + // In this case the Contact Photo resource (if the object has one) is not counted in the object size. This is + // because the default resource for this object contains text-only VCARD2 data, and doesn't include the Contact Photo. + // When an application transfers the default resource for this object, it will only get the VCARD2 text data, and the + // Contact Photo resource has to be transferred separately. + // This is done mainly for illustrative purposes (and convenience in the sample). In most cases, it is better to + // embed the non-default resource data in the default resource when the file format allows it + // (e.g. a VCARD2 with an embedded contact photo rather than a separate one). It does involve more work + // (because the driver has to extract the embedded data) but is usually a better experience. + CAtlStringA strVCard; + DWORD dwSize = 0; + hr = CreateVCard(pStore, strVCard); + if (hr == S_OK) + { + dwSize = strVCard.GetLength() + 1; + // Add WPD_OBJECT_SIZE + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_OBJECT_SIZE, dwSize); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_SIZE")); + return hrSetValue; + } + } + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_CONTACT_DISPLAY_NAME) || + IsEqualPropertyKey(key, WPD_CONTACT_PERSONAL_PHONE) || + IsEqualPropertyKey(key, WPD_CONTACT_BUSINESS_PHONE) || + IsEqualPropertyKey(key, WPD_CONTACT_MOBILE_PHONE)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + // Basic validation passed, so save the value appropriately + if(IsEqualPropertyKey(key, WPD_CONTACT_DISPLAY_NAME)) + { + DisplayName = Value.pwszVal; + } + else if(IsEqualPropertyKey(key, WPD_CONTACT_PRIMARY_PHONE)) + { + PrimaryPhone = Value.pwszVal; + } + else if(IsEqualPropertyKey(key, WPD_CONTACT_BUSINESS_PHONE)) + { + WorkPhone = Value.pwszVal; + } + else if(IsEqualPropertyKey(key, WPD_CONTACT_MOBILE_PHONE)) + { + CellPhone = Value.pwszVal; + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set property because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set property because type was not VT_LPWSTR"); + } + } + else + { + // Call the base class to write the standard properties + hr = FakeContent::WriteValue(key, Value); + CHECK_HR(hr, "Failed to write basic value"); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr) && (bHasContactPhoto == TRUE)) + { + // Add the contact photo resource + hr = pKeys->Add(WPD_RESOURCE_CONTACT_PHOTO); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_CONTACT_PHOTO to supported resource list"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + DWORD dwSize = 0; + if (IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT)) + { + CAtlStringA strVCard; + hr = GetVCardString(strVCard); + if (hr == S_OK) + { + dwSize = strVCard.GetLength() + 1; + } + } + else if((IsEqualPropertyKey(Key, WPD_RESOURCE_CONTACT_PHOTO)) && (bHasContactPhoto == TRUE)) + { + dwSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_CONTACT_PHOTO); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Could not return resource attributes for unknown resource %ws.%d", (LPWSTR)CComBSTR(Key.fmtid), Key.pid); + } + + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, dwSize); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + } + + if (SUCCEEDED(hr) && (IsEqualPropertyKey(Key, WPD_RESOURCE_CONTACT_PHOTO))) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_MEDIA_WIDTH, 100); + CHECK_HR(hr, "Failed to set WPD_MEDIA_WIDTH"); + + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_MEDIA_HEIGHT, 134); + CHECK_HR(hr, "Failed to set WPD_MEDIA_HEIGHT"); + } + + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_IMAGE_BITDEPTH, 32); + CHECK_HR(hr, "Failed to set WPD_IMAGE_BITDEPTH"); + } + + if (SUCCEEDED(hr)) + { + // Override the format attribute for this resource. + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_PNG); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded image file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + CAtlStringA strVCard; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_DEFAULT)) + { + hr = GetVCardString(strVCard); + if (hr == S_OK) + { + pResource = (PBYTE) strVCard.GetString(); + dwObjectDataSize = strVCard.GetLength() + 1; + } + } + else if((IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_CONTACT_PHOTO)) && (bHasContactPhoto == TRUE)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_CONTACT_PHOTO); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_CONTACT_PHOTO); + } + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get DLL resource representing WPD resource data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + + // Copy the embedded image file data. + if (dwBytesToTransfer > 0) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + *pdwNumBytesRead = dwBytesToTransfer; + } + } + } + return hr; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_VCARD2; + } + + virtual HRESULT EnableResource( + _In_ REFPROPERTYKEY ResourceKey) + { + UNREFERENCED_PARAMETER(ResourceKey); + bHasContactPhoto = TRUE; + return S_OK; + } + + HRESULT GetVCardString( + _Out_ CAtlStringA& strVCard) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pContactProperties; + + strVCard = L""; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pContactProperties); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if (hr == S_OK) + { + hr = GetAllValues(pContactProperties); + CHECK_HR(hr, "Failed to get all values for contact object [%ws]", ObjectID); + } + + if (hr == S_OK) + { + hr = CreateVCard(pContactProperties, strVCard); + CHECK_HR(hr, "Failed to create VCard string"); + } + + return hr; + } + + CAtlStringW DisplayName; + CAtlStringW PrimaryPhone; + CAtlStringW WorkPhone; + CAtlStringW CellPhone; + BOOL bHasContactPhoto; +}; + diff --git a/wpd/WpdWudfSampleDriver/FakeContent.h b/wpd/WpdWudfSampleDriver/FakeContent.h new file mode 100644 index 00000000..6e427793 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeContent.h @@ -0,0 +1,576 @@ +#include "FakeContent.h.tmh" + +class FakeContent +{ +public: + FakeContent() : + MarkedForDeletion(FALSE), + CanDelete(TRUE), + IsHidden(FALSE), + IsSystem(FALSE), + NonConsumable(FALSE) + { + ContentType = WPD_CONTENT_TYPE_UNSPECIFIED; + } + + FakeContent(const FakeContent& src) : + MarkedForDeletion(FALSE), + CanDelete(TRUE), + IsHidden(FALSE), + IsSystem(FALSE), + NonConsumable(FALSE) + { + *this = src; + } + + virtual ~FakeContent() + { + } + + virtual FakeContent& operator= (const FakeContent& src) + { + ObjectID = src.ObjectID; + PersistentUniqueID = src.PersistentUniqueID; + ParentID = src.ParentID; + Name = src.Name; + FileName = src.FileName; + ContentType = src.ContentType; + MarkedForDeletion = src.MarkedForDeletion; + CanDelete = src.CanDelete; + IsHidden = src.IsHidden; + IsSystem = src.IsSystem; + NonConsumable = src.NonConsumable; + + return *this; + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + hr = AddSupportedProperties(GetObjectFormat(), ppKeys); + CHECK_HR(hr, ("Failed to add supported proeprties for FakeContent")); + return hr; + } + + virtual HRESULT GetAllValues(_In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add WPD_OBJECT_ID + pvValue = ObjectID; + hrSetValue = pStore->SetValue(WPD_OBJECT_ID, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ID")); + return hrSetValue; + } + + // Add WPD_OBJECT_PERSISTENT_UNIQUE_ID + pvValue = this->PersistentUniqueID; + hrSetValue = pStore->SetValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID")); + return hrSetValue; + } + + // Add WPD_OBJECT_PARENT_ID + pvValue = ParentID; + hrSetValue = pStore->SetValue(WPD_OBJECT_PARENT_ID, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_PARENT_ID")); + return hrSetValue; + } + + // Add WPD_OBJECT_NAME + pvValue = Name; + hrSetValue = pStore->SetValue(WPD_OBJECT_NAME, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_NAME")); + return hrSetValue; + } + + // Add WPD_OBJECT_CONTENT_TYPE + hrSetValue = pStore->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, ContentType); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_CONTENT_TYPE")); + return hrSetValue; + } + + // Add WPD_OBJECT_FORMAT + hrSetValue = pStore->SetGuidValue(WPD_OBJECT_FORMAT, GetObjectFormat()); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_FORMAT")); + return hrSetValue; + } + + // Add WPD_OBJECT_CAN_DELETE + hrSetValue = pStore->SetBoolValue(WPD_OBJECT_CAN_DELETE, CanDelete); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_CAN_DELETE")); + return hrSetValue; + } + + // Add WPD_OBJECT_ISHIDDEN + hrSetValue = pStore->SetBoolValue(WPD_OBJECT_ISHIDDEN, IsHidden); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ISHIDDEN")); + return hrSetValue; + } + + // Add WPD_OBJECT_ISSYSTEM + hrSetValue = pStore->SetBoolValue(WPD_OBJECT_ISSYSTEM, IsSystem); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ISSYSTEM")); + return hrSetValue; + } + + // Add WPD_OBJECT_NON_CONSUMABLE + hrSetValue = pStore->SetBoolValue(WPD_OBJECT_NON_CONSUMABLE, NonConsumable); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_NON_CONSUMABLE")); + return hrSetValue; + } + + // Add WPD_FOLDER_CONTENT_TYPES_ALLOWED if necessary + if(!RestrictToContentTypes.IsEmpty()) + { + CComPtr<IPortableDevicePropVariantCollection> pContentTypes; + hrSetValue = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pContentTypes); + CHECK_HR(hrSetValue, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + if (hrSetValue == S_OK) + { + PROPVARIANT pvContentType = {0}; + PropVariantInit(&pvContentType); + pvContentType.vt = VT_CLSID; // Don't PropVariantClear this since we won't allocate memory for it + + size_t numElems = RestrictToContentTypes.GetCount(); + for(size_t typeIndex = 0; hrSetValue == S_OK && typeIndex < numElems; typeIndex++) + { + pvContentType.puuid = &RestrictToContentTypes[typeIndex]; + hrSetValue = pContentTypes->Add(&pvContentType); + CHECK_HR(hrSetValue, "Failed to add content type"); + } + + if (hrSetValue == S_OK) + { + hrSetValue = pStore->SetIPortableDevicePropVariantCollectionValue(WPD_FOLDER_CONTENT_TYPES_ALLOWED, pContentTypes); + CHECK_HR(hrSetValue, "Failed to set WPD_FOLDER_CONTENT_TYPES_ALLOWED"); + } + + if (FAILED(hrSetValue)) + { + hr = hrSetValue; + } + } + } + + return hr; + } + + virtual HRESULT 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_OBJECT_NON_CONSUMABLE)) + { + if(Value.vt == VT_BOOL) + { + NonConsumable = Value.boolVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_NON_CONSUMABLE because type was not VT_BOOL"); + } + } + 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; + } + + virtual HRESULT GetAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = AddFixedPropertyAttributes(GetObjectFormat(), Key, pAttributes); + CHECK_HR(hr, "Failed to add fixed property attributes for %ws.%d on FakeContent", 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 the property attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // This object has no resources so return the empty collection + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + *ppAttributes = NULL; + + // This object has no resources therefore has no attributes to return. + + return E_NOTIMPL; + } + + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + UNREFERENCED_PARAMETER(ResourceKey); + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < FAKE_DATA_SIZE) + { + dwBytesToTransfer = (FAKE_DATA_SIZE - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // This sample driver does not have any real data content, + // so we generate fake data content. + // The content we generate is made up of the Name string repeated + // in the data buffer. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + DWORD dwOffset = 0; + DWORD dwSourceStringSizeInBytes = (Name.GetLength() * sizeof(WCHAR)) + sizeof(L'\0'); + for (DWORD counter = 0; counter < (dwBytesToTransfer / dwSourceStringSizeInBytes); counter++) + { + _Analysis_assume_((dwOffset + dwSourceStringSizeInBytes) <= dwNumBytesToRead); + memcpy(pBuffer + dwOffset, Name.GetString(), dwSourceStringSizeInBytes); + dwOffset += dwSourceStringSizeInBytes; + } + memset(pBuffer + dwOffset, 0, dwBytesToTransfer % dwSourceStringSizeInBytes); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual HRESULT WriteData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _In_reads_(dwNumBytesToWrite) BYTE* pBuffer, + DWORD dwNumBytesToWrite, + _Out_ DWORD* pdwNumBytesWritten) + { + HRESULT hr = S_OK; + + if((pBuffer == NULL) || + (pdwNumBytesWritten == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesWritten = 0; + UNREFERENCED_PARAMETER(ResourceKey); + UNREFERENCED_PARAMETER(dwStartByte); + // This fake driver does nothing with the data. The write method is simply + // a dummy one. + // Normally, a driver would copy the contents from the buffer to the device. + + if (hr == S_OK) + { + *pdwNumBytesWritten = dwNumBytesToWrite; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return FakeContent_Format; + } + + virtual HRESULT EnableResource( + _In_ REFPROPERTYKEY ResourceKey) + { + UNREFERENCED_PARAMETER(ResourceKey); + return E_NOTIMPL; + } + + CAtlStringW ObjectID; + CAtlStringW PersistentUniqueID; + CAtlStringW ParentID; + CAtlStringW Name; + CAtlStringW FileName; + GUID ContentType; + BOOL MarkedForDeletion; + BOOL CanDelete; + BOOL IsHidden; + BOOL IsSystem; + BOOL NonConsumable; + CAtlArray<GUID> RestrictToContentTypes; +}; + +class FakeGenericFileContent : public FakeContent +{ +public: + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hr = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hr != S_OK) + { + CHECK_HR(hr, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hr; + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + // Override the necessary attributes for this resource. + if ((hr == S_OK) && (IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT))) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_WRITE, TRUE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_WRITE"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } +}; diff --git a/wpd/WpdWudfSampleDriver/FakeDevice.h b/wpd/WpdWudfSampleDriver/FakeDevice.h new file mode 100644 index 00000000..56b9990a --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeDevice.h @@ -0,0 +1,2415 @@ +#include "FakeDevice.h.tmh" + +#define NUM_VERTICAL_OBJECTS 7 +#define NUM_IMAGE_OBJECTS 31 +#define NUM_MUSIC_OBJECTS 8 +#define NUM_VIDEO_OBJECTS 4 +#define NUM_MEMO_OBJECTS 2 +#define NUM_CONTACT_OBJECTS 5 +#define STORAGE1_OBJECT_ID L"Storage1" +#define STORAGE2_OBJECT_ID L"Storage2" +#define RENDERING_INFORMATION_OBJECT_ID L"RenderingInformation" +#define NETWORK_CONFIG_OBJECT_ID L"NetworkConfig" +#define UNTYPEDDATA_FOLDER_OBJECT_ID L"UntypedData" +#define CONTACT_FOLDER_OBJECT_ID L"Phonebook" +#define MEDIA_FOLDER_OBJECT_ID L"Media folder" +#define MEMO_FOLDER_OBJECT_ID L"Memo folder" +#define IMAGE_FOLDER_OBJECT_ID L"Picture folder" +#define MUSIC_FOLDER_OBJECT_ID L"Music folder" +#define VIDEO_FOLDER_OBJECT_ID L"Video folder" + +/** + * 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() + { + for(size_t index = 0; index < m_Content.GetCount(); index++) + { + delete m_Content[index]; + m_Content[index] = NULL; + } + } + + HRESULT InitializeContent( + _In_ IPortableDeviceClassExtension *pPortableDeviceClassExtension) + { + HRESULT hr = S_OK; + + if(pPortableDeviceClassExtension == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Add device object + CAutoPtr<DeviceObjectFakeContent> pDeviceObjectContent(new DeviceObjectFakeContent(pPortableDeviceClassExtension)); + if (pDeviceObjectContent) + { + pDeviceObjectContent->Name = L"Fake Device"; + pDeviceObjectContent->ParentID = L""; + pDeviceObjectContent->ObjectID = WPD_DEVICE_OBJECT_ID; + pDeviceObjectContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pDeviceObjectContent->ObjectID.GetString()); + pDeviceObjectContent->CanDelete = FALSE; + pDeviceObjectContent->IsHidden = TRUE; + pDeviceObjectContent->ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + + _ATLTRY + { + m_Content.Add(pDeviceObjectContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding DeviceObjectFakeContent"); + return hr; + } + pDeviceObjectContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: Storage1 + CAutoPtr<StorageObjectFakeContent> pStorageContent(new StorageObjectFakeContent()); + if (pStorageContent) + { + pStorageContent->Name = STORAGE1_OBJECT_ID; + pStorageContent->ParentID = WPD_DEVICE_OBJECT_ID; + pStorageContent->ObjectID = pStorageContent->Name; + pStorageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pStorageContent->ObjectID.GetString()); + pStorageContent->CanDelete = FALSE; + pStorageContent->ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + pStorageContent->IsExternalStorage = FALSE; + pStorageContent->Capacity = 120034890000; + pStorageContent->FreeSpaceInBytes = 60017445000; + + _ATLTRY + { + m_Content.Add(pStorageContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding StorageObjectFakeContent"); + return hr; + } + pStorageContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: Storage2 + pStorageContent.Attach(new StorageObjectFakeContent()); + if (pStorageContent) + { + pStorageContent->Name = STORAGE2_OBJECT_ID; + pStorageContent->ParentID = WPD_DEVICE_OBJECT_ID; + pStorageContent->ObjectID = pStorageContent->Name; + pStorageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pStorageContent->ObjectID.GetString()); + pStorageContent->CanDelete = FALSE; + pStorageContent->ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + pStorageContent->IsExternalStorage = TRUE; + pStorageContent->Capacity = 80000000000; + pStorageContent->FreeSpaceInBytes = 75000000000; + + _ATLTRY + { + m_Content.Add(pStorageContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding StorageObjectFakeContent"); + return hr; + } + pStorageContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: RenderingInformation + CAutoPtr<RenderingInformationFakeContent> pRenderingInformationContent(new RenderingInformationFakeContent()); + if (pRenderingInformationContent) + { + pRenderingInformationContent->Name = RENDERING_INFORMATION_OBJECT_ID; + pRenderingInformationContent->ParentID = WPD_DEVICE_OBJECT_ID; + pRenderingInformationContent->ObjectID = pRenderingInformationContent->Name; + pRenderingInformationContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pRenderingInformationContent->ObjectID.GetString()); + pRenderingInformationContent->CanDelete = FALSE; + pRenderingInformationContent->IsHidden = TRUE; + pRenderingInformationContent->ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + + _ATLTRY + { + m_Content.Add(pRenderingInformationContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding RenderingInformationFakeContent"); + return hr; + } + pRenderingInformationContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: NetworkConfig + CAutoPtr<NetworkConfigFakeContent> pNetworkConfigContent(new NetworkConfigFakeContent()); + if (pNetworkConfigContent) + { + pNetworkConfigContent->Name = NETWORK_CONFIG_OBJECT_ID; + pNetworkConfigContent->ParentID = WPD_DEVICE_OBJECT_ID; + pNetworkConfigContent->ObjectID = pNetworkConfigContent->Name; + pNetworkConfigContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pNetworkConfigContent->ObjectID.GetString()); + pNetworkConfigContent->CanDelete = FALSE; + pNetworkConfigContent->IsHidden = TRUE; + pNetworkConfigContent->ContentType = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT; + pNetworkConfigContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_NETWORK_ASSOCIATION); + pNetworkConfigContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_WIRELESS_PROFILE); + + _ATLTRY + { + m_Content.Add(pNetworkConfigContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding NetworkConfigFakeContent"); + return hr; + } + pNetworkConfigContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: Untyped data folder + CAutoPtr<FakeFolderContent> pFolderContent(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = UNTYPEDDATA_FOLDER_OBJECT_ID; + pFolderContent->ParentID = STORAGE2_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: Phonebook folder + pFolderContent.Attach(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = CONTACT_FOLDER_OBJECT_ID; + pFolderContent->ParentID = STORAGE1_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_CONTACT); + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel folder object with custom icon: Memos + CAutoPtr<FakeMemoFolderContent> pMemoFolderContent(new FakeMemoFolderContent()); + if (pMemoFolderContent) + { + pMemoFolderContent->Name = MEMO_FOLDER_OBJECT_ID; + pMemoFolderContent->ParentID = STORAGE1_OBJECT_ID; + pMemoFolderContent->ObjectID = pMemoFolderContent->Name; + pMemoFolderContent->FileName = pMemoFolderContent->Name; + pMemoFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMemoFolderContent->ObjectID.GetString()); + pMemoFolderContent->CanDelete = FALSE; + pMemoFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pMemoFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_MEMO); + + _ATLTRY + { + m_Content.Add(pMemoFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeMemoFolderContent"); + return hr; + } + pMemoFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Toplevel object: Media folder + pFolderContent.Attach(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = MEDIA_FOLDER_OBJECT_ID; + pFolderContent->ParentID = STORAGE1_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_FOLDER); + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Media folder: Picture folder + pFolderContent.Attach(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = IMAGE_FOLDER_OBJECT_ID; + pFolderContent->ParentID = MEDIA_FOLDER_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_IMAGE); + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Media folder: Music folder + pFolderContent.Attach(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = MUSIC_FOLDER_OBJECT_ID; + pFolderContent->ParentID = MEDIA_FOLDER_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_AUDIO); + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add Media folder: Video folder + pFolderContent.Attach(new FakeFolderContent()); + if (pFolderContent) + { + pFolderContent->Name = VIDEO_FOLDER_OBJECT_ID; + pFolderContent->ParentID = MEDIA_FOLDER_OBJECT_ID; + pFolderContent->ObjectID = pFolderContent->Name; + pFolderContent->FileName = pFolderContent->Name; + pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString()); + pFolderContent->CanDelete = FALSE; + pFolderContent->ContentType = WPD_CONTENT_TYPE_FOLDER; + pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_VIDEO); + + _ATLTRY + { + m_Content.Add(pFolderContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeFolderContent"); + return hr; + } + pFolderContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + + // Add generic file content objects to storage 1 + for(DWORD dwIndex = 0; dwIndex < NUM_VERTICAL_OBJECTS; dwIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeGenericFileContent> pGenericFileContent(new FakeGenericFileContent()); + if (pGenericFileContent) + { + pGenericFileContent->Name.Format(L"Name%d", m_dwLastObjectID); + pGenericFileContent->ParentID = STORAGE1_OBJECT_ID; + pGenericFileContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pGenericFileContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pGenericFileContent->ObjectID.GetString()); + pGenericFileContent->ContentType = WPD_CONTENT_TYPE_UNSPECIFIED; + pGenericFileContent->CanDelete = FALSE; + pGenericFileContent->IsHidden = TRUE; + pGenericFileContent->IsSystem = TRUE; + pGenericFileContent->FileName = pGenericFileContent->Name; + + _ATLTRY + { + m_Content.Add(pGenericFileContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeGenericFileContent"); + return hr; + } + pGenericFileContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + // Add image objects to the image folder + for(DWORD dwImageIndex = 0; dwImageIndex < NUM_IMAGE_OBJECTS; dwImageIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeImageContent> pImageContent(new FakeImageContent()); + if (pImageContent) + { + pImageContent->Name.Format(L"Image%d", m_dwLastObjectID); + pImageContent->ParentID = IMAGE_FOLDER_OBJECT_ID; + pImageContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pImageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pImageContent->ObjectID.GetString()); + pImageContent->ContentType = WPD_CONTENT_TYPE_IMAGE; + pImageContent->FileName.Format(L"ImageFile_%d.jpg", m_dwLastObjectID); + + _ATLTRY + { + m_Content.Add(pImageContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeImageContent"); + return hr; + } + pImageContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + // Add music objects to the music folder + for(DWORD dwMusicIndex = 0; dwMusicIndex < NUM_MUSIC_OBJECTS; dwMusicIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeMusicContent> pMusicContent(new FakeMusicContent()); + if (pMusicContent) + { + pMusicContent->Name.Format(L"Music%d", m_dwLastObjectID); + pMusicContent->ParentID = MUSIC_FOLDER_OBJECT_ID; + pMusicContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pMusicContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMusicContent->ObjectID.GetString()); + pMusicContent->ContentType = WPD_CONTENT_TYPE_AUDIO; + pMusicContent->FileName.Format(L"MusicFile_%d.wma", m_dwLastObjectID); + + _ATLTRY + { + m_Content.Add(pMusicContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeMusicContent"); + return hr; + } + pMusicContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + // Add video objects to the video folder + for(DWORD dwVideoIndex = 0; dwVideoIndex < NUM_VIDEO_OBJECTS; dwVideoIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeVideoContent> pVideoContent(new FakeVideoContent()); + if (pVideoContent) + { + pVideoContent->Name.Format(L"Video%d", m_dwLastObjectID); + pVideoContent->ParentID = VIDEO_FOLDER_OBJECT_ID; + pVideoContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pVideoContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pVideoContent->ObjectID.GetString()); + pVideoContent->ContentType = WPD_CONTENT_TYPE_VIDEO; + pVideoContent->FileName.Format(L"VideoFile_%d.wmv", m_dwLastObjectID); + + _ATLTRY + { + m_Content.Add(pVideoContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeVideoContent"); + return hr; + } + pVideoContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + // Add contact objects to the contact folder + for(DWORD dwContactIndex = 0; dwContactIndex < NUM_CONTACT_OBJECTS; dwContactIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeContactContent> pContactContent(new FakeContactContent()); + if (pContactContent) + { + if ((m_dwLastObjectID % 2) == 0) // Mark every second contact as having a photo + { + pContactContent->bHasContactPhoto = TRUE; + } + pContactContent->Name.Format(L"Contact%d", m_dwLastObjectID); + pContactContent->ParentID = CONTACT_FOLDER_OBJECT_ID; + pContactContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContactContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContactContent->ObjectID.GetString()); + pContactContent->ContentType = WPD_CONTENT_TYPE_CONTACT; + pContactContent->DisplayName.Format(L"Surname%d, FirstName%d", dwContactIndex, dwContactIndex); + pContactContent->PrimaryPhone = L"(425) 555 0821"; + pContactContent->WorkPhone = L"(425) 556 6010"; + pContactContent->CellPhone = L"(206) 557 1441"; + + _ATLTRY + { + m_Content.Add(pContactContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeContactContent"); + return hr; + } + pContactContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + // Add memo objects to the memo folder + for(DWORD dwMemoIndex = 0; dwMemoIndex < NUM_MEMO_OBJECTS; dwMemoIndex++) + { + m_dwLastObjectID++; + + CAutoPtr<FakeMemoContent> pMemoContent(new FakeMemoContent()); + if (pMemoContent) + { + pMemoContent->Name.Format(L"Memo%d", m_dwLastObjectID); + pMemoContent->ParentID = MEMO_FOLDER_OBJECT_ID; + pMemoContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pMemoContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMemoContent->ObjectID.GetString()); + pMemoContent->ContentType = WPD_CONTENT_TYPE_MEMO; + pMemoContent->FileName.Format(L"MemoFile_%d.mem", m_dwLastObjectID); + + _ATLTRY + { + m_Content.Add(pMemoContent); + } + _ATLCATCH(e) + { + hr = e; + CHECK_HR(hr, "ATL Exception when adding FakeMemoContent"); + return hr; + } + pMemoContent.Detach(); + } + else + { + hr = E_OUTOFMEMORY; + return hr; + } + } + + return hr; + } + + bool FindNext( const DWORD dwStartIndex, + _In_ const CAtlStringW& strParentID, + _Out_ CAtlStringW& strObjectID, + _Out_ DWORD* pdwNextStartIndex) + { + DWORD curIndex = 0; + bool bFound = false; + strObjectID = L""; + *pdwNextStartIndex = 0; + + if(dwStartIndex >= m_Content.GetCount()) + { + return false; + } + + for(curIndex = dwStartIndex; curIndex < m_Content.GetCount(); curIndex++) + { + if(m_Content[curIndex]->ParentID == strParentID) + { + strObjectID = m_Content[curIndex]->ObjectID; + bFound = true; + break; + } + } + + if(bFound) + { + *pdwNextStartIndex = curIndex + 1; + } + else + { + *pdwNextStartIndex = (DWORD) m_Content.GetCount(); + } + + return bFound; + } + + /** + * Returns a pointer to the fake content in pElement corresponding to + * the specified ObjectID. + * Return value is true if found, otherwise false. + */ + _Success_(return) + bool GetContent(_In_ LPCWSTR pszObjectID, _Outptr_result_nullonfailure_ FakeContent** ppElement) + { + bool bFound = false; + + if((pszObjectID == NULL) || + (ppElement == NULL)) + { + return false; + } + + *ppElement = NULL; + + for(DWORD Index = 0; Index < m_Content.GetCount(); Index++) + { + if(m_Content[Index]->ObjectID == pszObjectID) + { + *ppElement = m_Content[Index]; + bFound = true; + break; + } + } + + return bFound; + } + + /** + * Returns the index of the fake content corresponding to the + * specified ObjectID. + * Return value is true if found, otherwise false. + */ + bool GetContentIndex(_In_ LPCWSTR pszObjectID, _Out_ DWORD* pIndex) + { + bool bFound = false; + + if((pszObjectID == NULL) || + (pIndex == NULL)) + { + return false; + } + + *pIndex = 0; + + for(DWORD Index = 0; Index < m_Content.GetCount(); Index++) + { + if(m_Content[Index]->ObjectID == pszObjectID) + { + *pIndex = Index; + bFound = true; + break; + } + } + + return bFound; + } + + HRESULT GetSupportedProperties( + _In_ LPCWSTR pszObjectID, + _COM_Outptr_ IPortableDeviceKeyCollection** ppCollection) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + *ppCollection = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->GetSupportedProperties(ppCollection); + CHECK_HR(hr, "Failed to get supported properties on element [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + return hr; + } + + HRESULT GetAllValues( + _In_ LPCWSTR pszObjectID, + _COM_Outptr_ IPortableDeviceValues** ppValues) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if(ppValues == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppValues = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + CComPtr<IPortableDeviceValues> pValues; + + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + + if (SUCCEEDED(hr)) + { + hr = pElement->GetAllValues(pValues); + CHECK_HR(hr, "Failed to fill property values"); + + if (SUCCEEDED(hr)) + { + // Keep hr intact in case it is S_FALSE + HRESULT hrTemp = pValues->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppValues); + + if (FAILED(hrTemp)) + { + hr = hrTemp; + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + } + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + return hr; + } + + /** + * This fake device is an unrealistic example from a performance standpoint, since + * requesting some values is more costly than requesting all values. + * The reason for this is that is is easier for the underlying objects to simply return all the + * values, and for us to filter out only the ones we want here. + * It is not expected that real devices function this way. + */ + HRESULT GetValues( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys, + _COM_Outptr_ IPortableDeviceValues** ppValues) + { + HRESULT hr = S_OK; + BOOL bError = FALSE; + CComPtr<IPortableDeviceValues> pAllValues; + CComPtr<IPortableDeviceValues> pValues; + + if((pszObjectID == NULL) || + (pKeys == NULL) || + (ppValues == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppValues = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pValues); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + // Get ALL values (NOTE: This is inefficient) + if (SUCCEEDED(hr)) + { + hr = GetAllValues(pszObjectID, &pAllValues); + CHECK_HR(hr, "Failed to get property values [%ws]", pszObjectID); + } + + // Return only the requested values + if (SUCCEEDED(hr)) + { + PROPVARIANT pv = {0}; + DWORD dwIndex = 0; + PROPERTYKEY Key = WPD_PROPERTY_NULL; + while(SUCCEEDED(hr) && pKeys->GetAt(dwIndex, &Key) == S_OK) + { + hr = pAllValues->GetValue(Key, &pv); + if(FAILED(hr)) + { + bError = TRUE; + hr = pValues->SetErrorValue(Key, hr); + CHECK_HR(hr, "Failed to set error value for %ws.%d", (LPWSTR)CComBSTR(Key.fmtid),Key.pid); + } + else + { + hr = pValues->SetValue(Key, &pv); + CHECK_HR(hr, "Failed to set property value %ws.%d", (LPWSTR)CComBSTR(Key.fmtid),Key.pid); + } + + PropVariantClear(&pv); + dwIndex++; + } + } + + if (SUCCEEDED(hr)) + { + hr = pValues->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppValues); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + + if (SUCCEEDED(hr) && + (bError == TRUE)) + { + hr = S_FALSE; + } + + return hr; + } + + HRESULT WriteProperty( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->WriteValue(key, Value); + CHECK_HR(hr, "Failed to write property value"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + return hr; + } + + HRESULT WritePropertiesOnObject( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceValues* pValues, + _COM_Outptr_ IPortableDeviceValues** ppWriteResults) + { + HRESULT hr = S_OK; + bool bErrorOnWrite = false; + DWORD cValues = 0; + CComPtr<IPortableDeviceValues> pWriteResults; + + *ppWriteResults = NULL; + + // Create property store for values + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**)&pWriteResults); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = pValues->GetCount(&cValues); + CHECK_HR(hr, "Failed to get property count"); + } + + if (SUCCEEDED(hr)) + { + for(DWORD dwIndex = 0; dwIndex < cValues; dwIndex++) + { + HRESULT hrWrite = S_OK; + PROPERTYKEY Key = {0}; + PropVariantWrapper pvValue; + PropVariantWrapper pvWriteResult; + + hrWrite = pValues->GetAt(dwIndex, &Key, &pvValue); + CHECK_HR(hrWrite, "Failed to get property key/value at index %d", dwIndex); + + if (SUCCEEDED(hrWrite)) + { + FakeContent* pElement = NULL; + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->WriteValue(Key, pvValue); + CHECK_HR(hr, "Failed to write property value"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + } + + // Always set the write result for this property + pvWriteResult.SetErrorValue(hrWrite); + if(FAILED(hrWrite)) + { + bErrorOnWrite = true; + } + + hrWrite = pWriteResults->SetValue(Key, &pvWriteResult); + CHECK_HR(hrWrite, "Failed to set write result for property %ws.%d", CComBSTR(Key.fmtid), Key.pid); + } + } + + if (SUCCEEDED(hr)) + { + hr = pWriteResults->QueryInterface(IID_IPortableDeviceValues, (VOID**)ppWriteResults); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on IPortableDeviceValues"); + } + + // Remember to set the hr to S_FALSE if there were any failures writing a property. + if((hr == S_OK) && + (bErrorOnWrite == true)) + { + hr = S_FALSE; + } + + return hr; + } + + HRESULT GetAttributes( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->GetAttributes(key, ppAttributes); + CHECK_HR(hr, "Failed to get attributes for %ws.%d", CComBSTR(key.fmtid), key.pid); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + return hr; + } + + HRESULT GetSupportedResources( + _In_ LPCWSTR pszObjectID, + _COM_Outptr_ IPortableDeviceKeyCollection** ppCollection) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + *ppCollection = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->GetSupportedResources(ppCollection); + CHECK_HR(hr, "Failed to get supported resources on element [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + return hr; + } + + HRESULT GetResourceAttributes( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->GetResourceAttributes(key, ppAttributes); + CHECK_HR(hr, "Failed to get resource attributes for %ws.%d", CComBSTR(key.fmtid), key.pid); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + return hr; + } + + HRESULT ReadData( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->ReadData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToRead, pdwNumBytesRead); + CHECK_HR(hr, "Failed to read resource data for %ws.%d", CComBSTR(ResourceKey.fmtid), ResourceKey.pid); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + return hr; + } + + HRESULT WriteData( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _In_reads_(dwNumBytesToWrite) BYTE* pBuffer, + DWORD dwNumBytesToWrite, + _Out_ DWORD* pdwNumBytesWritten) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesWritten == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesWritten = 0; + + if(GetContent(pszObjectID, &pElement)) + { + hr = pElement->WriteData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToWrite, pdwNumBytesWritten); + CHECK_HR(hr, "Failed to write resource data for %ws.%d", CComBSTR(ResourceKey.fmtid), ResourceKey.pid); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + return hr; + } + + HRESULT MarkChildrenForDeletion( + _In_ LPCWSTR pszParentObjectID) + { + HRESULT hr = S_OK; + + CAtlStringW strParentObjectID = pszParentObjectID; + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if (m_Content[Index]->ParentID == strParentObjectID) + { + // Mark this object + m_Content[Index]->MarkedForDeletion = TRUE; + hr = MarkChildrenForDeletion(m_Content[Index]->ObjectID); + if (FAILED(hr)) + { + break; + } + } + } + return hr; + } + + HRESULT RemoveObjectsMarkedForDeletion() + { + HRESULT hr = S_OK; + + for (size_t Index = m_Content.GetCount(); Index > 0; Index--) + { + if (m_Content[Index - 1]->MarkedForDeletion == TRUE) + { + // Delete this object + FakeContent* pContent = m_Content[Index - 1]; + m_Content.RemoveAt(Index - 1); + delete pContent; + } + } + + // Removes empty elements + m_Content.FreeExtra(); + + return hr; + } + + BOOL CanDeleteObject( + _In_ LPCWSTR pszObjectID) + { + BOOL bCanDelete = FALSE; + + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if (m_Content[Index]->ObjectID == pszObjectID) + { + bCanDelete = m_Content[Index]->CanDelete; + break; + } + } + return bCanDelete; + } + + HRESULT DeleteObject( + DWORD dwOptions, + _In_ LPCWSTR pszObjectID) + { + HRESULT hr = S_OK; + DWORD dwIndex = 0; + + if(pszObjectID == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Do an access check to verify whether the object may be deleted + if(CanDeleteObject(pszObjectID) == FALSE) + { + hr = E_ACCESSDENIED; + CHECK_HR(hr, "Failed to delete read-only object %ws", pszObjectID); + } + + // Objects can only be deleted with the no recursion flag + // if they have no children. + if ((dwOptions == PORTABLE_DEVICE_DELETE_NO_RECURSION) && + (HasChildren(pszObjectID) == TRUE)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION); + return hr; + } + + // Loop through the objects and delete the children only if + // the recursive option is specified. + if (dwOptions == PORTABLE_DEVICE_DELETE_WITH_RECURSION) + { + hr = MarkChildrenForDeletion(pszObjectID); + CHECK_HR(hr, "Error attempting to mark objects for (recursive) deletion"); + } + + // Mark this object for deletion + if (hr == S_OK) + { + if(GetContentIndex(pszObjectID, &dwIndex)) + { + m_Content[dwIndex]->MarkedForDeletion = TRUE; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + } + + // Delete the objects + if (hr == S_OK) + { + hr = RemoveObjectsMarkedForDeletion(); + CHECK_HR(hr, "Failed to remove objects marked for deletion"); + } + + return hr; + } + + BOOL HasChildren( + _In_ LPCWSTR pszObjectID) + { + BOOL bHasChild = FALSE; + + if (pszObjectID == NULL) + { + bHasChild = FALSE; + return bHasChild; + } + + CAtlStringW strObjectID = pszObjectID; + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if (m_Content[Index]->ParentID == strObjectID) + { + bHasChild = TRUE; + break; + } + } + + return bHasChild; + } + + HRESULT SaveNewObject( + _In_ IPortableDeviceValues* pObjectProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszObjectID) + { + HRESULT hr = S_OK; + DWORD dwParentIndex = 0; + LPWSTR pszObjectName = NULL; + LPWSTR pszParentID = NULL; + GUID guidContentType = WPD_CONTENT_TYPE_UNSPECIFIED; + + if(pObjectProperties == NULL || ppszObjectID == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppszObjectID = NULL; + + // Get the content type + hr = pObjectProperties->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, &guidContentType); + CHECK_HR(hr, "Failed to get WPD_OBJECT_CONTENT_TYPE"); + + // Get ParentID from pValues + if (SUCCEEDED(hr)) + { + hr = pObjectProperties->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID); + CHECK_HR(hr, "Failed to get WPD_OBJECT_PARENT_ID"); + } + + // Get Object Name from pValues + if (SUCCEEDED(hr)) + { + hr = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &pszObjectName); + CHECK_HR(hr, "Failed to get WPD_OBJECT_NAME"); + } + + if (SUCCEEDED(hr) && !GetContentIndex(pszParentID, &dwParentIndex)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid Parent ObjectID [%ws]", pszParentID); + } + + // Check whether the parent can hold objects of this content type + if (SUCCEEDED(hr)) + { + hr = IsValidContentType(guidContentType, m_Content[dwParentIndex]->RestrictToContentTypes); + CHECK_HR(hr, "Object named [%ws] could not be created, because parent [%ws] does not support this content type", pszObjectName, pszParentID); + } + + if (SUCCEEDED(hr)) + { + FakeContent* pContent; + + // Create the object + hr = CreateContentObject(pszObjectName, pszParentID, guidContentType, pObjectProperties, &pContent); + CHECK_HR(hr, "Failed to create object named %ws", pszObjectName); + + // Add it to the sample driver's internal list of content objects + if (SUCCEEDED(hr)) + { + m_Content.Add(pContent); + *ppszObjectID = AtlAllocTaskWideString(pContent->ObjectID); + if(*ppszObjectID == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Faield to allocate memory for newly created object's ID"); + } + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszParentID); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszObjectName); + + return hr; + } + + const CAtlStringW GetParentID( + _In_ LPCWSTR pszObjectID) + { + CAtlStringW strParent = L""; + + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if(m_Content[Index]->ObjectID.CompareNoCase(pszObjectID) == 0) + { + strParent = m_Content[Index]->ParentID; + break; + } + } + + return strParent; + } + + BOOL GetDepthFromParent( + _In_ LPCWSTR pszObjectID, + _In_ LPCWSTR pszParentObjectID, + _Out_ DWORD* pdwDepth) + { + HRESULT hr = S_OK; + BOOL bDescendant = FALSE; + + if((pszObjectID == NULL) || + (pszParentObjectID == NULL) || + (pdwDepth == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return bDescendant; + } + + *pdwDepth = 0; + + DWORD dwDepth = 0; + CAtlStringW strCurObject = pszObjectID; + + while((strCurObject.CompareNoCase(pszParentObjectID) != 0) && + (strCurObject.GetLength() > 0)) + { + strCurObject = GetParentID(strCurObject); + dwDepth++; + } + + if(strCurObject.CompareNoCase(pszParentObjectID) == 0) + { + bDescendant = TRUE; + *pdwDepth = dwDepth; + } + + return bDescendant; + } + +#pragma warning(suppress: 6388) // PREFast bug means here there is a false positive for the call to CComPtr<>::QueryInterface(). + HRESULT GetObjectIDsByFormat( + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR pszParentObjectID, + DWORD dwDepth, + _COM_Outptr_ IPortableDevicePropVariantCollection** ppObjectIDs) + { + HRESULT hr = S_OK; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + + if(ppObjectIDs == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppObjectIDs = NULL; + + // Create the ObjectID collection. Since this sample driver does not + // have any real hardware, it cannot take advantage of the devices + // 'bulk' operations, and so we must simulate it here. + // For simplicity, this sample driver simply walks the list of + // objects, finds out whether they are of the specified format and depth, and + // if the are then adds them to an object list. This is + // easy to do, but not the most efficient way to do it. + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pObjectIDs); + CHECK_HR(hr, "Failed to CoCreate CLSID_IPortableDevicePropVariantCollection"); + } + + if (SUCCEEDED(hr)) + { + // Walk the list of objects + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_LPWSTR; + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + // Check whether the object is of the correct format + if((m_Content[Index]->GetObjectFormat() == guidObjectFormat) || (guidObjectFormat == WPD_OBJECT_FORMAT_ALL)) + { + DWORD dwObjectDepth = 0; + // find out the depth of this object from the parent + if(GetDepthFromParent(m_Content[Index]->ObjectID, pszParentObjectID, &dwObjectDepth)) + { + // If it is within the appropriate depth, add it + if(dwObjectDepth <= dwDepth) + { + pv.pwszVal = (LPWSTR) m_Content[Index]->ObjectID.GetString(); + hr = pObjectIDs->Add(&pv); + CHECK_HR(hr, "Failed to add next ObjectID to collection"); + if(FAILED(hr)) + { + break; + } + } + } + } + } + } + + if (SUCCEEDED(hr)) + { + hr = pObjectIDs->QueryInterface(IID_PPV_ARGS(ppObjectIDs)); + CHECK_HR(hr, "Failed to QI IPortableDevicePropVariantCollection for IPortableDevicePropVariantCollection"); + } + + return hr; + } + + HRESULT GetObjectIDFromPersistentID( + _In_ LPCWSTR pszPersistentID, + _Outptr_result_nullonfailure_ LPWSTR* ppszObjectID) + { + HRESULT hr = S_OK; + + if((pszPersistentID == NULL) || + (ppszObjectID == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppszObjectID = NULL; + + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if(m_Content[Index]->PersistentUniqueID.CompareNoCase(pszPersistentID) == 0) + { + *ppszObjectID = AtlAllocTaskWideString(m_Content[Index]->ObjectID); + if(*ppszObjectID == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Could not allocate memory for ObjectID"); + } + break; + } + } + + if ((hr == S_OK) && (*ppszObjectID == NULL)) + { + // We reached the end of the content but did not find the element + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + return hr; + } + + /** + * Since this is a fake device with no hardware or real contents, we simulate + * a format by deleting the contents of a storage object. + **/ + HRESULT FormatStorage( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceValues* pCommandParams) + { + HRESULT hr = S_OK; + CAtlStringW strObjectID = pszObjectID; + + // Validate that the object specified is one of our storage objects + if((strObjectID.CompareNoCase(STORAGE1_OBJECT_ID) == 0) || + (strObjectID.CompareNoCase(STORAGE2_OBJECT_ID) == 0)) + { + CComPtr<IPortableDeviceValues> pEventParams; + + hr = GetObjectPropertiesForEvent(strObjectID.GetString(), &pEventParams); + CHECK_HR(hr, "Failed to get storage object properties for event"); + + // Indicate that format has started + hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_STARTED, 0); + CHECK_HR(hr, "Failed to send format event progress: WPD_OPERATION_STATE_STARTED"); + + if (hr == S_OK) + { + hr = MarkChildrenForDeletion(pszObjectID); + CHECK_HR(hr, "Failed to mark children of storage for deletion"); + + // Indicate that format is halfway there + hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_RUNNING, 50); + CHECK_HR(hr, "Failed to send format progress notification"); + + // Delete the objects + if (hr == S_OK) + { + hr = RemoveObjectsMarkedForDeletion(); + CHECK_HR(hr, "Failed to remove objects marked for deletion"); + } + + } + + // Indicate that format is done + hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_FINISHED, 100); + CHECK_HR(hr, "Failed to send format event progress: WPD_OPERATION_STATE_FINISHED"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Cannot format object [%ws] because it is not a storage", pszObjectID); + } + + return hr; + } + + HRESULT MoveObject( + _In_ LPCWSTR pszObjectID, + _In_ LPCWSTR pszDestinationID) + { + HRESULT hr = S_OK; + FakeContent* pSource = NULL; + FakeContent* pDestFolder = NULL; + + if(GetContent(pszObjectID, &pSource)) + { + // Verify that this is a content object. Functional objects cannot be moved. + if(pSource->ContentType != WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) + { + // Verify that the source is not the same as the destination + if(pSource->ObjectID.CompareNoCase(pszDestinationID) != 0) + { + // Verify that the destination is a folder + if(GetContent(pszDestinationID, &pDestFolder)) + { + if(pDestFolder->ContentType == WPD_CONTENT_TYPE_FOLDER) + { + pSource->ParentID = pszDestinationID; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Destination [%ws] for move object is not a folder", pszDestinationID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszDestinationID ? pszDestinationID : L"NULL"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object [%ws] cannot be moved to itself", pszObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Attempting to move functional object %ws", pszObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + return hr; + } + + HRESULT CopyObject( + _In_ LPCWSTR pszObjectID, + _In_ LPCWSTR pszDestinationID, + _Outptr_result_nullonfailure_ LPWSTR* ppNewObjectID) + { + HRESULT hr = S_OK; + FakeContent* pSource = NULL; + FakeContent* pDestFolder = NULL; + CComPtr<IPortableDeviceValues> pNewContentProperties; + LPWSTR pwszNewObjectID = NULL; + + if(ppNewObjectID == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppNewObjectID = NULL; + + if(GetContent(pszObjectID, &pSource)) + { + // Verify that this is a content object. Functional objects cannot be copied. + if(pSource->ContentType != WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) + { + // Verify that the source is not the same as the destination + if(pSource->ObjectID.CompareNoCase(pszDestinationID) != 0) + { + // Verify that the destination is a folder + if(GetContent(pszDestinationID, &pDestFolder)) + { + if(pDestFolder->ContentType == WPD_CONTENT_TYPE_FOLDER) + { + hr = GetAllValues(pSource->ObjectID, &pNewContentProperties); + CHECK_HR(hr, "Failed to get ALL values for [%ws]", pSource->ObjectID); + + if (SUCCEEDED(hr)) + { + hr = pNewContentProperties->SetStringValue(WPD_OBJECT_PARENT_ID, pDestFolder->ObjectID); + CHECK_HR(hr, "Failed to update WPD_OBJECT_PARENT_ID value to content object[%ws] on new copied content", pDestFolder->ObjectID); + } + + if (SUCCEEDED(hr)) + { + hr = SaveNewObject(pNewContentProperties, &pwszNewObjectID); + CHECK_HR(hr, "Failed Create new content from object[%ws]", pSource->ObjectID); + } + + // If the object we just copied was a folder and it contained child objects we need to copy the contents as well. + if (SUCCEEDED(hr) && (pSource->ContentType == WPD_CONTENT_TYPE_FOLDER) && (HasChildren(pSource->ObjectID) == TRUE)) + { + hr = CopyChildrenToNewParent(pSource->ObjectID, pwszNewObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Destination [%ws] for move object is not a folder", pszDestinationID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszDestinationID ? pszDestinationID : L"NULL"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object [%ws] cannot be copied to itself", pszObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Attempting to move functional object %ws", pszObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL"); + } + + // If we failed, free the allocated object ID. + if (FAILED(hr)) + { + if (pwszNewObjectID != NULL) + { + CoTaskMemFree(pwszNewObjectID); + pwszNewObjectID = NULL; + } + } + + if (pwszNewObjectID != NULL) + { + // The newly created object ID is given to the caller to manage and free + *ppNewObjectID = pwszNewObjectID; + } + + return hr; + } + + HRESULT GetObjectPropertiesForEvent( + _In_ LPCWSTR pszObjectID, + _COM_Outptr_ IPortableDeviceValues** ppValues) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + CComPtr<IPortableDeviceValues> pValues; + + *ppValues = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + // Add the keys we need in order to fill out event parameters + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_OBJECT_PERSISTENT_UNIQUE_ID); + CHECK_HR(hr, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID"); + + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_OBJECT_NAME); + CHECK_HR(hr, "Failed to add WPD_OBJECT_NAME"); + } + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_OBJECT_CONTENT_TYPE); + CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTENT_TYPE"); + } + + //These properties may or may not exist on the object + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_FUNCTIONAL_OBJECT_CATEGORY); + CHECK_HR(hr, "Failed to add WPD_FUNCTIONAL_OBJECT_CATEGORY"); + } + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_OBJECT_ORIGINAL_FILE_NAME); + CHECK_HR(hr, "Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME"); + } + if (SUCCEEDED(hr)) + { + hr = pKeys->Add(WPD_OBJECT_PARENT_ID); + CHECK_HR(hr, "Failed to add WPD_OBJECT_PARENT_ID"); + } + } + + // Get the values + if (SUCCEEDED(hr)) + { + hr = GetValues(pszObjectID, pKeys, &pValues); + if (hr == S_FALSE) + { + hr = S_OK; + } + + // Some objects supported by this driver don't support WPD_OBJECT_ORIGINAL_FILE_NAME + // (e.g. functional objects) so remove it if there was an error reading it. + if (SUCCEEDED(hr)) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + + hr = pValues->GetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pv); + CHECK_HR(hr, "Failed to get WPD_OBJECT_ORIGINAL_FILE_NAME"); + + if(SUCCEEDED(hr)) + { + if(pv.vt == VT_ERROR) + { + hr = pValues->RemoveValue(WPD_OBJECT_ORIGINAL_FILE_NAME); + CHECK_HR(hr, "Failed to remove WPD_OBJECT_ORIGINAL_FILE_NAME"); + } + } + + PropVariantClear(&pv); + + // None of the above is fatal + hr = S_OK; + } + + // Add the WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID + if (SUCCEEDED(hr)) + { + CAtlStringW strDevice = WPD_DEVICE_OBJECT_ID; + // If this is the device object, set the parent's persistent unique ID to to the empty string since it has no parent. + // Esle, get the persistent unique ID from the parent. + if(strDevice.CompareNoCase(pszObjectID) == 0) + { + pValues->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, L""); + } + else + { + CComPtr<IPortableDeviceValues> pParentValues; + HRESULT hrTemp = S_OK; + LPWSTR pszParentID = NULL; + LPWSTR pszParentUID = NULL; + + hrTemp = pValues->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID); + if (SUCCEEDED(hrTemp)) + { + hrTemp = GetValues(pszParentID, pKeys, &pParentValues); + if (SUCCEEDED(hrTemp)) + { + hrTemp = pParentValues->GetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pszParentUID); + if (SUCCEEDED(hrTemp)) + { + pValues->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pszParentUID); + CoTaskMemFree(pszParentUID); + } + CHECK_HR(hrTemp, "Failed to get parent unique id"); + } + CoTaskMemFree(pszParentID); + CHECK_HR(hrTemp, "Failed to get values for parent id"); + } + CHECK_HR(hrTemp, "Failed to get parent id"); + } + } + CHECK_HR(hr, "Failed to get properties to use in event information"); + } + + if (SUCCEEDED(hr)) + { + *ppValues = pValues.Detach(); + } + + return hr; + } + + + HRESULT CreateContentObject( + _In_ LPCWSTR pszObjectName, + _In_ LPCWSTR pszParentID, + _In_ REFGUID guidContentType, + _In_ IPortableDeviceValues* pObjectProperties, + _Outptr_result_nullonfailure_ FakeContent** ppContent) + { + HRESULT hr = S_OK; + HRESULT hrTemp = S_OK; + LPWSTR wszOriginalFileName = NULL; + + *ppContent = NULL; + + if(guidContentType == WPD_CONTENT_TYPE_FOLDER) + { + FakeContent* pContent = NULL; + + pContent = new FakeContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->FileName = pszObjectName; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + BOOL bTemp = FALSE; + // Get next optional property WPD_OBJECT_CAN_DELETE + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_CAN_DELETE, &bTemp); + if(hrTemp == S_OK) + { + pContent->CanDelete = bTemp; + } + // Get next optional property WPD_OBJECT_ISHIDDEN + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_ISHIDDEN, &bTemp); + if(hrTemp == S_OK) + { + pContent->IsHidden = bTemp; + } + // Get next optional property WPD_OBJECT_NON_CONSUMABLE + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_NON_CONSUMABLE, &bTemp); + if(hrTemp == S_OK) + { + pContent->NonConsumable = bTemp; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_CONTACT) + { + FakeContactContent* pContent = NULL; + + pContent = new FakeContactContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + // Get the other contact properties. + LPWSTR pszTempString = NULL; + + hr = pObjectProperties->GetStringValue(WPD_CONTACT_DISPLAY_NAME, &pszTempString); + CHECK_HR(hr, "Failed to get required contact property: WPD_CONTACT_DISPLAY_NAME"); + if (SUCCEEDED(hr)) + { + pContent->DisplayName = pszTempString; + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszTempString); + + // Get next optional property WPD_CONTACT_PRIMARY_PHONE + hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_PRIMARY_PHONE, &pszTempString); + if(hrTemp == S_OK) + { + pContent->PrimaryPhone = pszTempString; + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszTempString); + } + + // Get next optional property WPD_CONTACT_MOBILE_PHONE + hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_MOBILE_PHONE, &pszTempString); + if(hrTemp == S_OK) + { + pContent->CellPhone = pszTempString; + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszTempString); + } + + // Get next optional property WPD_CONTACT_BUSINESS_PHONE + hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_BUSINESS_PHONE, &pszTempString); + if(hrTemp == S_OK) + { + pContent->WorkPhone = pszTempString; + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszTempString); + } + + // Get next optional property WPD_OBJECT_ORIGINAL_FILE_NAME + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pszTempString); + if(hrTemp == S_OK) + { + pContent->FileName = pszTempString; + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszTempString); + } + + *ppContent = pContent; + } + else + { + delete pContent; + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeContactContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_AUDIO) + { + FakeMusicContent* pContent = NULL; + + pContent = new FakeMusicContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if (hrTemp == S_OK) + { + pContent->FileName = wszOriginalFileName; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeMusicContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_VIDEO) + { + FakeVideoContent* pContent = NULL; + + pContent = new FakeVideoContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if (hrTemp == S_OK) + { + pContent->FileName = wszOriginalFileName; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeVideoContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_IMAGE) + { + FakeImageContent* pContent = NULL; + + pContent = new FakeImageContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if (hrTemp == S_OK) + { + pContent->FileName = wszOriginalFileName; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeImageContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_NETWORK_ASSOCIATION) + { + FakeNetworkAssociationContent* pContent = NULL; + + pContent = new FakeNetworkAssociationContent(); + if (pContent) + { + BYTE *pValue = NULL; + DWORD cbValue = 0; + + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + // Get required property WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS + hr = pObjectProperties->GetBufferValue(WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, &pValue, &cbValue); + if (SUCCEEDED(hr)) + { + // Make sure the HostEUI64Array is the correct length + if ((cbValue % 8) == 0) + { + // Avoid copy by manually updating the PROPVARIANT + pContent->HostEUI64Array.vt = VT_VECTOR | VT_UI1; + pContent->HostEUI64Array.caub.cElems = cbValue; + pContent->HostEUI64Array.caub.pElems = pValue; + } + else + { + CoTaskMemFree(pValue); + pValue = NULL; + + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid value for HostEUI64Array"); + } + } + + if (SUCCEEDED(hr)) + { + *ppContent = pContent; + } + else + { + delete pContent; + } + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeNetworkAssociationContent object"); + } + } + else if(guidContentType == WPD_CONTENT_TYPE_WIRELESS_PROFILE) + { + FakeWirelessProfileContent* pContent = NULL; + + pContent = new FakeWirelessProfileContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if (hrTemp == S_OK) + { + pContent->FileName = wszOriginalFileName; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeWirelessProfileContent object"); + } + } + else + { + FakeContent* pContent; + + // Add generic file object + pContent = new FakeGenericFileContent(); + if (pContent) + { + pContent->Name = pszObjectName; + pContent->ParentID = pszParentID; + m_dwLastObjectID++; + pContent->ObjectID.Format(L"%d", m_dwLastObjectID); + pContent->ContentType = guidContentType; + pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString()); + + BOOL bTemp = FALSE; + // Get next optional property WPD_OBJECT_CAN_DELETE + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_CAN_DELETE, &bTemp); + if(hrTemp == S_OK) + { + pContent->CanDelete = bTemp; + } + // Get next optional property WPD_OBJECT_ISHIDDEN + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_ISHIDDEN, &bTemp); + if(hrTemp == S_OK) + { + pContent->IsHidden = bTemp; + } + // Get next optional property WPD_OBJECT_NON_CONSUMABLE + hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_NON_CONSUMABLE, &bTemp); + if(hrTemp == S_OK) + { + pContent->NonConsumable = bTemp; + } + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if (hrTemp == S_OK) + { + pContent->FileName = wszOriginalFileName; + } + + *ppContent = pContent; + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new FakeContent object"); + } + } + + if (wszOriginalFileName != NULL) + { + CoTaskMemFree(wszOriginalFileName); + wszOriginalFileName = NULL; + } + + return hr; + } + + HRESULT GetContentFormat( + _In_ LPCWSTR pszObjectID, + _Out_ GUID& guidObjectFormat) + { + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + guidObjectFormat = GUID_NULL; + + if (GetContent(pszObjectID, &pContent) == true) + { + guidObjectFormat = pContent->GetObjectFormat(); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + return hr; + } + + HRESULT EnableResource( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey) + { + HRESULT hr = S_OK; + FakeContent* pContent = NULL; + + if (GetContent(pszObjectID, &pContent) == true) + { + hr = pContent->EnableResource(ResourceKey); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + } + + return hr; + } + + HRESULT CopyChildrenToNewParent( + _In_ LPCWSTR pszParentObjectID, + _In_ LPCWSTR pszNewParentObjectID) + { + HRESULT hr = S_OK; + + CAtlStringW strParentObjectID = pszParentObjectID; + for (size_t Index = 0; Index < m_Content.GetCount(); Index++) + { + if (m_Content[Index]->ParentID == strParentObjectID) + { + LPWSTR pwszNewObjectID = NULL; + // Copy this object + hr = CopyObject(m_Content[Index]->ObjectID, pszNewParentObjectID, &pwszNewObjectID); + + // Free allocate new object ID + if (pwszNewObjectID != NULL) + { + CoTaskMemFree(pwszNewObjectID); + pwszNewObjectID = NULL; + } + + if (FAILED(hr)) + { + break; + } + } + } + return hr; + } + + HRESULT SupportsResource( + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey, + _Out_ BOOL* pbSupportsResource) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if((pszObjectID == NULL) || (pbSupportsResource == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pbSupportsResource = FALSE; + hr = GetSupportedResources(pszObjectID, &pKeys); + CHECK_HR(hr, "Failed to get supported resources for validation"); + + if (hr == S_OK) + { + DWORD dwIndex = 0; + PROPERTYKEY TempKey = WPD_PROPERTY_NULL; + while(pKeys->GetAt(dwIndex++, &TempKey) == S_OK) + { + if(IsEqualPropertyKey(ResourceKey, TempKey)) + { + *pbSupportsResource = TRUE; + } + } + } + + return hr; + } + + HRESULT UpdateContentObject( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceValues* pObjectProperties) + { + HRESULT hr = S_OK; + FakeContent* pElement = NULL; + + if (GetContent(pszObjectID, &pElement)) + { + HRESULT hrTemp = S_OK; + LPWSTR wszOriginalFileName = NULL; + LPWSTR wszObjectName = NULL; + + // Update selected properties; other properties will be discarded. + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName); + if(hrTemp == S_OK) + { + pElement->FileName = wszOriginalFileName; + CoTaskMemFree(wszOriginalFileName); + } + + hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &wszObjectName); + if(hrTemp == S_OK) + { + pElement->Name = wszObjectName; + CoTaskMemFree(wszObjectName); + } + + // Note: This fake driver does nothing with the data. The WriteData method is simply + // a dummy one, and the data size is a constant. + // Normally, a driver would also update the WPD_OBJECT_SIZE to the new data size. + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); + CHECK_HR(hr, "Failed to find the object '%ws'", pszObjectID); + } + + return hr; + } + +private: + CAtlArray<FakeContent*> m_Content; + DWORD m_dwLastObjectID; +}; + + diff --git a/wpd/WpdWudfSampleDriver/FakeFolderContent.h b/wpd/WpdWudfSampleDriver/FakeFolderContent.h new file mode 100644 index 00000000..1241a650 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeFolderContent.h @@ -0,0 +1,234 @@ +#include "mmsystem.h" +#include "FakeFolderContent.h.tmh" + +class FakeFolderContent : public FakeContent +{ +public: + FakeFolderContent() + { + } + + FakeFolderContent(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeFolderContent() + { + } + + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hr = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hr != S_OK) + { + CHECK_HR(hr, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hr; + } + + return hr; + } + +}; + + +class FakeMemoFolderContent : public FakeFolderContent +{ +public: + FakeMemoFolderContent() + { + } + + FakeMemoFolderContent(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeMemoFolderContent() + { + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_ICON + hr = pKeys->Add(WPD_RESOURCE_ICON); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_ICON to collection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (IsEqualPropertyKey(Key, WPD_RESOURCE_ICON)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO_FOLDER_ICON)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support this resource"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + + return hr; + } + + // This sample driver uses a embedded icon resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_ICON)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_MEMO_FOLDER_ICON); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO_FOLDER_ICON); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support this resource"); + } + + if (hr == S_OK) + { + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing image data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + } + + return hr; + } + +}; + diff --git a/wpd/WpdWudfSampleDriver/FakeImageContent.h b/wpd/WpdWudfSampleDriver/FakeImageContent.h new file mode 100644 index 00000000..64a6f02d --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeImageContent.h @@ -0,0 +1,402 @@ +#include "mmsystem.h" +#include "FakeImageContent.h.tmh" + +class FakeImageContent : public FakeContent +{ +public: + FakeImageContent() + { + } + + FakeImageContent(const FakeContent& src) + { + *this = src; + } + + virtual ~FakeImageContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_OBJECT_FORMAT_EXIF, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for WPD_OBJECT_FORMAT_EXIF"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_MEDIA_WIDTH + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_MEDIA_WIDTH, 800); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_WIDTH")); + return hrSetValue; + } + + // Add WPD_MEDIA_HEIGHT + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_MEDIA_HEIGHT, 600); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_HEIGHT")); + return hrSetValue; + } + + // Add WPD_OBJECT_DATE_CREATED + SYSTEMTIME systemtime = {0}; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_DATE; + systemtime.wDay = 26; + systemtime.wDayOfWeek = 0; + systemtime.wHour = 5; + systemtime.wMinute = 30; + systemtime.wMilliseconds = 100; + systemtime.wMonth = 6; + systemtime.wSecond = 15; + systemtime.wYear = 2004; + + SystemTimeToVariantTime(&systemtime, &pv.date); + + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_CREATED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_CREATED")); + return hrSetValue; + } + + PropVariantClear(&pv); + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hrSetValue = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hrSetValue; + } + + // Add WPD_OBJECT_SIZE + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_IMAGE)); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_SIZE")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_OBJECT_ORIGINAL_FILE_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + FileName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because type was not VT_LPWSTR"); + } + } + else + { + hr = FakeContent::WriteValue(key, Value); + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(key.fmtid), key.pid, ObjectID); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr)) + { + // Add the thumbnail resource + hr = pKeys->Add(WPD_RESOURCE_THUMBNAIL); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_THUMBNAIL to supported resource list"); + } + + if (SUCCEEDED(hr)) + { + // Add the audio annotation resource + hr = pKeys->Add(WPD_RESOURCE_AUDIO_CLIP); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_AUDIO_CLIP to supported resource list"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + DWORD dwSize = 0; + if (IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT)) + { + dwSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_IMAGE); + } + else if(IsEqualPropertyKey(Key, WPD_RESOURCE_THUMBNAIL)) + { + dwSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_IMAGE_THUMBNAIL); + } + else if(IsEqualPropertyKey(Key, WPD_RESOURCE_AUDIO_CLIP)) + { + dwSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_AUDIO_ANNOTATION); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Could not return resource attributes for unknown resource %ws.%d", (LPWSTR)CComBSTR(Key.fmtid), Key.pid); + } + + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, dwSize); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + } + + if (SUCCEEDED(hr) && (IsEqualPropertyKey(Key, WPD_RESOURCE_THUMBNAIL))) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_MEDIA_WIDTH, 96); + CHECK_HR(hr, "Failed to set WPD_MEDIA_WIDTH"); + + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_MEDIA_HEIGHT, 96); + CHECK_HR(hr, "Failed to set WPD_MEDIA_HEIGHT"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_EXIF); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + + if (SUCCEEDED(hr) && (IsEqualPropertyKey(Key, WPD_RESOURCE_AUDIO_CLIP))) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_AUDIO_BITRATE, 180224 /* 176kbps */); + CHECK_HR(hr, "Failed to set WPD_AUDIO_BITRATE"); + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetFloatValue(WPD_AUDIO_CHANNEL_COUNT, 1.0f /* Mono */); + CHECK_HR(hr, "Failed to set WPD_AUDIO_CHANNEL_COUNT"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_AUDIO_FORMAT_CODE, WAVE_FORMAT_PCM); + CHECK_HR(hr, "Failed to set WPD_AUDIO_FORMAT_CODE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_MEDIA_SAMPLE_RATE, 22000 /* 22kHz */); + CHECK_HR(hr, "Failed to set WPD_MEDIA_SAMPLE_RATE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_AUDIO_BIT_DEPTH, 8); + CHECK_HR(hr, "Failed to set WPD_AUDIO_BIT_DEPTH"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_WAVE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded image file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_DEFAULT)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_IMAGE); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_IMAGE); + } + else if(IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_THUMBNAIL)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_IMAGE_THUMBNAIL); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_IMAGE_THUMBNAIL); + } + else if(IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_AUDIO_CLIP)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_AUDIO_ANNOTATION); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_AUDIO_ANNOTATION); + } + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get DLL resource representing WPD resource data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded image file data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_EXIF; + } +}; + diff --git a/wpd/WpdWudfSampleDriver/FakeMemoContent.h b/wpd/WpdWudfSampleDriver/FakeMemoContent.h new file mode 100644 index 00000000..a726336c --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeMemoContent.h @@ -0,0 +1,347 @@ +#include "FakeMemoContent.h.tmh" + +class FakeMemoContent : public FakeContent +{ +public: + FakeMemoContent() + { + } + + FakeMemoContent(const FakeMemoContent& src) + { + *this = src; + } + + virtual ~FakeMemoContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (hr == S_OK) + { + hr = AddSupportedProperties(FakeMemoContent_Format, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for FakeMemoContent"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + CAtlStringW strVal; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_OBJECT_SIZE + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO)); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_SIZE")); + return hrSetValue; + } + + // Add WPD_OBJECT_DATE_AUTHORED and WPD_OBJECT_DATE_MODIFIED + SYSTEMTIME systemtime = {0}; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_DATE; + systemtime.wDay = 26; + systemtime.wDayOfWeek = 0; + systemtime.wHour = 5; + systemtime.wMinute = 30; + systemtime.wMilliseconds = 100; + systemtime.wMonth = 6; + systemtime.wSecond = 15; + systemtime.wYear = 2004; + + SystemTimeToVariantTime(&systemtime, &pv.date); + + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_AUTHORED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_AUTHORED ")); + return hrSetValue; + } + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_MODIFIED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_MODIFIED ")); + return hrSetValue; + } + + PropVariantClear(&pv); + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hrSetValue = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_OBJECT_ORIGINAL_FILE_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + FileName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because type was not VT_LPWSTR"); + } + } + else + { + hr = FakeContent::WriteValue(key, Value); + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(key.fmtid), key.pid, ObjectID); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_ICON + hr = pKeys->Add(WPD_RESOURCE_ICON); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_ICON to collection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (IsEqualPropertyKey(Key, WPD_RESOURCE_ICON)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO_ICON)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + + } + else if (IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, FakeMemoContent_Format); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support this resource"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + + return hr; + } + + // This sample driver uses a embedded resources as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_ICON)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_MEMO_ICON); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO_ICON); + } + else if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_DEFAULT)) + { + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_MEMO); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_MEMO); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support this resource"); + } + + if (hr == S_OK) + { + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing image data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return FakeMemoContent_Format; + } +}; diff --git a/wpd/WpdWudfSampleDriver/FakeMusicContent.h b/wpd/WpdWudfSampleDriver/FakeMusicContent.h new file mode 100644 index 00000000..846a2f84 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeMusicContent.h @@ -0,0 +1,354 @@ +#include "FakeMusicContent.h.tmh" + +class FakeMusicContent : public FakeContent +{ +public: + FakeMusicContent() + { + } + + FakeMusicContent(const FakeMusicContent& src) + { + *this = src; + } + + virtual ~FakeMusicContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_OBJECT_FORMAT_WMA, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for FakeMusicContent"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + CAtlStringW strVal; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_MEDIA_TITLE + strVal.Format(L"Song_%ws", ObjectID.GetString()); + hrSetValue = pStore->SetStringValue(WPD_MEDIA_TITLE, strVal.GetString()); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_TITLE")); + return hrSetValue; + } + + // Add WPD_MEDIA_ARTIST + strVal.Format(L"Artist_%ws", ObjectID.GetString()); + hrSetValue = pStore->SetStringValue(WPD_MEDIA_ARTIST, strVal.GetString()); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_ARTIST")); + return hrSetValue; + } + + // Add WPD_MEDIA_DURATION + ULONGLONG ulDuration = 210000; + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_MEDIA_DURATION, ulDuration); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MUSIC_DURATION")); + return hrSetValue; + } + + // Add WPD_OBJECT_SIZE + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MUSIC)); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_SIZE")); + return hrSetValue; + } + + // Add WPD_OBJECT_DATE_AUTHORED and WPD_OBJECT_DATE_MODIFIED + SYSTEMTIME systemtime = {0}; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_DATE; + systemtime.wDay = 26; + systemtime.wDayOfWeek = 0; + systemtime.wHour = 5; + systemtime.wMinute = 30; + systemtime.wMilliseconds = 100; + systemtime.wMonth = 6; + systemtime.wSecond = 15; + systemtime.wYear = 2004; + + SystemTimeToVariantTime(&systemtime, &pv.date); + + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_AUTHORED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_AUTHORED ")); + return hrSetValue; + } + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_MODIFIED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_MODIFIED ")); + return hrSetValue; + } + + PropVariantClear(&pv); + + // Add WPD_MUSIC_ALBUM + strVal.Format(L"Album_%ws", ObjectID.GetString()); + hrSetValue = pStore->SetStringValue(WPD_MUSIC_ALBUM, strVal.GetString()); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MUSIC_ALBUM")); + return hrSetValue; + } + + // Add WPD_MEDIA_GENRE + hrSetValue = pStore->SetStringValue(WPD_MEDIA_GENRE, L"Top 40"); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_GENRE")); + return hrSetValue; + } + + // Add WPD_MUSIC_TRACK + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_MUSIC_TRACK, 3); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MUSIC_TRACK")); + return hrSetValue; + } + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hrSetValue = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_OBJECT_ORIGINAL_FILE_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + FileName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because type was not VT_LPWSTR"); + } + } + else + { + hr = FakeContent::WriteValue(key, Value); + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(key.fmtid), key.pid, ObjectID); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_MUSIC)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_WMA); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded music file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + UNREFERENCED_PARAMETER(ResourceKey); + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_MUSIC); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_MUSIC); + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing image data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded music file data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_WMA; + } +}; + diff --git a/wpd/WpdWudfSampleDriver/FakeVideoContent.h b/wpd/WpdWudfSampleDriver/FakeVideoContent.h new file mode 100644 index 00000000..1fa0d8a1 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/FakeVideoContent.h @@ -0,0 +1,368 @@ +#include "FakeVideoContent.h.tmh" + +class FakeVideoContent : public FakeContent +{ +public: + FakeVideoContent() + { + } + + FakeVideoContent(const FakeVideoContent& src) + { + *this = src; + } + + virtual ~FakeVideoContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_OBJECT_FORMAT_WMV, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for FakeVideoContent"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + CAtlStringW strVal; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_MEDIA_TITLE + strVal.Format(L"Video_%ws", ObjectID.GetString()); + hrSetValue = pStore->SetStringValue(WPD_MEDIA_TITLE, strVal.GetString()); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_TITLE")); + return hrSetValue; + } + + // Add WPD_MEDIA_DURATION + ULONGLONG ulDuration = 6000; + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_MEDIA_DURATION, ulDuration); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MUSIC_DURATION")); + return hrSetValue; + } + + // Add WPD_OBJECT_SIZE + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_VIDEO)); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_SIZE")); + return hrSetValue; + } + + // Add WPD_OBJECT_DATE_AUTHORED and WPD_OBJECT_DATE_MODIFIED + SYSTEMTIME systemtime = {0}; + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + pv.vt = VT_DATE; + systemtime.wDay = 27; + systemtime.wDayOfWeek = 1; + systemtime.wHour = 5; + systemtime.wMinute = 30; + systemtime.wMilliseconds = 100; + systemtime.wMonth = 6; + systemtime.wSecond = 15; + systemtime.wYear = 2004; + + SystemTimeToVariantTime(&systemtime, &pv.date); + + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_AUTHORED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_AUTHORED ")); + return hrSetValue; + } + hrSetValue = pStore->SetValue(WPD_OBJECT_DATE_MODIFIED , &pv); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_DATE_MODIFIED ")); + return hrSetValue; + } + + PropVariantClear(&pv); + + // Add WPD_MEDIA_WIDTH + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_MEDIA_WIDTH, 160); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_WIDTH")); + return hrSetValue; + } + + // Add WPD_MEDIA_HEIGHT + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_MEDIA_HEIGHT, 120); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_MEDIA_HEIGHT")); + return hrSetValue; + } + + // Add WPD_OBJECT_ORIGINAL_FILE_NAME + pvValue = FileName; + hrSetValue = pStore->SetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pvValue); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME")); + return hrSetValue; + } + + // Add WPD_VIDEO_SCAN_TYPE + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_VIDEO_SCAN_TYPE, WPD_VIDEO_SCAN_TYPE_UNUSED); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_VIDEO_SCAN_TYPE")); + return hrSetValue; + } + + // Add WPD_VIDEO_BITRATE + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_VIDEO_BITRATE, 40960); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_VIDEO_BITRATE")); + return hrSetValue; + } + + // Add WPD_VIDEO_FOURCC_CODE + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_VIDEO_FOURCC_CODE, MAKEFOURCC('W', 'M', 'V', '3')); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_VIDEO_FOURCC_CODE")); + return hrSetValue; + } + + // Add WPD_OBJECT_GENERATE_THUMBNAIL_FROM_RESOURCE + hrSetValue = pStore->SetBoolValue(WPD_OBJECT_GENERATE_THUMBNAIL_FROM_RESOURCE, TRUE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_OBJECT_GENERATE_THUMBNAIL_FROM_RESOURCE ")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_OBJECT_ORIGINAL_FILE_NAME)) + { + if(Value.vt == VT_LPWSTR) + { + if (Value.pwszVal != NULL && Value.pwszVal[0] != L'\0') + { + FileName = Value.pwszVal; + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because value was an empty string"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME because type was not VT_LPWSTR"); + } + } + else + { + hr = FakeContent::WriteValue(key, Value); + CHECK_HR(hr, "Property %ws.%d on [%ws] does not support set value operation", CComBSTR(key.fmtid), key.pid, ObjectID); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add WPD_RESOURCE_DEFAULT + hr = pKeys->Add(WPD_RESOURCE_DEFAULT); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_DEFAULT to collection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the common resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + // Override the size attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(IDR_WPD_SAMPLEDRIVER_VIDEO)); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + // Override the format attribute for this resource. + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_WMV); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded music file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + UNREFERENCED_PARAMETER(ResourceKey); + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *pdwNumBytesRead = 0; + + pResource = GetResourceData(IDR_WPD_SAMPLEDRIVER_VIDEO); + dwObjectDataSize = GetResourceSize(IDR_WPD_SAMPLEDRIVER_VIDEO); + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing image data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded music file data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_WMV; + } +}; + diff --git a/wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h b/wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h new file mode 100644 index 00000000..e428e278 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h @@ -0,0 +1,378 @@ +#include "NetworkConfigFakeContent.h.tmh" + +class NetworkConfigFakeContent : public FakeContent +{ +public: + NetworkConfigFakeContent() + { + } + + virtual ~NetworkConfigFakeContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for NetworkConfigFakeContent"); + } + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_FUNCTIONAL_OBJECT_CATEGORY + hrSetValue = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION); + if (hrSetValue != S_OK) + { + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT GetAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = AddFixedPropertyAttributes(WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, Key, pAttributes); + CHECK_HR(hr, "Failed to add fixed property attributes for %ws.%d on NetworkConfigFakeContent", CComBSTR(Key.fmtid), Key.pid); + } + + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on IPortableDeviceValues"); + } + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + *ppAttributes = NULL; + return E_NOTIMPL; + } + + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + UNREFERENCED_PARAMETER(ResourceKey); + UNREFERENCED_PARAMETER(dwStartByte); + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(dwNumBytesToRead); + *pdwNumBytesRead = 0; + return E_NOTIMPL; + } + + virtual HRESULT WriteData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _In_reads_(dwNumBytesToWrite) BYTE* pBuffer, + DWORD dwNumBytesToWrite, + _Out_ DWORD* pdwNumBytesWritten) + { + UNREFERENCED_PARAMETER(ResourceKey); + UNREFERENCED_PARAMETER(dwStartByte); + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(dwNumBytesToWrite); + *pdwNumBytesWritten = 0; + return E_NOTIMPL; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_PROPERTIES_ONLY; + } +}; + +class FakeNetworkAssociationContent : public FakeContent +{ +public: + FakeNetworkAssociationContent() + { + PropVariantInit(&HostEUI64Array); + } + + virtual ~FakeNetworkAssociationContent() + { + } + + virtual HRESULT GetAllValues(_In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + PropVariantWrapper pvValue; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS + if (HostEUI64Array.vt == VT_EMPTY) + { + // This value is supposed to be available, but it is just a soft error if it is missing + pvValue.SetErrorValue(HRESULT_FROM_WIN32(ERROR_NOT_FOUND)); + hrSetValue = pStore->SetValue(WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, &pvValue); + + hr = S_FALSE; + } + else + { + hrSetValue = pStore->SetValue(WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, &HostEUI64Array); + } + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT WriteValue( + _In_ REFPROPERTYKEY key, + _In_ REFPROPVARIANT Value) + { + HRESULT hr = S_OK; + PropVariantWrapper pvValue; + + if(IsEqualPropertyKey(key, WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS)) + { + if (Value.vt == (VARTYPE)(VT_VECTOR | VT_UI1)) + { + if ((Value.caub.cElems % 8) == 0) + { + if (FAILED(PropVariantClear(&HostEUI64Array))) + { + PropVariantInit(&HostEUI64Array); + } + + hr = PropVariantCopy(&HostEUI64Array, &Value); + CHECK_HR(hr, "Error setting WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS"); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS because value was the wrong length"); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Failed to set WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS because type was not VT_VECTOR|VT_UI1"); + } + } + else + { + hr = FakeContent::WriteValue(key, Value); + } + + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pKeys); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + *ppAttributes = NULL; + return E_NOTIMPL; + } + + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + UNREFERENCED_PARAMETER(ResourceKey); + UNREFERENCED_PARAMETER(dwStartByte); + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(dwNumBytesToRead); + *pdwNumBytesRead = 0; + return E_NOTIMPL; + } + + virtual HRESULT WriteData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _In_reads_(dwNumBytesToWrite) BYTE* pBuffer, + DWORD dwNumBytesToWrite, + _Out_ DWORD* pdwNumBytesWritten) + { + UNREFERENCED_PARAMETER(ResourceKey); + UNREFERENCED_PARAMETER(dwStartByte); + UNREFERENCED_PARAMETER(pBuffer); + UNREFERENCED_PARAMETER(dwNumBytesToWrite); + *pdwNumBytesWritten = 0; + return E_NOTIMPL; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION; + } + + PROPVARIANT HostEUI64Array; +}; + + +class FakeWirelessProfileContent : public FakeContent +{ +public: + FakeWirelessProfileContent() + { + } + + virtual ~FakeWirelessProfileContent() + { + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_MICROSOFT_WFC; + } +}; + + diff --git a/wpd/WpdWudfSampleDriver/Queue.cpp b/wpd/WpdWudfSampleDriver/Queue.cpp new file mode 100644 index 00000000..6741c920 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Queue.cpp @@ -0,0 +1,334 @@ +// Queue.cpp : Implementation of CQueue + +#include "stdafx.h" +#include "Queue.h" +#include <devioctl.h> +#include <initguid.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_ 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"); + } + + // 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"); + } + + *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; +} + +// 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; + 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) + { + // Get the device object + pQueue->GetDevice(&pDevice ); + hr = ProcessWpdMessage(ControlCode, + pClientContextMap, + pDevice, + pInputBuffer, + (DWORD)cbInputBuffer, + pOutputBuffer, + (DWORD)cbOutputBuffer, + &dwBytesWritten); + } + } + else + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "WDF File Object is NULL"); + } + } + 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/WpdWudfSampleDriver/Queue.h b/wpd/WpdWudfSampleDriver/Queue.h new file mode 100644 index 00000000..d53ce76e --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Queue.h @@ -0,0 +1,93 @@ +// Queue.h : Declaration of the CQueue + +#pragma once +#include "resource.h" // main symbols +#include "WpdWudfSampleDriver.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_ 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); + + CComPtr<IWpdSerializer> m_pWpdSerializer; + CComAutoCriticalSection m_CriticalSection; +}; + diff --git a/wpd/WpdWudfSampleDriver/ReadMe.md b/wpd/WpdWudfSampleDriver/ReadMe.md new file mode 100644 index 00000000..f47a712b --- /dev/null +++ b/wpd/WpdWudfSampleDriver/ReadMe.md @@ -0,0 +1,20 @@ +WPD WUDF sample driver +====================== + +The comprehensive WPD sample driver (WpdWudfSampleDriver) demonstrates virtually all aspects of the Microsoft Windows Portable Devides (WPD) device driver interface (DDI). This driver is built as a normal User-Mode Driver Framework (UMDF) driver that also processes the WPD command set. Although this driver does not interact with actual hardware, it simulates communicating with a device that supports phone contacts, pictures, music, and video. + +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. + +Some of the tasks that are accomplished by the WpdWudfSampleDriver are written for the advanced Windows Portable Devices (WPD) driver developer. + +For a complete description of this sample and its underlying code and functionality, refer to the [WPD WUDF Sample Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597723) 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/WpdWudfSampleDriver/RenderingInformationFakeContent.h b/wpd/WpdWudfSampleDriver/RenderingInformationFakeContent.h new file mode 100644 index 00000000..75b2a0bd --- /dev/null +++ b/wpd/WpdWudfSampleDriver/RenderingInformationFakeContent.h @@ -0,0 +1,138 @@ +#include "RenderingInformationFakeContent.h.tmh" + +class RenderingInformationFakeContent : public FakeContent +{ +public: + RenderingInformationFakeContent() + { + } + + RenderingInformationFakeContent(const RenderingInformationFakeContent& src) + { + *this = src; + } + + virtual ~RenderingInformationFakeContent() + { + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for RenderingInformationFakeContent"); + } + + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_FUNCTIONAL_OBJECT_CATEGORY + hrSetValue = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION); + if (hrSetValue != S_OK) + { + CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + return hrSetValue; + } + + // Add WPD_RENDERING_INFORMATION_PROFILES + hrSetValue = SetRenderingProfiles(pStore); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, ("Failed to set WPD_RENDERING_INFORMATION_PROFILES")); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT GetAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = AddFixedPropertyAttributes(WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, Key, pAttributes); + CHECK_HR(hr, "Failed to add fixed property attributes for %ws.%d on RenderingInformationFakeContent", 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 the property attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + virtual GUID GetObjectFormat() + { + return WPD_OBJECT_FORMAT_UNSPECIFIED; + } +}; + diff --git a/wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav b/wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav Binary files differnew file mode 100644 index 00000000..fa21dd53 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav diff --git a/wpd/WpdWudfSampleDriver/SampleContactPhoto.png b/wpd/WpdWudfSampleDriver/SampleContactPhoto.png Binary files differnew file mode 100644 index 00000000..7979e772 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleContactPhoto.png diff --git a/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico b/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico Binary files differnew file mode 100644 index 00000000..33a1d1a5 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico diff --git a/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico b/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico Binary files differnew file mode 100644 index 00000000..5598b83b --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico diff --git a/wpd/WpdWudfSampleDriver/SampleImage.jpg b/wpd/WpdWudfSampleDriver/SampleImage.jpg Binary files differnew file mode 100644 index 00000000..d018ec4e --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleImage.jpg diff --git a/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg b/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg Binary files differnew file mode 100644 index 00000000..39738839 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg diff --git a/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico b/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico Binary files differnew file mode 100644 index 00000000..e1496876 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico diff --git a/wpd/WpdWudfSampleDriver/SampleMemo.txt b/wpd/WpdWudfSampleDriver/SampleMemo.txt new file mode 100644 index 00000000..c5436941 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleMemo.txt @@ -0,0 +1 @@ +This is a sample memo.
\ No newline at end of file diff --git a/wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico b/wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico Binary files differnew file mode 100644 index 00000000..9c07ff66 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico diff --git a/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico b/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico Binary files differnew file mode 100644 index 00000000..1bde7cc8 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico diff --git a/wpd/WpdWudfSampleDriver/SampleMusic.wma b/wpd/WpdWudfSampleDriver/SampleMusic.wma Binary files differnew file mode 100644 index 00000000..ae9b8f40 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleMusic.wma diff --git a/wpd/WpdWudfSampleDriver/SampleVideo.wmv b/wpd/WpdWudfSampleDriver/SampleVideo.wmv Binary files differnew file mode 100644 index 00000000..657d57ab --- /dev/null +++ b/wpd/WpdWudfSampleDriver/SampleVideo.wmv diff --git a/wpd/WpdWudfSampleDriver/Stdafxsrc.cpp b/wpd/WpdWudfSampleDriver/Stdafxsrc.cpp new file mode 100644 index 00000000..5105a28d --- /dev/null +++ b/wpd/WpdWudfSampleDriver/Stdafxsrc.cpp @@ -0,0 +1 @@ +#include "Stdafx.h"
\ No newline at end of file diff --git a/wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h b/wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h new file mode 100644 index 00000000..d7ca2a7b --- /dev/null +++ b/wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h @@ -0,0 +1,327 @@ +#include "StorageObjectFakeContent.h.tmh" + +class StorageObjectFakeContent : public FakeContent +{ +public: + StorageObjectFakeContent() + { + Capacity = 0; + FreeSpaceInBytes = 0; + IsExternalStorage = FALSE; + } + + StorageObjectFakeContent(const FakeContent& src) + { + *this = src; + } + + virtual ~StorageObjectFakeContent() + { + } + + virtual StorageObjectFakeContent& operator= (const StorageObjectFakeContent& src) + { + ObjectID = src.ObjectID; + PersistentUniqueID = src.PersistentUniqueID; + ParentID = src.ParentID; + Name = src.Name; + ContentType = src.ContentType; + MarkedForDeletion = src.MarkedForDeletion; + CanDelete = src.CanDelete; + IsHidden = src.IsHidden; + IsSystem = src.IsSystem; + NonConsumable = src.NonConsumable; + Capacity = src.Capacity; + FreeSpaceInBytes = src.FreeSpaceInBytes; + IsExternalStorage = src.IsExternalStorage; + + return *this; + } + + virtual HRESULT GetSupportedProperties(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(WPD_FUNCTIONAL_CATEGORY_STORAGE, ppKeys); + CHECK_HR(hr, "Failed to add additional properties for StorageObjectFakeContent"); + } + return hr; + } + + virtual HRESULT GetAllValues( + _In_ IPortableDeviceValues* pStore) + { + HRESULT hr = S_OK; + HRESULT hrSetValue = S_OK; + + if(pStore == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + // Call the base class to fill in the standard properties + hr = FakeContent::GetAllValues(pStore); + if (FAILED(hr)) + { + CHECK_HR(hr, "Failed to get basic property set"); + return hr; + } + + // Add WPD_STORAGE_CAPACITY + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY, Capacity); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_STORAGE_CAPACITY"); + return hrSetValue; + } + + // Add WPD_STORAGE_FREE_SPACE_IN_BYTES + hrSetValue = pStore->SetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, FreeSpaceInBytes); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_STORAGE_FREE_SPACE_IN_BYTES"); + return hrSetValue; + } + + // Add WPD_FUNCTIONAL_OBJECT_CATEGORY + hrSetValue = pStore->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_STORAGE); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY"); + return hrSetValue; + } + + // Add WPD_STORAGE_TYPE + hrSetValue = pStore->SetUnsignedIntegerValue(WPD_STORAGE_TYPE, WPD_STORAGE_TYPE_FIXED_RAM); + if (hrSetValue != S_OK) + { + CHECK_HR(hrSetValue, "Failed to set WPD_STORAGE_TYPE"); + return hrSetValue; + } + + return hr; + } + + virtual HRESULT GetAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = AddFixedPropertyAttributes(FakeStorageContent_Format, Key, pAttributes); + CHECK_HR(hr, "Failed to add fixed property attributes for %ws.%d on StorageObjectFakeContent", CComBSTR(Key.fmtid), Key.pid); + } + + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on IPortableDeviceValues"); + } + return hr; + } + + virtual HRESULT GetSupportedResources(_COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) + { + HRESULT hr = S_OK; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppKeys = NULL; + + if (SUCCEEDED(hr)) + { + // Call the base class to fill in the standard resources if any exist + hr = FakeContent::GetSupportedResources(&pKeys); + CHECK_HR(hr, "Failed to get basic supported resources"); + } + + if (SUCCEEDED(hr)) + { + // Add the icon resource + hr = pKeys->Add(WPD_RESOURCE_ICON); + CHECK_HR(hr, "Failed to add WPD_RESOURCE_ICON to supported resource list"); + } + + if (SUCCEEDED(hr)) + { + hr = pKeys->QueryInterface(IID_IPortableDeviceKeyCollection, (VOID**) ppKeys); + CHECK_HR(hr, "Failed to QI for IPortableDeviceKeyCollection on IPortableDeviceKeyCollection"); + } + + return hr; + } + + virtual HRESULT GetResourceAttributes( + _In_ REFPROPERTYKEY Key, + _COM_Outptr_ IPortableDeviceValues** ppAttributes) + { + UNREFERENCED_PARAMETER(Key); + + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + // Fill in the standard resource attributes + hr = GetCommonResourceAttributes(&pAttributes); + CHECK_HR(hr, "Failed to get common resource attributes set"); + } + + if (SUCCEEDED(hr)) + { + if (IsEqualPropertyKey(Key, WPD_RESOURCE_ICON)) + { + // Override the size attribute for this resource. + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetResourceSize(GetResourceID())); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + + // Override the format attribute for this resource. + hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, WPD_OBJECT_FORMAT_ICON); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT"); + } + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; + } + + // This sample driver uses a embedded image file resource as its data. + virtual HRESULT ReadData( + _In_ REFPROPERTYKEY ResourceKey, + DWORD dwStartByte, + _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer, + DWORD dwNumBytesToRead, + _Out_ DWORD* pdwNumBytesRead) + { + HRESULT hr = S_OK; + DWORD dwBytesToTransfer = 0; + DWORD dwObjectDataSize = 0; + PBYTE pResource = NULL; + + if((pBuffer == NULL) || + (pdwNumBytesRead == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + if (IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_DEFAULT)) + { + return FakeContent::ReadData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToRead, pdwNumBytesRead); + } + + *pdwNumBytesRead = 0; + + pResource = GetResourceData(GetResourceID()); + dwObjectDataSize = GetResourceSize(GetResourceID()); + + if (pResource == NULL) + { + hr = E_UNEXPECTED; + CHECK_HR(hr, "Failed to get resource representing the device icon data"); + } + + // Calculate how many bytes to transfer + if (hr == S_OK) + { + if (dwStartByte < dwObjectDataSize) + { + dwBytesToTransfer = (dwObjectDataSize - dwStartByte); + if (dwBytesToTransfer > dwNumBytesToRead) + { + dwBytesToTransfer = dwNumBytesToRead; + } + } + } + + // Copy the embedded image file data. + if ((hr == S_OK) && (dwBytesToTransfer > 0)) + { + memcpy(pBuffer, pResource + dwStartByte, dwBytesToTransfer); + } + + if (hr == S_OK) + { + *pdwNumBytesRead = dwBytesToTransfer; + } + + return hr; + } + + virtual GUID GetObjectFormat() + { + return FakeStorageContent_Format; + } + + UINT GetResourceID() + { + UINT uiResourceID = IDR_WPD_SAMPLEDRIVER_INTERNAL_STORAGE_ICON; + if(IsExternalStorage == TRUE) + { + uiResourceID = IDR_WPD_SAMPLEDRIVER_EXTERNAL_STORAGE_ICON; + } + + return uiResourceID; + } + + BOOL IsExternalStorage; + ULONGLONG Capacity; + ULONGLONG FreeSpaceInBytes; +}; + diff --git a/wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp b/wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp new file mode 100644 index 00000000..b06591b6 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp @@ -0,0 +1,456 @@ +#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; +} + +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_PROPERTIES_BULK) + { + hr = m_ObjectPropertiesBulk.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_OBJECT_MANAGEMENT) + { + hr = m_ObjectManagement.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_CAPABILITIES) + { + hr = m_Capabilities.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_STORAGE) + { + hr = m_Storage.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if (CommandKey.fmtid == WPD_CATEGORY_NETWORK_CONFIGURATION) + { + hr = m_NetworkConfig.DispatchWpdMessage(CommandKey, pParams, pResults); + } + else if(IsEqualPropertyKey(CommandKey, WPD_COMMAND_COMMON_SAVE_CLIENT_INFORMATION)) + { + hr = OnSaveClientInfo(pParams, pResults); + } + else if(IsEqualPropertyKey(CommandKey, WPD_COMMAND_COMMON_GET_OBJECT_IDS_FROM_PERSISTENT_UNIQUE_IDS)) + { + hr = OnGetObjectIDsFromPersistentUniqueIDs(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 recieved. + // 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 to initialize the driver object. + * In a real driver, this is where the driver would set up it's I/O libraries + * and so on. + * + * For this sample driver, since we don't have a real device, we + * simply ignore the port name and initialize our internal FakeDevice. + * + */ +HRESULT WpdBaseDriver::Initialize( + _In_ LPCWSTR pszPortName, + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension) +{ + UNREFERENCED_PARAMETER(pszPortName); + + HRESULT hr = S_OK; + + if(pPortableDeviceClassExtension == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pPortableDeviceClassExtension = pPortableDeviceClassExtension; + + if (hr == S_OK) + { + hr = m_FakeDevice.InitializeContent(m_pPortableDeviceClassExtension); + CHECK_HR(hr, "Failed to initialize our fake device"); + } + + if (hr == S_OK) + { + hr = m_ObjectEnum.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our object enumerator"); + } + + if (hr == S_OK) + { + hr = m_ObjectProperties.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our properties object"); + } + + if (hr == S_OK) + { + hr = m_ObjectPropertiesBulk.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our properties bulk object"); + } + + if (hr == S_OK) + { + hr = m_ObjectResources.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our resources object"); + } + + if (hr == S_OK) + { + hr = m_ObjectManagement.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our object managment object"); + } + + if (hr == S_OK) + { + hr = m_Capabilities.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our capabilities object"); + } + + if (hr == S_OK) + { + hr = m_Storage.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our storage object"); + } + + if (hr == S_OK) + { + hr = m_NetworkConfig.Initialize(&m_FakeDevice); + CHECK_HR(hr, "Failed to initialize our network config object"); + } + + 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() +{ +} + +/** + * Save the client information + */ +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 = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_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 strKey = bstrContext; + hr = pContextMap->Add(strKey, pContext); + 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, bstrContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_COMMON_CLIENT_INFORMATION_CONTEXT"); + } + } + + SAFE_RELEASE(pContext); // Always release the context, pContextMap::Add would have AddRef'ed it + SAFE_RELEASE(pContextMap); + + 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; + DWORD dwCount = 0; + 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"); + } + + // Iterate through the persistent ID list and add the equivalent object ID for each element. + if (hr == S_OK) + { + hr = pPersistentIDs->GetCount(&dwCount); + CHECK_HR(hr, "Failed to get count from persistent ID collection"); + + if (hr == S_OK) + { + DWORD dwIndex = 0; + PROPVARIANT pvPersistentID = {0}; + PROPVARIANT pvObjectID = {0}; + PropVariantInit(&pvPersistentID); + PropVariantInit(&pvObjectID); + + for(dwIndex = 0; dwIndex < dwCount; dwIndex++) + { + pvObjectID.vt = VT_LPWSTR; + hr = pPersistentIDs->GetAt(dwIndex, &pvPersistentID); + CHECK_HR(hr, "Failed to get persistent ID at index %d", dwIndex); + + if (hr == S_OK) + { + hr = m_FakeDevice.GetObjectIDFromPersistentID(pvPersistentID.pwszVal, &pvObjectID.pwszVal); + // Don't log this error, since it is expected if the object is not found + } + + if(FAILED(hr)) + { + hr = S_OK; + pvObjectID.pwszVal = AtlAllocTaskWideString(L""); + if(pvObjectID.pwszVal == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate memory for empty ObjectID"); + } + } + + if (hr == S_OK) + { + hr = pObjectIDs->Add(&pvObjectID); + CHECK_HR(hr, "Failed to add next Object ID"); + } + + PropVariantClear(&pvPersistentID); + PropVariantClear(&pvObjectID); + + if(FAILED(hr)) + { + break; + } + } + } + } + + 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; +} + diff --git a/wpd/WpdWudfSampleDriver/WpdBaseDriver.h b/wpd/WpdWudfSampleDriver/WpdBaseDriver.h new file mode 100644 index 00000000..ee092fd6 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdBaseDriver.h @@ -0,0 +1,113 @@ +#pragma once + +// This class is used to store the connected client information. +// This is for demonstration purposes only - this driver does not +// make use of the 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 WpdBaseDriver : + public IUnknown +{ +public: + WpdBaseDriver(); + virtual ~WpdBaseDriver(); + + HRESULT Initialize(_In_ LPCWSTR pszPortName, _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension); + VOID Uninitialize(); + + HRESULT DispatchWpdMessage(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT OnSaveClientInfo(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetObjectIDsFromPersistentUniqueIDs(_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); + +private: + WpdObjectEnumerator m_ObjectEnum; + WpdObjectProperties m_ObjectProperties; + WpdObjectPropertiesBulk m_ObjectPropertiesBulk; + WpdObjectResources m_ObjectResources; + WpdObjectManagement m_ObjectManagement; + WpdCapabilities m_Capabilities; + WpdStorage m_Storage; + WpdNetworkConfig m_NetworkConfig; + FakeDevice m_FakeDevice; + CComPtr<IPortableDeviceClassExtension> m_pPortableDeviceClassExtension; + ULONG m_cRef; +}; + diff --git a/wpd/WpdWudfSampleDriver/WpdCapabilities.cpp b/wpd/WpdWudfSampleDriver/WpdCapabilities.cpp new file mode 100644 index 00000000..1656eaf4 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdCapabilities.cpp @@ -0,0 +1,899 @@ +#include "stdafx.h" +#include "WpdCapabilities.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_MANAGEMENT + &WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS, + &WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY, + &WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_AND_DATA, + &WPD_COMMAND_OBJECT_MANAGEMENT_WRITE_OBJECT_DATA, + &WPD_COMMAND_OBJECT_MANAGEMENT_COMMIT_OBJECT, + &WPD_COMMAND_OBJECT_MANAGEMENT_REVERT_OBJECT, + &WPD_COMMAND_OBJECT_MANAGEMENT_MOVE_OBJECTS, + &WPD_COMMAND_OBJECT_MANAGEMENT_COPY_OBJECTS, + &WPD_COMMAND_OBJECT_MANAGEMENT_UPDATE_OBJECT_WITH_PROPERTIES_AND_DATA, + + // 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_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_OBJECT_RESOURCES + &WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED, + &WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES, + &WPD_COMMAND_OBJECT_RESOURCES_OPEN, + &WPD_COMMAND_OBJECT_RESOURCES_READ, + &WPD_COMMAND_OBJECT_RESOURCES_WRITE, + &WPD_COMMAND_OBJECT_RESOURCES_CLOSE, + &WPD_COMMAND_OBJECT_RESOURCES_DELETE, + &WPD_COMMAND_OBJECT_RESOURCES_SEEK, + + // 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_STORAGE + &WPD_COMMAND_STORAGE_FORMAT, + + // WPD_CATEGORY_NETWORK_CONFIGURATION + &WPD_COMMAND_PROCESS_WIRELESS_PROFILE, +}; + +const GUID* g_SupportedFunctionalCategories[] = +{ + &WPD_FUNCTIONAL_CATEGORY_DEVICE, + &WPD_FUNCTIONAL_CATEGORY_STORAGE, + &WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, + &WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, +}; + +const GUID* g_SupportedEvents[] = +{ + &WPD_EVENT_OBJECT_ADDED, + &WPD_EVENT_OBJECT_REMOVED, + &WPD_EVENT_OBJECT_UPDATED, +}; + +typedef struct _FunctionalCategoryContentTypePair +{ + const GUID* FunctionalCategory; + const GUID* ContentType; +} FunctionalCategoryContentTypePair; + +const FunctionalCategoryContentTypePair g_CategoryContentTypePairs[] = +{ + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_UNSPECIFIED}, + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_FOLDER}, + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_AUDIO}, + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_VIDEO}, + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_IMAGE}, + {&WPD_FUNCTIONAL_CATEGORY_STORAGE, &WPD_CONTENT_TYPE_CONTACT}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_CONTENT_TYPE_NETWORK_ASSOCIATION}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_CONTENT_TYPE_WIRELESS_PROFILE}, +}; + +typedef struct _ContentTypeFormatPair +{ + const GUID* ContentType; + const GUID* Format; +} ContentTypeFormatPair; + +const ContentTypeFormatPair g_ContentTypeFormatPairs[] = +{ + {&WPD_CONTENT_TYPE_UNSPECIFIED, &WPD_OBJECT_FORMAT_UNSPECIFIED}, + {&WPD_CONTENT_TYPE_UNSPECIFIED, &FakeContent_Format}, + {&WPD_CONTENT_TYPE_FOLDER, &FakeContent_Format}, + {&WPD_CONTENT_TYPE_AUDIO, &WPD_OBJECT_FORMAT_WMA}, + {&WPD_CONTENT_TYPE_VIDEO, &WPD_OBJECT_FORMAT_WMV}, + {&WPD_CONTENT_TYPE_IMAGE, &WPD_OBJECT_FORMAT_EXIF}, + {&WPD_CONTENT_TYPE_CONTACT, &WPD_OBJECT_FORMAT_VCARD2}, + {&WPD_CONTENT_TYPE_NETWORK_ASSOCIATION, &WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION}, + {&WPD_CONTENT_TYPE_WIRELESS_PROFILE, &WPD_OBJECT_FORMAT_MICROSOFT_WFC}, +}; + +WpdCapabilities::WpdCapabilities() +{ + +} + +WpdCapabilities::~WpdCapabilities() +{ + +} + +HRESULT WpdCapabilities::Initialize(_In_ FakeDevice *pFakeDevice) +{ + + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + + +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 should be returned as an + * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS. + * That includes custom commands, if any. + * Note that certain commands require a "command target" + * to function correctly (e.g. delete object), and 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) +{ + HRESULT hr = S_OK; + + CComPtr<IPortableDeviceKeyCollection> pCommands; + UNREFERENCED_PARAMETER(pParams); + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pCommands); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (hr == S_OK) + { + // Add the supported commands + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommands); dwIndex++) + { + hr = pCommands->Add(*g_SupportedCommands[dwIndex]); + CHECK_HR(hr, "Failed to add supported command at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + 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; + + if (hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_COMMAND, &Command); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_COMMAND"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + // 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, FALSE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_MANAGEMENT_RECURSIVE_DELETE_SUPPORTED"); + } + else if (IsEqualPropertyKey(WPD_COMMAND_OBJECT_RESOURCES_SEEK, Command)) + { + // This driver supports Seek on resources opened for Read access + hr = pOptions->SetBoolValue(WPD_OPTION_OBJECT_RESOURCES_SEEK_ON_READ_SUPPORTED, TRUE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_RESOURCES_SEEK_ON_READ_SUPPORTED"); + + if (hr == S_OK) + { + // This driver does not support Seek on resources opened for WRITE access + hr = pOptions->SetBoolValue(WPD_OPTION_OBJECT_RESOURCES_SEEK_ON_WRITE_SUPPORTED, FALSE); + CHECK_HR(hr, "Failed to set WPD_OPTION_OBJECT_RESOURCES_SEEK_ON_WRITE_SUPPORTED"); + } + } + else 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"); + } + } + + 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); + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalCategories); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + // Add the supported functional categories + 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 this GUID + pv.vt = VT_CLSID; + pv.puuid = (GUID*) g_SupportedFunctionalCategories[dwIndex]; + + hr = pFunctionalCategories->Add(&pv); + CHECK_HR(hr, "Failed to add supported functional category at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + 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; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"); + } + + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFunctionalObjects); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + + + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = STORAGE1_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add %ws object ID", STORAGE1_OBJECT_ID); + + if (hr == S_OK) + { + pv.pwszVal = STORAGE2_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add %ws object ID", STORAGE2_OBJECT_ID); + } + } + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = RENDERING_INFORMATION_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add %ws object ID", RENDERING_INFORMATION_OBJECT_ID); + } + 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 %ws object ID", WPD_DEVICE_OBJECT_ID); + } + if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.vt = VT_LPWSTR; + pv.pwszVal = NETWORK_CONFIG_OBJECT_ID; + hr = pFunctionalObjects->Add(&pv); + CHECK_HR(hr, "Failed to add %ws object ID", NETWORK_CONFIG_OBJECT_ID); + } + } + + 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; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"); + } + + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pContentTypes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + + for(DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_CategoryContentTypePairs); dwIndex++) + { + pv.vt = VT_CLSID; + if ((*g_CategoryContentTypePairs[dwIndex].FunctionalCategory == guidFunctionalCategory) || + (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL)) + { + pv.puuid = (CLSID*)g_CategoryContentTypePairs[dwIndex].ContentType; + hr = pContentTypes->Add(&pv); + CHECK_HR(hr, "Failed to add content type"); + } + // Don't clear the PropVariant since we don't own the memory for the GUIDs + } + } + + 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; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE, &guidContentType); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE"); + } + + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFormats); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + + for(DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_ContentTypeFormatPairs); dwIndex++) + { + pv.vt = VT_CLSID; + if ((*g_ContentTypeFormatPairs[dwIndex].ContentType == guidContentType) || + (guidContentType == WPD_CONTENT_TYPE_ALL)) + { + // Don't add duplicates. Some formats appear under more than one content type + if(!ExistsInCollection(*g_ContentTypeFormatPairs[dwIndex].Format, pFormats)) + { + pv.puuid = (CLSID*)g_ContentTypeFormatPairs[dwIndex].Format; + hr = pFormats->Add(&pv); + CHECK_HR(hr, "Failed to add Format"); + } + } + // Don't clear the PropVariant since we don't own the memory for the GUIDs + } + } + + 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> pPropertyKeys; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT"); + } + + if (hr == S_OK) + { + hr = AddSupportedProperties(guidObjectFormat, &pPropertyKeys); + CHECK_HR(hr, "Failed to add supported properties for %ws", (LPWSTR)CComBSTR(guidObjectFormat)); + } + + if (hr == S_OK) + { + hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, pPropertyKeys); + 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 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; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT"); + } + + if(hr == S_OK) + { + hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, &key); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + hr = AddFixedPropertyAttributes(guidObjectFormat, key, pAttributes); + CHECK_HR(hr, "Failed to add fixed property attributes for format %ws and key %ws.%d", CComBSTR(guidObjectFormat), CComBSTR(key.fmtid), key.pid); + } + + 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); + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pEvents); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + } + + if (hr == S_OK) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + + pv.vt = VT_CLSID; + // Add the supported events + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedEvents); dwIndex++) + { + pv.puuid = (CLSID*) g_SupportedEvents[dwIndex]; + hr = pEvents->Add(&pv); + CHECK_HR(hr, "Failed to add supported events at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + 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; + + if (hr == S_OK) + { + hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_EVENT, &Event); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_EVENT"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pOptions); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (hr == S_OK) + { + // Check for the events we support + if ((Event == WPD_EVENT_OBJECT_ADDED) || + (Event == WPD_EVENT_OBJECT_REMOVED) || + (Event == WPD_EVENT_OBJECT_UPDATED)) + { + // These events are boradcast events + hr = pOptions->SetBoolValue(WPD_EVENT_OPTION_IS_BROADCAST_EVENT, TRUE); + CHECK_HR(hr, "Failed to set WPD_EVENT_OPTION_IS_BROADCAST_EVENT"); + } + + } + + 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/WpdWudfSampleDriver/WpdCapabilities.h b/wpd/WpdWudfSampleDriver/WpdCapabilities.h new file mode 100644 index 00000000..15a96261 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdCapabilities.h @@ -0,0 +1,60 @@ +#pragma once + +class WpdCapabilities +{ +public: + WpdCapabilities(); + ~WpdCapabilities(); + + HRESULT Initialize( + _In_ FakeDevice *pFakeDevice); + + 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_pFakeDevice; +}; + diff --git a/wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp b/wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp new file mode 100644 index 00000000..c09f7c25 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "WpdNetworkConfig.tmh" + +WpdNetworkConfig::WpdNetworkConfig() +{ + +} + +WpdNetworkConfig::~WpdNetworkConfig() +{ + +} + +HRESULT WpdNetworkConfig::Initialize(_In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + + +HRESULT WpdNetworkConfig::DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_NETWORK_CONFIGURATION) + { + 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_PROCESS_WIRELESS_PROFILE)) + { + hr = OnProcessWFCObject(pParams, pResults); + CHECK_HR(hr, "Failed to commit WFC file"); + } + 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_PROCESS_WIRELESS_PROFILE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_ID: identifies the object to process. + * + * The driver should: + * - + */ +HRESULT WpdNetworkConfig::OnProcessWFCObject( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + + UNREFERENCED_PARAMETER(pResults); + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID); + if (hr == S_OK) + { + FakeContent* pElement = NULL; + + // Check if the object exists + if(m_pFakeDevice->GetContent(pszObjectID, &pElement)) + { + // Check if the object is of the proper content type + if (IsEqualGUID(pElement->ContentType, WPD_CONTENT_TYPE_WIRELESS_PROFILE)) + { + BYTE Buffer[16]; + DWORD dwNumBytesRead = 0; + + // Perform minimal validation on the object contents (just read some bytes for this sample) + hr = pElement->ReadData(WPD_RESOURCE_DEFAULT, 0, Buffer, sizeof(Buffer), &dwNumBytesRead); + CHECK_HR(hr, "Failed to read resource data for %ws.%d", CComBSTR(WPD_RESOURCE_DEFAULT.fmtid), WPD_RESOURCE_DEFAULT.pid); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID for OnProcessWFCObject [%ws]", pszObjectID); + } + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID); + } + + CoTaskMemFree(pszObjectID); + } + else + { + CHECK_HR(hr, "Missing or invalid value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + return hr; +} + + diff --git a/wpd/WpdWudfSampleDriver/WpdNetworkConfig.h b/wpd/WpdWudfSampleDriver/WpdNetworkConfig.h new file mode 100644 index 00000000..9aa94ea9 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdNetworkConfig.h @@ -0,0 +1,21 @@ +#pragma once + +class WpdNetworkConfig +{ +public: + WpdNetworkConfig(); + ~WpdNetworkConfig(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + + HRESULT OnProcessWFCObject(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); +private: + FakeDevice* m_pFakeDevice; +}; + diff --git a/wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp b/wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp new file mode 100644 index 00000000..d67e6c5d --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp @@ -0,0 +1,386 @@ +#include "stdafx.h" +#include "WpdObjectEnum.tmh" + +WpdObjectEnumerator::WpdObjectEnumerator() +{ + +} + +WpdObjectEnumerator::~WpdObjectEnumerator() +{ + +} + +HRESULT WpdObjectEnumerator::Initialize(_In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + m_pFakeDevice = pFakeDevice; + + return hr; +} + +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. + * - Return an identifier for the context in WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT. + * + */ +HRESULT WpdObjectEnumerator::OnStartFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszParentID = NULL; + LPWSTR pszEnumContext = NULL; + IUnknown* pContextMap = NULL; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID, &pszParentID); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID"); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, &pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + if (SUCCEEDED(hr)) + { + hr = CreateEnumContext((ContextMap*)pContextMap, pszParentID, &pszEnumContext); + CHECK_HR(hr, "Failed to create enumeration context"); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, pszEnumContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszParentID); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszEnumContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectEnumerator::OnFindNext(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszEnumContext = NULL; + DWORD dwNumObjects = 0; + ContextMap* pContextMap = NULL; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDCollection; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &pszEnumContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED, &dwNumObjects); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED"); + } + } + + // 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)) + { + // Find the next objects in the enumeration + hr = GetObjectIDs(pContextMap, dwNumObjects, pszEnumContext, &pObjectIDCollection); + CHECK_HR(hr, "Failed to get the objectIDs"); + } + + 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 (SUCCEEDED(hr)) + { + if(pObjectIDCollection != NULL) + { + ULONG ulCount = 0; + + pObjectIDCollection->GetCount(&ulCount); + if(ulCount < dwNumObjects) + { + hr = S_FALSE; + } + } + } + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszEnumContext); + + SAFE_RELEASE(pContextMap); + + 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 resources associated with this context. + */ +HRESULT WpdObjectEnumerator::OnEndFind(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszEnumContext = NULL; + IUnknown* pContextMap = NULL; + + UNREFERENCED_PARAMETER(pResults); + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &pszEnumContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT"); + } + + if (SUCCEEDED(hr)) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, &pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + if (SUCCEEDED(hr)) + { + hr = DestroyEnumContext((ContextMap*) pContextMap, pszEnumContext); + CHECK_HR(hr, "Failed to destroy enumeration context"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszEnumContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectEnumerator::CreateEnumContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszParentID, + _Outptr_result_nullonfailure_ LPWSTR* ppszEnumContext) +{ + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + EnumContext* pContext = NULL; + + if((pContextMap == NULL) || + (pszParentID == NULL) || + (ppszEnumContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszEnumContext = NULL; + + hr = CoCreateGuid(&guidContext); + if (SUCCEEDED(hr)) + { + bstrContext = guidContext; + if(bstrContext.Length() == 0) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create BSTR from GUID"); + } + } + + if (SUCCEEDED(hr)) + { + pContext = new EnumContext(); + if(pContext != NULL) + { + CAtlStringW strKey = bstrContext; + pContext->ParentID = pszParentID; + + hr = pContextMap->Add(strKey, pContext); + CHECK_HR(hr, "Failed to add enumeration context to client context map"); + + pContext->Release(); + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate enumeration context"); + } + } + + if (SUCCEEDED(hr)) + { + *ppszEnumContext = AtlAllocTaskWideString(bstrContext); + } + + return hr; +} + +HRESULT WpdObjectEnumerator::DestroyEnumContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszEnumContext) +{ + HRESULT hr = S_OK; + + CAtlStringW strKey = pszEnumContext; + pContextMap->Remove(strKey); + + return hr; +} + +#pragma warning(suppress: 6388) // PREFast bug means here there is a false positive for the call to CComPtr<>::QueryInterface(). +HRESULT WpdObjectEnumerator::GetObjectIDs( + _In_ ContextMap* pContextMap, + _In_ const DWORD dwNumObjects, + _In_ LPCWSTR pszEnumContext, + _COM_Outptr_ IPortableDevicePropVariantCollection** ppCollection) +{ + HRESULT hr = S_OK; + EnumContext* pContext = NULL; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + + if(ppCollection == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL collection parameter"); + return hr; + } + + *ppCollection = NULL; + + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&pObjectIDs)); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection"); + + if (SUCCEEDED(hr)) + { + CAtlStringW strKey = pszEnumContext; + pContext = (EnumContext*) pContextMap->GetContext(strKey); + if(pContext != NULL) + { + for(DWORD dwCount = 0; dwCount < dwNumObjects; dwCount++) + { + CAtlStringW strObjectID; + + DWORD dwNextStartIndex = 0; + if(m_pFakeDevice->FindNext(pContext->SearchIndex, + pContext->ParentID, + strObjectID, + &dwNextStartIndex)) + { + PropVariantWrapper pvObjectID(strObjectID); + + hr = pObjectIDs->Add(&pvObjectID); + CHECK_HR(hr, "Failed to add object [%ws]", pvObjectID.pwszVal); + pContext->SearchIndex = dwNextStartIndex; + } + } + + pContext->Release(); + } + else + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Cannot find enum context [%ws]", pszEnumContext); + } + } + + if (SUCCEEDED(hr)) + { + hr = pObjectIDs->QueryInterface(IID_PPV_ARGS(ppCollection)); + CHECK_HR(hr, "Failed to QI for IID_IPortableDevicePropVariantCollection on IPortableDevicePropVariantCollection"); + } + return hr; +} + diff --git a/wpd/WpdWudfSampleDriver/WpdObjectEnum.h b/wpd/WpdWudfSampleDriver/WpdObjectEnum.h new file mode 100644 index 00000000..a7cc4c02 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectEnum.h @@ -0,0 +1,105 @@ +#pragma once + +// This class is used to store the context for a specific enumeration. +// Currrently, this is done by storing the object index. +class EnumContext : public IUnknown +{ +public: + EnumContext() : + m_cRef(1), + SearchIndex(0) + { + + } + + ~EnumContext() + { + + } + +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: + DWORD SearchIndex; + CAtlStringW ParentID; +private: + DWORD m_cRef; +}; + +class WpdObjectEnumerator +{ +public: + WpdObjectEnumerator(); + ~WpdObjectEnumerator(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + 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: + HRESULT CreateEnumContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszParentID, + _Outptr_result_nullonfailure_ LPWSTR* ppszEnumContext); + + HRESULT DestroyEnumContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszEnumContext); + + HRESULT GetObjectIDs( + _In_ ContextMap* pContextMap, + _In_ const DWORD dwNumObjects, + _In_ LPCWSTR pszEnumContext, + _COM_Outptr_ IPortableDevicePropVariantCollection** ppCollection); + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp b/wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp new file mode 100644 index 00000000..9c3d22b2 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp @@ -0,0 +1,1160 @@ +#include "stdafx.h" +#include "WpdObjectManagement.tmh" + +WpdObjectManagement::WpdObjectManagement() +{ + +} + +WpdObjectManagement::~WpdObjectManagement() +{ + +} + +HRESULT WpdObjectManagement::Initialize(_In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + m_pFakeDevice = pFakeDevice; + + return hr; +} + +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_DELETE_OBJECTS.pid) + { + hr = OnDelete(pParams, pResults); + CHECK_HR(hr, "Failed to delete object"); + } + else 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_CREATE_OBJECT_WITH_PROPERTIES_AND_DATA.pid) + { + hr = OnCreateObjectWithPropertiesAndData(pParams, pResults); + CHECK_HR(hr, "Failed to create object"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_WRITE_OBJECT_DATA.pid) + { + hr = OnWriteObjectData(pParams, pResults); + CHECK_HR(hr, "Failed to write object data"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_COMMIT_OBJECT.pid) + { + hr = OnCommit(pParams, pResults); + CHECK_HR(hr, "Failed to commit object"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_REVERT_OBJECT.pid) + { + hr = OnRevert(pParams, pResults); + CHECK_HR(hr, "Failed to revert object"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_MOVE_OBJECTS.pid) + { + hr = OnMove(pParams, pResults); + CHECK_HR(hr, "Failed to Move objects"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_COPY_OBJECTS.pid) + { + hr = OnCopy(pParams, pResults); + CHECK_HR(hr, "Failed to Copy objects"); + } + else if (Command.pid == WPD_COMMAND_OBJECT_MANAGEMENT_UPDATE_OBJECT_WITH_PROPERTIES_AND_DATA.pid) + { + hr = OnUpdateObjectWithPropertiesAndData(pParams, pResults); + CHECK_HR(hr, "Failed to update 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_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; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + CComPtr<IPortableDevicePropVariantCollection> pDeleteResults; + CComPtr<IPortableDeviceValues> pEventParams; + VARTYPE vt = VT_EMPTY; + + 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) + { + 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) + { + 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) + { + pEventParams = NULL; + // Get the object properties used for sending the event. Ignore errors (these are expected in some cases + // e.g. app is sending object ID that doesn't exist), since this relates to the event, not the delete operation, + // and we return results for the delete (an error posting the event is non-fatal). + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pv.pwszVal, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pv.pwszVal); + + HRESULT hrDelete = S_OK; + PROPVARIANT pvResult = {0}; + + PropVariantInit(&pvResult); + + hrDelete = m_pFakeDevice->DeleteObject(dwOptions, pv.pwszVal); + CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal); + + if(FAILED(hrDelete)) + { + bDeleteFailed = TRUE; + } + + // 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); + + if ((hrDelete == S_OK) && (hrTemp == S_OK)) + { + HRESULT hrEvent = S_OK; + // Set the event-specific parameters + hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_REMOVED); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + // Send the Event + if (hrEvent == S_OK) + { + PostWpdEvent(pParams, pEventParams); + } + } + 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; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_MOVE_OBJECTS + * command. + * + * This command will move the specified objects to the destination folder. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS : the ObjectIDs, indicating which objects to move. These may + * be folder objects. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID: Indicates the destination folder for the move operation. + * + * The driver should: + * - Attempt to Move the object specified in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS to the folder specified in + * WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID. + * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_MOVE_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_MOVE_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 moved successfully. + * - S_FALSE if any object move failed. + * - An error return indicates that the driver did not move any objects, and + * WPD_PROPERTY_OBJECT_MANAGEMENT_MOVE_RESULTS is ignored. + */ +HRESULT WpdObjectManagement::OnMove( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszDestFolderObjectID = NULL; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + CComPtr<IPortableDevicePropVariantCollection> pMoveResults; + BOOL bMoveFailed = FALSE; + VARTYPE vt = VT_EMPTY; + + 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->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID, &pszDestFolderObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID"); + } + + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pMoveResults); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); + } + + + if (hr == S_OK) + { + DWORD cObjects = 0; + // Loop through the object list and attempt to move + hr = pObjectIDs->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objects to move"); + + if (hr == S_OK) + { + for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) + { + HRESULT hrTemp = S_OK; + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + // Get the next Object to move + hrTemp = pObjectIDs->GetAt(dwIndex, &pv); + CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); + if (hrTemp == S_OK) + { + // Move this object + hrTemp = m_pFakeDevice->MoveObject(pv.pwszVal, pszDestFolderObjectID); + CHECK_HR(hrTemp, "Failed to move object [%ws] to folder [%ws]", pv.pwszVal, pszDestFolderObjectID); + + if(FAILED(hrTemp)) + { + bMoveFailed = TRUE; + } + + PROPVARIANT pvResult = {0}; + + PropVariantInit(&pvResult); + + // Save this result + pvResult.vt = VT_ERROR; + pvResult.scode = hrTemp; + hrTemp = pMoveResults->Add(&pvResult); + CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); + + PropVariantClear(&pvResult); + } + PropVariantClear(&pv); + } + } + } + + // Set the results + if (hr == S_OK) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_MOVE_RESULTS, pMoveResults); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_MOVE_RESULTS"); + } + + CoTaskMemFree(pszDestFolderObjectID); + + // If an object failed to move, make sure we return S_FALSE + if ((hr == S_OK) && (bMoveFailed)) + { + hr = S_FALSE; + } + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COPY_OBJECTS + * command. + * + * This command will copy the specified objects to the destination folder. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS : the ObjectIDs, indicating which objects to copy. These may + * be folder objects. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID: Indicates the destination folder for the copy operation. + * + * The driver should: + * - Attempt to copy the object specified in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS to the folder specified in + * WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID. + * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_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_COPY_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 copied successfully. + * - S_FALSE if any object copy failed. + * - An error return indicates that the driver did not copy any objects, and + * WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS is ignored. + */ +HRESULT WpdObjectManagement::OnCopy( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszDestFolderObjectID = NULL; + CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; + CComPtr<IPortableDevicePropVariantCollection> pCopyResults; + BOOL bCopyFailed = FALSE; + VARTYPE vt = VT_EMPTY; + + 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 (SUCCEEDED(hr)) + { + 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 (SUCCEEDED(hr)) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID, &pszDestFolderObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID"); + } + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pCopyResults); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); + } + + + if (SUCCEEDED(hr)) + { + DWORD cObjects = 0; + // Loop through the object list and attempt to copy + hr = pObjectIDs->GetCount(&cObjects); + CHECK_HR(hr, "Failed to get number of objects to copy"); + + if (SUCCEEDED(hr)) + { + for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) + { + CComPtr<IPortableDeviceValues> pEventParams; + LPWSTR pszNewObjectID = NULL; + HRESULT hrTemp = S_OK; + PROPVARIANT pv = {0}; + + PropVariantInit(&pv); + // Get the next Object to copy + hrTemp = pObjectIDs->GetAt(dwIndex, &pv); + CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); + if (SUCCEEDED(hrTemp)) + { + // Copy this object + hrTemp = m_pFakeDevice->CopyObject(pv.pwszVal, pszDestFolderObjectID, &pszNewObjectID); + CHECK_HR(hrTemp, "Failed to copy object [%ws] to folder [%ws]", pv.pwszVal, pszDestFolderObjectID); + + if(FAILED(hrTemp)) + { + bCopyFailed = TRUE; + } + + PROPVARIANT pvResult = {0}; + + PropVariantInit(&pvResult); + + // Save this result + pvResult.vt = VT_ERROR; + pvResult.scode = hrTemp; + hrTemp = pCopyResults->Add(&pvResult); + CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); + + PropVariantClear(&pvResult); + + if (SUCCEEDED(hrTemp)) + { + // Send the event + // Get the object properties used for sending the event + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszNewObjectID, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszNewObjectID); + if (SUCCEEDED(hrTemp)) + { + // Set the event-specific parameters + hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); + CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + // Send the Event + if (SUCCEEDED(hrTemp)) + { + PostWpdEvent(pParams, pEventParams); + } + } + } + } + + if (pszNewObjectID != NULL) + { + CoTaskMemFree(pszNewObjectID); + pszNewObjectID = NULL; + } + + PropVariantClear(&pv); + } + } + } + + // Set the results + if (SUCCEEDED(hr)) + { + hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS, pCopyResults); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS"); + } + + CoTaskMemFree(pszDestFolderObjectID); + + // If an object failed to copy, make sure we return S_FALSE + if (SUCCEEDED(hr) && bCopyFailed) + { + hr = S_FALSE; + } + + 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 pszContext = NULL; + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceValues> pEventParams; + + // Get the Object Properties + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pValues); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); + + // Save the object to the device here. + if (SUCCEEDED(hr)) + { + hr = m_pFakeDevice->SaveNewObject(pValues, &pszContext); + CHECK_HR(hr, "Failed to save new (properties only) object to device"); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + } + + if (SUCCEEDED(hr)) + { + // Send the event + HRESULT hrTemp = S_OK; + // Get the object properties used for sending the event + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszContext, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszContext); + if (SUCCEEDED(hrTemp)) + { + // Set the event-specific parameters + hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); + CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + // Send the Event + if (SUCCEEDED(hrTemp)) + { + PostWpdEvent(pParams, pEventParams); + } + } + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_AND_DATA + * 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). + * - WPD_OBJECT_SIZE: The total size of the object data stream. + * - WPD_OBJECT_FORMAT: The format the object data stream. + * + * The driver should: + * - Create a context for this operation and return it in WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT. + * - Return the optimal transfer buffer size in WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE. + */ +HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesAndData( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + LPWSTR pszObjectName = NULL; + IUnknown* pContextMap = NULL; + CComPtr<IPortableDeviceValues> pValues; + + // Get the Object Properties + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pValues); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); + + // Get Object Name from pValues + if (SUCCEEDED(hr)) + { + hr = pValues->GetStringValue(WPD_OBJECT_NAME, &pszObjectName); + CHECK_HR(hr, "Failed to get WPD_OBJECT_NAME"); + } + + // Get the context map + if (SUCCEEDED(hr)) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, &pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + if (SUCCEEDED(hr)) + { + // Object data will be provided by the caller. So Create a context identifying this + // creation request, since the actual data will come later, and the object can only be saved + // when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COMMIT_OBJECT call. + hr = CreateObjectManagementContext((ContextMap*) pContextMap, pszObjectName, pValues, false /*bUpdateRequest*/, &pszContext); + CHECK_HR(hr, "Failed to create object management context"); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT, pszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT"); + } + + // Set the optimal buffer size + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE value"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszObjectName); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_REVERT_OBJECT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT: identifies the object creation request (driver returned this in + * WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT). + * + * The driver should: + * - Free any resources associated with this context/operation. The object should not be saved to the + * device. If a placeholder was created on the device for this operation, it should be removed. + */ +HRESULT WpdObjectManagement::OnRevert( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + ContextMap* pContextMap = NULL; + + // There are no return parameters for this operation. + UNREFERENCED_PARAMETER(pResults); + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT, &pszContext); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + + 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 = DestroyObjectManagementContext(pContextMap, pszContext); + CHECK_HR(hr, "Failed to revert object identified by context [%ws]", pszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COMMIT_OBJECT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT: identifies the object creation request (driver returned this in + * WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT). + * + * The driver should: + * - Commit the object to the device. + * - Return the new/updated ObjectID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID. + */ +HRESULT WpdObjectManagement::OnCommit( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + ObjectManagementContext* pContext = NULL; + ContextMap* pContextMap = NULL; + CComPtr<IPortableDeviceValues> pEventParams; + + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT, &pszContext); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + + // Get the object management context for this request + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pszContext, (IUnknown**)&pContext); + CHECK_HR(hr, "Failed to get Object Management Context"); + } + + 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)) + { + if (pContext->UpdateRequest) + { + // Update the object + hr = UpdateObject(pContext, pParams, pResults); + } + else + { + // Save the new object + hr = CommitNewObject(pContext, pszContext, pParams, pResults); + } + } + + // We're done with the context + SAFE_RELEASE(pContext); + + // Destroy the context associated with this request, since it is no longer needed + if (SUCCEEDED(hr)) + { + hr = DestroyObjectManagementContext(pContextMap, pszContext); + CHECK_HR(hr, "Failed to destroy object identified by context [%ws]", pszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_WRITE_OBJECT_DATA + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT: identifies the object creation request previsouly started with + * WPD_PROPERTY_OBJECT_MANAGEMENT_CREATE_OBJECT. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_TO_WRITE: specifies the next number of bytes to write. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_DATA: specifies byte array where the data should be copied from. + * + * The driver should: + * - Write the next WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_TO_WRITE to the resource. + * - Return the number of bytes actually written in WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_WRITTEN. + * It is normally considered an error if this value does not match WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_TO_WRITE. + */ +HRESULT WpdObjectManagement::OnWriteObjectData( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + DWORD dwNumBytesToWrite = 0; + DWORD dwNumBytesWritten = 0; + BYTE* pBuffer = NULL; + DWORD cbBuffer = 0; + ObjectManagementContext* pContext = NULL; + + // Get the Context string + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT, &pszContext); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT"); + + // Get the number of bytes to write + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_TO_WRITE, &dwNumBytesToWrite); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_TO_WRITE"); + } + + // Get the source buffer + if (SUCCEEDED(hr)) + { + hr = pParams->GetBufferValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DATA, &pBuffer, &cbBuffer); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DATA"); + } + + // Get the resource context for this object creation request + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pszContext, (IUnknown**)&pContext); + CHECK_HR(hr, "Failed to get Object Management Context"); + } + + // Write the next band of data for this new object request + if (SUCCEEDED(hr)) + { + // TBD: Write bytes to object. This sample driver ignores the data content. + dwNumBytesWritten = dwNumBytesToWrite; + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_WRITTEN, dwNumBytesWritten); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_NUM_BYTES_WRITTEN"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pBuffer); + + SAFE_RELEASE(pContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_UPDATE_OBJECT_WITH_PROPERTIES_AND_DATA + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID: Identifies the object to update. + * - WPD_PROPERTY_OBJECT_MANAGEMENT_UPDATE_PROPERTIES: Contains an IPortableDeviceValues, describing + * the updated properties of the object. At the very least, it will contain: + * - WPD_OBJECT_SIZE: The total size of the object data stream. + * + * The driver should: + * - Create a context for this operation and return it in WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT. + * - Return the optimal transfer buffer size in WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE. + */ +HRESULT WpdObjectManagement::OnUpdateObjectWithPropertiesAndData( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + LPWSTR pszObjectId = NULL; + IUnknown* pContextMap = NULL; + BOOL bUpdatable = NULL; + CComPtr<IPortableDeviceValues> pValues; + + // Get the Object Identifier. + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, &pszObjectId); + CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + + // Get the Object Properties + if (SUCCEEDED(hr)) + { + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_UPDATE_PROPERTIES, &pValues); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); + } + + // Get the context map + if (SUCCEEDED(hr)) + { + hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, &pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Validate whether this object supports the default resource + if (SUCCEEDED(hr)) + { + hr = m_pFakeDevice->SupportsResource(pszObjectId, WPD_RESOURCE_DEFAULT, &bUpdatable); + CHECK_HR(hr, "Failed to check whether object supports WPD_RESOURCE_DEFAULT"); + + if (SUCCEEDED(hr) && !bUpdatable) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support WPD_RESOURCE_DEFAULT"); + } + } + + if (SUCCEEDED(hr)) + { + // Object data will be provided by the caller. So Create a context identifying this + // creation request, since the actual data will come later, and the object can only be saved + // when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COMMIT_OBJECT call. + hr = CreateObjectManagementContext((ContextMap*) pContextMap, pszObjectId, pValues, true /*bUpdateRequest*/, &pszContext); + CHECK_HR(hr, "Failed to create object management context"); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT, pszContext); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_CONTEXT"); + } + + // Set the optimal buffer size + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OPTIMAL_TRANSFER_BUFFER_SIZE value"); + } + + CoTaskMemFree(pszContext); + CoTaskMemFree(pszObjectId); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +HRESULT WpdObjectManagement::CreateObjectManagementContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectName, + _In_ IPortableDeviceValues* pObjectProperties, + _In_ BOOL bUpdateRequest, + _Outptr_result_nullonfailure_ LPWSTR* ppszObjectManagementContext) +{ + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + ObjectManagementContext* pContext = NULL; + CComBSTR bstrContext; + + if((pContextMap == NULL) || + (pObjectProperties == NULL) || + (pszObjectName == NULL) || + (ppszObjectManagementContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszObjectManagementContext = NULL; + + hr = CoCreateGuid(&guidContext); + if (SUCCEEDED(hr)) + { + bstrContext = guidContext; + if(bstrContext.Length() == 0) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create BSTR from GUID"); + } + } + + if (SUCCEEDED(hr)) + { + pContext = new ObjectManagementContext(); + if(pContext != NULL) + { + CAtlStringW strKey = bstrContext; + pContext->Name = pszObjectName; + pContext->ObjectProperties = pObjectProperties; + pContext->UpdateRequest = bUpdateRequest; + + hr = pContextMap->Add(strKey, pContext); + CHECK_HR(hr, "Failed to add object creation context to client context map"); + + pContext->Release(); + } + else + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate object creation context"); + } + } + + if (SUCCEEDED(hr)) + { + *ppszObjectManagementContext = AtlAllocTaskWideString(bstrContext); + } + + return hr; +} + +HRESULT WpdObjectManagement::DestroyObjectManagementContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectManagementContext) +{ + HRESULT hr = S_OK; + + CAtlStringW strKey = pszObjectManagementContext; + pContextMap->Remove(strKey); + + return hr; +} + +HRESULT WpdObjectManagement::CommitNewObject( + _In_ ObjectManagementContext* pContext, + _In_ LPCWSTR pszContext, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + LPWSTR pszObjectID = NULL; + + // Save the new object + HRESULT hr = m_pFakeDevice->SaveNewObject(pContext->ObjectProperties, &pszObjectID); + CHECK_HR(hr, "Failed to save new object [%ws] to device", pContext->Name); + + // Let the caller know the new object's ID. + 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"); + } + + if (SUCCEEDED(hr)) + { + // Send the event + HRESULT hrTemp = S_OK; + CComPtr<IPortableDeviceValues> pEventParams; + + // Get the object properties used for sending the event + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID); + + if (SUCCEEDED(hrTemp)) + { + // Set the event-specific parameters + hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); + CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + hrTemp = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE, pszContext); + CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE"); + + // Send the Event + if (SUCCEEDED(hr)) + { + PostWpdEvent(pParams, pEventParams); + } + } + } + + CoTaskMemFree(pszObjectID); + + return hr; +} + +HRESULT WpdObjectManagement::UpdateObject( + _In_ ObjectManagementContext* pContext, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + // For convenience, the ObjectID was stored in Name for object updates + LPCWSTR pszObjectId = pContext->Name; + + // Update the object + HRESULT hr = m_pFakeDevice->UpdateContentObject(pszObjectId, pContext->ObjectProperties); + CHECK_HR(hr, "Failed to update object [%ws] to device", pszObjectId); + + // Set the object ID. + if (hr == S_OK) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectId); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); + } + + if (hr == S_OK) + { + // Send the event + HRESULT hrTemp = S_OK; + CComPtr<IPortableDeviceValues> pEventParams; + + // Get the object properties used for sending the event + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pContext->Name, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectId); + + if (hrTemp == S_OK) + { + // Set the event-specific parameters + hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED); + CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + // Send the Event + if (hrTemp == S_OK) + { + PostWpdEvent(pParams, pEventParams); + } + } + } + + return hr; +} diff --git a/wpd/WpdWudfSampleDriver/WpdObjectManagement.h b/wpd/WpdWudfSampleDriver/WpdObjectManagement.h new file mode 100644 index 00000000..7fe3a097 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectManagement.h @@ -0,0 +1,131 @@ +#pragma once + +// This class is used to store the context for a specific enumeration. +// Currently, this is done by storing the object index. +class ObjectManagementContext : public IUnknown +{ +public: + ObjectManagementContext() : + m_cRef(1), + UpdateRequest(FALSE) + { + + } + + ~ObjectManagementContext() + { + + } + +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: + CAtlStringW Name; + CComPtr<IPortableDeviceValues> ObjectProperties; + BOOL UpdateRequest; +private: + DWORD m_cRef; +}; + +class WpdObjectManagement +{ +public: + WpdObjectManagement(); + ~WpdObjectManagement(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDelete(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCreateObjectWithPropertiesOnly(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCreateObjectWithPropertiesAndData(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnWriteObjectData(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnRevert(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCommit(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnMove(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCopy(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnUpdateObjectWithPropertiesAndData(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT CreateObjectManagementContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectName, + _In_ IPortableDeviceValues* pObjectProperties, + _In_ BOOL bUpdateRequest, + _Outptr_result_nullonfailure_ LPWSTR* ppszObjectManagementContext); + + HRESULT DestroyObjectManagementContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectManagementContext); + + HRESULT CommitNewObject( + _In_ ObjectManagementContext* pContext, + _In_ LPCWSTR pszContext, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT UpdateObject( + _In_ ObjectManagementContext* pContext, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp b/wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp new file mode 100644 index 00000000..d6e0d5dd --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp @@ -0,0 +1,445 @@ +#include "stdafx.h" +#include "WpdObjectProperties.tmh" + +WpdObjectProperties::WpdObjectProperties() +{ + +} + +WpdObjectProperties::~WpdObjectProperties() +{ + +} + +HRESULT WpdObjectProperties::Initialize(_In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + + +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 = OnGetValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to read properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL)) + { + hr = OnGetAllValues(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to read all properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_SET)) + { + hr = OnWriteProperties(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to write properties"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES)) + { + hr = OnGetAttributes(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get property attributes"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_DELETE)) + { + hr = OnDelete(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 properties we want to return. + * - 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 all properties that match the filter. + */ +HRESULT WpdObjectProperties::OnGetSupportedProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing string value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + // Get the properties to read + if (hr == S_OK) + { + hr = m_pFakeDevice->GetSupportedProperties(pszObjectID, &pKeys); + CHECK_HR(hr, "Failed to get property keys collection"); + } + + 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(pszObjectID); + 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 we want to return. + * - 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::OnGetValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + + CComPtr<IPortableDeviceValues> pValueStore; + CComPtr<IPortableDeviceKeyCollection> pKeys; + + 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"); + } + + 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"); + } + + if (hr == S_OK) + { + hr = m_pFakeDevice->GetValues(wszObjectID, pKeys, &pValueStore); + CHECK_HR(hr, "Failed to read properties on [%ws]", wszObjectID); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValueStore); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + 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 we want to return. + * + * 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::OnGetAllValues( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR wszObjectID = NULL; + + CComPtr<IPortableDeviceValues> pValueStore; + + 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"); + } + + if (hr == S_OK) + { + hr = m_pFakeDevice->GetAllValues(wszObjectID, &pValueStore); + CHECK_HR(hr, "Failed to read all properties on [%ws]", wszObjectID); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValueStore); + CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES")); + + if(FAILED(hrTemp)) + { + hr = hrTemp; + } + } + + CoTaskMemFree(wszObjectID); + wszObjectID = NULL; + + 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::OnWriteProperties( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + CComPtr<IPortableDeviceValues> pValues; + CComPtr<IPortableDeviceValues> pWriteResults; + CComPtr<IPortableDeviceValues> pEventParams; + + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + 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"); + } + + if (hr == S_OK) + { + hr = m_pFakeDevice->WritePropertiesOnObject(pszObjectID, pValues, &pWriteResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to write properties on [%ws]", pszObjectID); + } + } + + // Be sure to preserve possible S_FALSE returns + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + 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; + } + + pEventParams = NULL; + hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams); + CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID); + + if (hrTemp == S_OK) + { + HRESULT hrEvent = S_OK; + // Set the event-specific parameters + hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED); + CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); + + // Send the Event + if (hrEvent == S_OK) + { + PostWpdEvent(pParams, pEventParams); + } + } + } + + // 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_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::OnGetAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + + PROPERTYKEY Key = WPD_PROPERTY_NULL; + CComPtr<IPortableDeviceValues> pAttributeStore; + + if (hr == S_OK) + { + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); + } + + 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"); + } + + if (hr == S_OK) + { + hr = m_pFakeDevice->GetAttributes(pszObjectID, Key, &pAttributeStore); + CHECK_HR(hr, "Failed to get attributes on [%ws]", pszObjectID); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + + hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributeStore); + 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(pszObjectID); + + 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::OnDelete( + _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/WpdWudfSampleDriver/WpdObjectProperties.h b/wpd/WpdWudfSampleDriver/WpdObjectProperties.h new file mode 100644 index 00000000..a63b2021 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectProperties.h @@ -0,0 +1,36 @@ +#pragma once + +class WpdObjectProperties +{ +public: + WpdObjectProperties(); + ~WpdObjectProperties(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedProperties(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetValues(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetAllValues(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnWriteProperties(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetAttributes(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDelete(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.cpp b/wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.cpp new file mode 100644 index 00000000..6eafc6fb --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.cpp @@ -0,0 +1,1027 @@ +#include "stdafx.h" +#include "WpdObjectPropertiesBulk.tmh" + +#define MAX_OBJECTS_TO_RETURN 20 + +WpdObjectPropertiesBulk::WpdObjectPropertiesBulk() +{ + +} + +WpdObjectPropertiesBulk::~WpdObjectPropertiesBulk() +{ + +} + +HRESULT WpdObjectPropertiesBulk::Initialize(_In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + +HRESULT WpdObjectPropertiesBulk::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_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; + hr = CreateBulkPropertiesContext(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)) + { + // If a key list was supplied, get the specified object properties, other get all + // properties. + if(pContext->Properties != NULL) + { + hr = m_pFakeDevice->GetValues(pv.pwszVal, pContext->Properties, &pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + else + { + hr = m_pFakeDevice->GetAllValues(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 guidOjbectFormat = 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, &guidOjbectFormat); + 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; + hr = CreateBulkPropertiesContext(pContextMap, guidOjbectFormat, 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)) + { + // If a key list was supplied, get the specified object properties, other get all + // properties. + if(pContext->Properties != NULL) + { + hr = m_pFakeDevice->GetValues(pv.pwszVal, pContext->Properties, &pValues); + CHECK_HR(hr, "Failed to get property values for [%ws]", pv.pwszVal); + } + else + { + hr = m_pFakeDevice->GetAllValues(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; + hr = CreateBulkPropertiesContext(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<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"); + } + + if (SUCCEEDED(hr)) + { + for (DWORD dwIndex = pContext->NextObject; dwIndex < cObjects; dwIndex++) + { + CComPtr<IPortableDeviceValues> pSetValues; + CComPtr<IPortableDeviceValues> pSetResults; + hr = pContext->ValuesCollection->GetAt(dwIndex, &pSetValues); + CHECK_HR(hr, "Failed to get next values from bulk properties context"); + + if (SUCCEEDED(hr)) + { + LPWSTR pszObjectID = NULL; + + // Get which object this is for + hr = pSetValues->GetStringValue(WPD_OBJECT_ID, &pszObjectID); + if (SUCCEEDED(hr)) + { + // Set the values + hr = m_pFakeDevice->WritePropertiesOnObject(pszObjectID, pSetValues, &pSetResults); + CHECK_HR(hr, "Failed to set proeprties on [%ws]", pszObjectID); + + // Ensure the write results contain which ObjectID this was for + if (SUCCEEDED(hr)) + { + hr = pSetResults->SetStringValue(WPD_OBJECT_ID, pszObjectID); + CHECK_HR(hr, "Failed to set WPD_OBJECT_ID in write resutls"); + } + } + 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_ ContextMap* pContextMap, + _In_ IPortableDevicePropVariantCollection* pObjectIDs, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + BulkPropertiesContext* pContext = NULL; + + if((pContextMap == NULL) || + (pObjectIDs == NULL) || + (ppszBulkPropertiesContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszBulkPropertiesContext = NULL; + + hr = CoCreateGuid(&guidContext); + if (SUCCEEDED(hr)) + { + bstrContext = guidContext; + if(bstrContext.Length() == 0) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create BSTR from GUID"); + } + } + + if (SUCCEEDED(hr)) + { + 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; + + CAtlStringW strKey = bstrContext; + hr = pContextMap->Add(strKey, pContext); // calls AddRef on pContext + CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map"); + } + + if (SUCCEEDED(hr)) + { + *ppszBulkPropertiesContext = AtlAllocTaskWideString(bstrContext); + if (*ppszBulkPropertiesContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate bulk properties context"); + } + } + + SAFE_RELEASE(pContext); + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext( + _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 = m_pFakeDevice->GetObjectIDsByFormat(guidObjectFormat, pszParentObjectID, dwDepth, &pObjectIDs); + CHECK_HR(hr, "Faield to get list of object ids by format"); + + if (SUCCEEDED(hr)) + { + hr = CreateBulkPropertiesContext(pContextMap, pObjectIDs, pProperties, ppszBulkPropertiesContext); + } + + return hr; +} + +HRESULT WpdObjectPropertiesBulk::CreateBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ IPortableDeviceValuesCollection* pValuesCollection, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext) +{ + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + CComBSTR bstrContext; + BulkPropertiesContext* pContext = NULL; + + if((pContextMap == NULL) || + (pValuesCollection == NULL) || + (ppszBulkPropertiesContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszBulkPropertiesContext = NULL; + + hr = CoCreateGuid(&guidContext); + if (SUCCEEDED(hr)) + { + bstrContext = guidContext; + if(bstrContext.Length() == 0) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to create BSTR from GUID"); + } + } + + if (SUCCEEDED(hr)) + { + 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; + + CAtlStringW strKey = bstrContext; + hr = pContextMap->Add(strKey, pContext); + CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map"); + } + + if (SUCCEEDED(hr)) + { + *ppszBulkPropertiesContext = AtlAllocTaskWideString(bstrContext); + 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/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h b/wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h new file mode 100644 index 00000000..dc5cafc6 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h @@ -0,0 +1,134 @@ +#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() : + NextObject(0), + m_cRef(1) + { + + } + + ~BulkPropertiesContext() + { + + } + + 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 *pFakeDevice); + + 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_ ContextMap* pContextMap, + _In_ IPortableDevicePropVariantCollection* pObjectIDs, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT CreateBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ REFGUID guidObjectFormat, + _In_ LPCWSTR pszParentObjectID, + _In_ DWORD dwDepth, + _In_ IPortableDeviceKeyCollection* pProperties, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT CreateBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ IPortableDeviceValuesCollection* pValuesCollection, + _Outptr_result_nullonfailure_ LPWSTR* ppszBulkPropertiesContext); + + HRESULT DestroyBulkPropertiesContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszBulkPropertiesContext); + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp b/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp new file mode 100644 index 00000000..6f7955d5 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp @@ -0,0 +1,944 @@ +#include "stdafx.h" +#include "WpdObjectResources.tmh" + +WpdObjectResources::WpdObjectResources() +{ + +} + +WpdObjectResources::~WpdObjectResources() +{ + +} + +HRESULT WpdObjectResources::Initialize( + _In_ FakeDevice *pFakeDevice) +{ + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + +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_GET_ATTRIBUTES)) + { + hr = OnGetAttributes(pParams, pResults); + if(FAILED(hr)) + { + CHECK_HR(hr, "Failed to get resource attributes"); + } + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_OPEN)) + { + hr = OnOpen(pParams, pResults); + CHECK_HR(hr, "Failed to open resource"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_READ)) + { + hr = OnRead(pParams, pResults); + CHECK_HR(hr, "Failed to read resource data"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_WRITE)) + { + hr = OnWrite(pParams, pResults); + CHECK_HR(hr, "Failed to write resource data"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CLOSE)) + { + hr = OnClose(pParams, pResults); + CHECK_HR(hr, "Failed to close resource"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_DELETE)) + { + hr = OnDelete(pParams, pResults); + CHECK_HR(hr, "Failed to delete resources"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CREATE_RESOURCE)) + { + hr = OnCreate(pParams, pResults); + CHECK_HR(hr, "Failed to create resource"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_REVERT)) + { + hr = OnRevert(pParams, pResults); + CHECK_HR(hr, "Failed to revert resource operation"); + } + else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_SEEK)) + { + hr = OnSeek(pParams, pResults); + CHECK_HR(hr, "Failed resource seek 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_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 pszObjectID = NULL; + + CComPtr<IPortableDeviceKeyCollection> pKeys; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID"); + } + + // Get the collection of resource keys + if (hr == S_OK) + { + hr = m_pFakeDevice->GetSupportedResources(pszObjectID, &pKeys); + CHECK_HR(hr, "Failed to get resource keys collection on [%ws]", pszObjectID); + } + + 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(pszObjectID); + + 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 resource keys containing a single value, + * which is the key identifying the specific resource whose attributes we are requested to return. + * + * The driver should: + * - Return the requested 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 resource attributes. + */ +HRESULT WpdObjectResources::OnGetAttributes( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + PROPERTYKEY Key = {0}; + + CComPtr<IPortableDeviceValues> pAttributeStore; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + 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 = m_pFakeDevice->GetResourceAttributes(pszObjectID, Key, &pAttributeStore); + CHECK_HR(hr, "Failed to get attributes on [%ws]", pszObjectID); + } + + if (SUCCEEDED(hr)) + { + HRESULT hrTemp = S_OK; + + hrTemp = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, pAttributeStore); + 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(pszObjectID); + + 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: identifies the object whose resource we are interested in. + * - WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE: specifies the access requested by the caller. It will + * be either STGM_READ or STGM_WRITE. + * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of resource keys containing a single value, + * which is the key identifying the specific resource the caller is interested in. + * + * The driver should: + * - Create a context associated with this resource. This context will be used by clients when reading/writing + * resource data. Generally, most drivers will also lock the resource here if necessary. + * The context identifier should be a string value returned in WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT. + * - Return the optimal transfer buffer size in WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE. + */ +HRESULT WpdObjectResources::OnOpen( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + PROPERTYKEY Key = {0}; + DWORD dwMode = STGM_READ; + LPWSTR pszContext = NULL; + ContextMap* pContextMap = NULL; + BOOL bSupportsResource = FALSE; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID); + 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 = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap); + CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP"); + } + + // Validate whether this object supports the requested resource + if (SUCCEEDED(hr)) + { + hr = m_pFakeDevice->SupportsResource(pszObjectID, Key, &bSupportsResource); + CHECK_HR(hr, "Failed to check whether object supports resources"); + } + + if (SUCCEEDED(hr) && !bSupportsResource) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Object does not support this resource"); + } + + // Create the context + if (SUCCEEDED(hr)) + { + hr = CreateResourceContext(pContextMap, pszObjectID, Key, FALSE, &pszContext); + CHECK_HR(hr, "Failed to create resource context for %ws.%d on %ws", CComBSTR(Key.fmtid), Key.pid, pszObjectID); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, pszContext); + 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, OPTIMAL_BUFFER_SIZE); + 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(pszObjectID); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + 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: identifies the previsouly opened resource we are going to read from. + * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ: specifies the next number of bytes to read. + * + * The driver should: + * - Read up to the next WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ from the resource. + * - Return the number of bytes actually read in WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ. + */ +HRESULT WpdObjectResources::OnRead( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + DWORD dwNumBytesToRead = 0; + DWORD dwNumBytesRead = 0; + BYTE* pBuffer = NULL; + ResourceContext* pContext = NULL; + + // Get the Context + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext); + 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 context for this transfer + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get resource context"); + } + + // Read the next band of data for this transfer request + if (SUCCEEDED(hr) && pBuffer != NULL) + { + hr = m_pFakeDevice->ReadData(pContext->ObjectID, + pContext->Key, + pContext->NumBytesTransfered, + pBuffer, + dwNumBytesToRead, + &dwNumBytesRead); + CHECK_HR(hr, "Failed to read %d bytes from [%ws] on resource {%ws}.%d", dwNumBytesToRead, pContext->ObjectID, CComBSTR(pContext->Key.fmtid), pContext->Key.pid); + if (SUCCEEDED(hr)) + { + pContext->NumBytesTransfered += dwNumBytesRead; + } + } + + if (SUCCEEDED(hr) && pBuffer != NULL) + { + 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(pszContext); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pBuffer); + + SAFE_RELEASE(pContext); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_WRITE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the previsouly opened resource we are going to write to. + * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE: specifies the next number of bytes to write. + * - WPD_PROPERTY_OBJECT_RESOURCES_DATA: specifies byte array where the data should be copied from. + * + * The driver should: + * - Write the next WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE to the resource. + * - Return the number of bytes actually written in WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN. + * It is normally considered an error if this value does not match WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE. + */ +HRESULT WpdObjectResources::OnWrite( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + DWORD dwNumBytesToWrite = 0; + DWORD dwNumBytesWritten = 0; + BYTE* pBuffer = NULL; + DWORD cbBuffer = 0; + ResourceContext* pContext = NULL; + + // Get the Context string + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // Get the number of bytes to write + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE, &dwNumBytesToWrite); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE"); + } + + // Get the source buffer + if (SUCCEEDED(hr)) + { + hr = pParams->GetBufferValue(WPD_PROPERTY_OBJECT_RESOURCES_DATA, &pBuffer, &cbBuffer); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_DATA"); + } + + // Get the resource context for this transfer + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Faield to get resource context"); + } + + // Write the next band of data for this transfer request + if (SUCCEEDED(hr) && pBuffer != NULL) + { + hr = m_pFakeDevice->WriteData(pContext->ObjectID, + pContext->Key, + pContext->NumBytesTransfered, + pBuffer, + dwNumBytesToWrite, + &dwNumBytesWritten); + CHECK_HR(hr, "Failed to write %d bytes to [%ws] on resource {%ws}.%d", dwNumBytesToWrite, pContext->ObjectID, CComBSTR(pContext->Key.fmtid), pContext->Key.pid); + if (SUCCEEDED(hr)) + { + pContext->NumBytesTransfered += dwNumBytesWritten; + } + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN, dwNumBytesWritten); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pBuffer); + + SAFE_RELEASE(pContext); + + 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: identifies the resource context associated with a + * previously opened resource. + * + * The driver should: + * - Unlock the resource if necessary, and release any system and device resources associated with this WPD object resource. + */ +HRESULT WpdObjectResources::OnClose( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + // No results are expected to be returned + UNREFERENCED_PARAMETER(pResults); + + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + ContextMap* pContextMap = NULL; + ResourceContext* pContext = NULL; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // 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 this was a creation request, enable the resource for the content + if (SUCCEEDED(hr)) + { + // Get the context for this transfer + hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get resource context"); + } + + if (SUCCEEDED(hr)) + { + if (pContext->CreateRequest == TRUE) + { + hr = m_pFakeDevice->EnableResource(pContext->ObjectID, pContext->Key); + CHECK_HR(hr, "Failed to enable resource on object [%ws]", pContext->ObjectID); + } + } + + //Free the context + SAFE_RELEASE(pContext); + + if (SUCCEEDED(hr)) + { + hr = DestroyResourceContext(pContextMap, pszContext); + CHECK_HR(hr, "Failed to remove resource context [%ws]", pszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_DELETE + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resources should be deleted. + * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of keys indicating which + * resources to delete. + * + * The driver should: + * - Delete the specified resources 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 resource which is not deletable (i.e. + * WPD_RESOURCE_ATTRIBUTE_CAN_DELETE is FALSE for that resource.) + */ +HRESULT WpdObjectResources::OnDelete( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = E_ACCESSDENIED; + + UNREFERENCED_PARAMETER(pParams); + UNREFERENCED_PARAMETER(pResults); + + // This driver has no resources which can be deleted. + return hr; +} + +HRESULT WpdObjectResources::OnCreate( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + LPWSTR pszContext = NULL; + ContextMap* pContextMap = NULL; + PROPERTYKEY ResourceKey = WPD_PROPERTY_NULL; + GUID guidObjectFormat = GUID_NULL; + CComPtr<IPortableDeviceValues> pResourceAttributes; + + // Getthe Resource Attributes + hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, &pResourceAttributes); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES"); + } + + // Get the Resource Key + if (SUCCEEDED(hr)) + { + hr = pResourceAttributes->GetKeyValue(WPD_RESOURCE_ATTRIBUTE_RESOURCE_KEY, &ResourceKey); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_RESOURCE_ATTRIBUTE_RESOURCE_KEY"); + } + } + + if (SUCCEEDED(hr) && !IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_CONTACT_PHOTO)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "CreateResource is only supported on WPD_RESOURCE_CONTACT_PHOTO"); + } + + if (SUCCEEDED(hr)) + { + // Get the Object ID + hr = pResourceAttributes->GetStringValue(WPD_OBJECT_ID, &pszObjectID); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_OBJECT_ID"); + } + } + + if (SUCCEEDED(hr)) + { + hr = m_pFakeDevice->GetContentFormat(pszObjectID, guidObjectFormat); + CHECK_HR(hr, "Failed to get content object format"); + + if (SUCCEEDED(hr) && !IsEqualGUID(guidObjectFormat, WPD_OBJECT_FORMAT_VCARD2)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "CreateResource is only supported on contact objects"); + } + } + + // 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"); + } + + // Create the context + if (SUCCEEDED(hr)) + { + hr = CreateResourceContext(pContextMap, pszObjectID, ResourceKey, TRUE, &pszContext); + CHECK_HR(hr, "Failed to create resource context for %ws.%d on %ws", CComBSTR(ResourceKey.fmtid), ResourceKey.pid, pszObjectID); + } + + if (SUCCEEDED(hr)) + { + hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, pszContext); + 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, OPTIMAL_BUFFER_SIZE); + 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(pszObjectID); + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_REVERT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the resource context associated with a + * previously opened resource. + * + * The driver should: + * - Unlock and remove the resource if necessary, and release any system and device resources associated with this WPD object resource. + */ +HRESULT WpdObjectResources::OnRevert( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + // No results are expected to be returned + UNREFERENCED_PARAMETER(pResults); + + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + ContextMap* pContextMap = NULL; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // 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"); + } + + //Free the context + if (SUCCEEDED(hr)) + { + hr = DestroyResourceContext(pContextMap, pszContext); + CHECK_HR(hr, "Failed to remove resource context [%ws]", pszContext); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContextMap); + + return hr; +} + +/** + * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_SEEK + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the resource context associated with a + * previously opened resource. + * - WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET: Displacement to be added to the location indicated by the WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG parameter. + * - WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG: Specifies the origin of the displacement for the seek operation. Can be one of the following values: + * STREAM_SEEK_SET - Offset is from the beginning of the stream + * STREAM_SEEK_CUR - Offset is from the current position in the stream + * STREAM_SEEK_END - Offset is from the end of the stream + * + * The driver should: + * - Move the seek pointer for the resource to point to the specified position, so that + * subsequent read / write operations occur from the new position. + * - Return the new position as an offset relative to the start of the data stream in WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START. + */ +HRESULT WpdObjectResources::OnSeek( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + // No results are expected to be returned + UNREFERENCED_PARAMETER(pResults); + + HRESULT hr = S_OK; + LPWSTR pszContext = NULL; + DWORD dwOrigin = 0; + LONGLONG lOffset = 0; + + ResourceContext* pContext = NULL; + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext); + if (FAILED(hr)) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT"); + } + + // Get the offset + if (SUCCEEDED(hr)) + { + hr = pParams->GetSignedLargeIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET, &lOffset); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET"); + } + + // Get the origin flags + if (SUCCEEDED(hr)) + { + hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG, &dwOrigin); + CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG"); + } + + // Get the context for this transfer + if (SUCCEEDED(hr)) + { + hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext); + CHECK_HR(hr, "Failed to get resource context"); + } + + // Update the current seek pointer. For this driver, we update the NumBytesTransfered in the context so that subsequent reads/writes start + // at the appropriate place. + if (SUCCEEDED(hr)) + { + ULONG ulSize = 0; + ULONG ulOriginalNumBytesTransfered = pContext->NumBytesTransfered; + CComPtr<IPortableDeviceValues> pResourceAttributes; + + // Get the total size of the resource + hr = m_pFakeDevice->GetResourceAttributes(pContext->ObjectID, pContext->Key, &pResourceAttributes); + CHECK_HR(hr, "Failed to get attributes on [%ws]", pContext->ObjectID); + if (SUCCEEDED(hr)) + { + hr = pResourceAttributes->GetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, &ulSize); + CHECK_HR(hr, "Failed to get WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + + if(dwOrigin == STREAM_SEEK_CUR) + { + pContext->NumBytesTransfered += (LONG) lOffset; + } + else if(dwOrigin == STREAM_SEEK_SET) + { + pContext->NumBytesTransfered = (LONG) lOffset; + } + else + { + pContext->NumBytesTransfered = ulSize + (LONG) lOffset; + } + + // Validate that this is in the correct range + if (SUCCEEDED(hr) && pContext->NumBytesTransfered > ulSize) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Attempting to seek beyond existing data"); + // Restore the seek pointer to its original value + pContext->NumBytesTransfered = ulOriginalNumBytesTransfered; + } + } + + // Set the return value + if (SUCCEEDED(hr)) + { + hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START, pContext->NumBytesTransfered); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START"); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszContext); + + SAFE_RELEASE(pContext); + + return hr; +} + +HRESULT WpdObjectResources::CreateResourceContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey, + _In_ BOOL bCreateRequest, + _Outptr_ LPWSTR* ppszResourceContext) +{ + HRESULT hr = S_OK; + GUID guidContext = GUID_NULL; + ResourceContext* pContext = NULL; + + if((pContextMap == NULL) || + (pszObjectID == NULL) || + (ppszResourceContext == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppszResourceContext = NULL; + + hr = CoCreateGuid(&guidContext); + CHECK_HR(hr, "Failed to CoCreateGuid used for identifying the resource context"); + + if (SUCCEEDED(hr)) + { + pContext = new ResourceContext(); + if(pContext == NULL) + { + hr = E_OUTOFMEMORY; + CHECK_HR(hr, "Failed to allocate new resource context"); + } + } + + if (SUCCEEDED(hr)) + { + pContext->ObjectID = pszObjectID; + pContext->Key = ResourceKey; + pContext->CreateRequest = bCreateRequest; + + CAtlStringW strKey = CComBSTR(guidContext); + hr = pContextMap->Add(strKey, pContext); + CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map"); + } + + if (SUCCEEDED(hr)) + { + hr = StringFromCLSID(guidContext, ppszResourceContext); + CHECK_HR(hr, "Failed to allocate string from GUID for resource context"); + } + + SAFE_RELEASE(pContext); + + return hr; +} + +HRESULT WpdObjectResources::DestroyResourceContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszResourceContext) +{ + HRESULT hr = S_OK; + + if(pszResourceContext == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + CAtlStringW strKey = pszResourceContext; + pContextMap->Remove(strKey); + + return hr; +} + diff --git a/wpd/WpdWudfSampleDriver/WpdObjectResources.h b/wpd/WpdWudfSampleDriver/WpdObjectResources.h new file mode 100644 index 00000000..81894662 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdObjectResources.h @@ -0,0 +1,129 @@ +#pragma once + +// This context is used for managing reads/writes for data transfer. +// It keeps track of the number of bytes read/written for the current transfer. +class ResourceContext : public IUnknown +{ +public: + ResourceContext() : + NumBytesTransfered(0), + m_cRef(1), + CreateRequest(FALSE) + { + + + Key = WPD_PROPERTY_NULL; + } + + ~ResourceContext() + { + + } + + CAtlStringW ObjectID; + PROPERTYKEY Key; + DWORD NumBytesTransfered; + BOOL CreateRequest; + +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 WpdObjectResources +{ +public: + WpdObjectResources(); + ~WpdObjectResources(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetSupportedResources(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnGetAttributes(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnOpen(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnRead(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnWrite(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnClose(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnDelete(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnCreate(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnRevert(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnSeek(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + HRESULT CreateResourceContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszObjectID, + _In_ REFPROPERTYKEY ResourceKey, + _In_ BOOL bCreateRequest, + _Outptr_ LPWSTR* ppszResourceContext); + + HRESULT DestroyResourceContext( + _In_ ContextMap* pContextMap, + _In_ LPCWSTR pszResourceContext); + +private: + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdStorage.cpp b/wpd/WpdWudfSampleDriver/WpdStorage.cpp new file mode 100644 index 00000000..cb4e85c3 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdStorage.cpp @@ -0,0 +1,102 @@ +#include "stdafx.h" +#include "WpdStorage.tmh" + +WpdStorage::WpdStorage() +{ + +} + +WpdStorage::~WpdStorage() +{ + +} + +HRESULT WpdStorage::Initialize(_In_ FakeDevice *pFakeDevice) +{ + + HRESULT hr = S_OK; + + if(pFakeDevice == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + m_pFakeDevice = pFakeDevice; + return hr; +} + + +HRESULT WpdStorage::DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + + HRESULT hr = S_OK; + + if (hr == S_OK) + { + if (Command.fmtid != WPD_CATEGORY_STORAGE) + { + 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_STORAGE_FORMAT)) + { + hr = OnFormat(pParams, pResults); + CHECK_HR(hr, "Failed to format storage"); + } + 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_STORAGE_FORMAT + * command. + * + * The parameters sent to us are: + * - WPD_PROPERTY_STORAGE_OBJECT_ID: identifies the storage object to format. + * + * The driver should: + * - Format the storage identified by WPD_PROPERTY_STORAGE_OBJECT_ID. + */ +HRESULT WpdStorage::OnFormat( + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults) +{ + HRESULT hr = S_OK; + LPWSTR pszObjectID = NULL; + + UNREFERENCED_PARAMETER(pResults); + + // Get the Object ID + hr = pParams->GetStringValue(WPD_PROPERTY_STORAGE_OBJECT_ID, &pszObjectID); + if (hr != S_OK) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Missing string value for WPD_PROPERTY_STORAGE_OBJECT_ID"); + } + + // Format this storage + if (hr == S_OK) + { + hr = m_pFakeDevice->FormatStorage(pszObjectID, pParams); + CHECK_HR(hr, "Failed to process format command on [%ws]", pszObjectID); + } + + // Free the memory. CoTaskMemFree ignores NULLs so no need to check. + CoTaskMemFree(pszObjectID); + + return hr; +} + + diff --git a/wpd/WpdWudfSampleDriver/WpdStorage.h b/wpd/WpdWudfSampleDriver/WpdStorage.h new file mode 100644 index 00000000..a20c8e06 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdStorage.h @@ -0,0 +1,24 @@ +#pragma once + +class WpdStorage +{ +public: + WpdStorage(); + ~WpdStorage(); + + HRESULT Initialize(_In_ FakeDevice *pFakeDevice); + + HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command, + _In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnFormat(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + + HRESULT OnMoveObject(_In_ IPortableDeviceValues* pParams, + _In_ IPortableDeviceValues* pResults); + +private: + + FakeDevice* m_pFakeDevice; +}; diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp new file mode 100644 index 00000000..2728b2e7 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp @@ -0,0 +1,68 @@ +// Implementation of DLL Exports. + +#include "stdafx.h" +#include "resource.h" +#include "WpdWudfSampleDriver.h" + +#include "WpdWudfSampleDriver.tmh" + +HINSTANCE g_hInstance = NULL; + +class CWpdWudfSampleDriverModule : public CAtlDllModuleT< CWpdWudfSampleDriverModule > +{ +public : + DECLARE_LIBID(LIBID_WpdWudfSampleDriverLib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_WpdWudfSampleDriver, "{9FF28171-F2BA-4720-AAE1-92DA54E8BB0E}") +}; + +CWpdWudfSampleDriverModule _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/WpdWudfSampleDriver/WpdWudfSampleDriver.def b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.def new file mode 100644 index 00000000..661dd814 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.def @@ -0,0 +1,9 @@ +; WpdWudfSampleDriver.def : Declares the module parameters. + +LIBRARY "WpdWudfSampleDriver.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl new file mode 100644 index 00000000..4e3f3dd0 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl @@ -0,0 +1,24 @@ + +import "oaidl.idl"; +import "ocidl.idl"; + +import "wudfddi.idl"; + +[ + uuid(780B0652-9672-42FF-9AEA-8AE2AE1AABE0), + version(1.0), + helpstring("WPD Sample Driver for new WUDF 1.0 Type Library") +] +library WpdWudfSampleDriverLib +{ + importlib("stdole2.tlb"); + [ + uuid(4F2FDA86-31DD-4840-A391-7A0F29220208), + helpstring("WpdWudfSampleDriver Class") + ] + coclass WpdWudfSampleDriver + { + [default] interface IDriverEntry; + }; +}; + diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx new file mode 100644 index 00000000..d0b343f0 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx @@ -0,0 +1,82 @@ +; +; WpdWudfSampleDriver.inf +; + +[Version] +Signature="$Windows NT$" +Class=WPD +ClassGuid={EEC5AD98-8080-425f-922A-DABF3DE3F69A} +Provider=%Provider% +CatalogFile=WpdWudfSampleDriver.cat +DriverVer=01/24/2005,1.1.1.1 + +[Manufacturer] +%MSFTWUDF%=Microsoft,NT$ARCH$ + +[Microsoft.NT$ARCH$] +%BasicDeviceName%=Basic_Install,WUDF\Basic + +[SourceDisksFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1 +WpdWudfSampleDriver.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=WpdWudfSampleDriver, WpdWudfSampleDriver_Install +UmdfServiceOrder=WpdWudfSampleDriver + +[CoInstallers_CopyFiles] +WudfUpdate_$UMDFCOINSTALLERVERSION$.dll + +[WpdWudfSampleDriver_Install] +UmdfLibraryVersion=$UMDFVERSION$ +DriverCLSID="{4F2FDA86-31DD-4840-A391-7A0F29220208}" +ServiceBinary=%12%\UMDF\WpdWudfSampleDriver.dll + +[Device_AddReg] +; Enable support for legacy WIA and WMDM applications +HKR,,"EnableLegacySupport",0x10001,3 + +; 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] +WpdWudfSampleDriver.dll + + +; =================== Generic ================================== + +[Strings] +MSFTWUDF="Microsoft Windows Portable Devices" +Provider="Microsoft WPD" +MediaDescription="Windows Portable Device Sample Driver Installation Media" +BasicDeviceName="Windows Portable Device Comprehensive Sample Driver" diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc new file mode 100644 index 00000000..7b337741 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc @@ -0,0 +1,31 @@ +// Microsoft Visual C++ generated resource script. +// +#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 Sample Driver using WUDF" +#define VER_INTERNALNAME_STR "WpdWudfSampleDriver.dll" + +#include <common.ver> + +IDR_WPD_SAMPLEDRIVER_IMAGE DATA_FILE "SampleImage.jpg" +IDR_WPD_SAMPLEDRIVER_IMAGE_THUMBNAIL DATA_FILE "SampleImageThumbnail.jpg" +IDR_WPD_SAMPLEDRIVER_MUSIC DATA_FILE "SampleMusic.wma" +IDR_WPD_SAMPLEDRIVER_DEVICE_ICON DATA_FILE "SampleDeviceIcon.ico" +IDR_WPD_SAMPLEDRIVER_AUDIO_ANNOTATION DATA_FILE "SampleAudioAnnotation.wav" +IDR_WPD_SAMPLEDRIVER_VIDEO DATA_FILE "SampleVideo.wmv" +IDR_WPD_SAMPLEDRIVER_CONTACT_PHOTO DATA_FILE "SampleContactPhoto.png" +IDR_WPD_SAMPLEDRIVER_INTERNAL_STORAGE_ICON DATA_FILE "SampleInternalStorageIcon.ico" +IDR_WPD_SAMPLEDRIVER_EXTERNAL_STORAGE_ICON DATA_FILE "SampleExternalStorageIcon.ico" +IDR_WPD_SAMPLEDRIVER_MEMO DATA_FILE "SampleMemo.txt" +IDR_WPD_SAMPLEDRIVER_MEMO_ICON DATA_FILE "SampleMemoIcon.ico" +IDR_WPD_SAMPLEDRIVER_MEMO_FOLDER_ICON DATA_FILE "SampleMemoFolderIcon.ico" + +1 TYPELIB "WpdWudfSampleDriver.tlb" + +IDR_WpdWudfSampleDriver REGISTRY "WpdWudfSampleDriver.rgs" + + diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs new file mode 100644 index 00000000..42a55b28 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs @@ -0,0 +1,26 @@ +HKCR +{ + WpdWudfSampleDriver.WpdWudfSampleDriver.1 = s 'WpdWudfSampleDriver Class' + { + CLSID = s '{4F2FDA86-31DD-4840-A391-7A0F29220208}' + } + WpdWudfSampleDriver.WpdWudfSampleDriver = s 'WpdWudfSampleDriver Class' + { + CLSID = s '{4F2FDA86-31DD-4840-A391-7A0F29220208}' + CurVer = s 'WpdWudfSampleDriver.WpdWudfSampleDriver.1' + } + NoRemove CLSID + { + ForceRemove {4F2FDA86-31DD-4840-A391-7A0F29220208} = s 'WpdWudfSampleDriver Class' + { + ProgID = s 'WpdWudfSampleDriver.WpdWudfSampleDriver.1' + VersionIndependentProgID = s 'WpdWudfSampleDriver.WpdWudfSampleDriver.1' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Free' + } + 'TypeLib' = s '{780B0652-9672-42FF-9AEA-8AE2AE1AABE0}' + } + } +} + diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln new file mode 100644 index 00000000..58705fa8 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.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}") = "WpdWudfSampleDriver", "WpdWudfSampleDriver.vcxproj", "{1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}" +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 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Debug|Win32.ActiveCfg = Debug|Win32 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Debug|Win32.Build.0 = Debug|Win32 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Release|Win32.ActiveCfg = Release|Win32 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Release|Win32.Build.0 = Release|Win32 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Debug|x64.ActiveCfg = Debug|x64 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Debug|x64.Build.0 = Debug|x64 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Release|x64.ActiveCfg = Release|x64 + {1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj new file mode 100644 index 00000000..8a233df1 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj @@ -0,0 +1,407 @@ +<?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>{1B4BD322-6FB3-46C9-87CD-D0CFED5EC3A2}</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>{ADD979C5-C00B-421A-BFE9-D2F1CEFB4506}</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="WpdWudfSampleDriver.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdStorage.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <ClCompile Include="WpdNetworkConfig.cpp"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <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> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile> + <PreCompiledHeader>Use</PreCompiledHeader> + <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile> + </ClCompile> + <Inf Include="WpdWudfSampleDriver.inx"> + <Architecture>$(InfArch)</Architecture> + <SpecifyArchitecture>true</SpecifyArchitecture> + <CopyOutput>.\$(IntDir)\WpdWudfSampleDriver.inf</CopyOutput> + </Inf> + <OtherWpp Include="WpdWudfSampleDriver.rc; WpdWudfSampleDriver.idl; DeviceObjectFakeContent.h; FakeContactContent.h; FakeContent.h; FakeDevice.h; FakeFolderContent.h; FakeImageContent.h; FakeMemoContent.h; FakeMusicContent.h; FakeVideoContent.h; NetworkConfigFakeContent.h; RenderingInformationFakeContent.h; StorageObjectFakeContent.h"> + <WppEnabled>true</WppEnabled> + <WppDllMacro>true</WppDllMacro> + <WppFileExtensions>.cpp.h.H</WppFileExtensions> + <WppPreserveExtensions>.h.H</WppPreserveExtensions> + <WppScanConfigurationData>stdafx.h</WppScanConfigurationData> + </OtherWpp> + </ItemGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <TargetName>WpdWudfSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <TargetName>WpdWudfSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <TargetName>WpdWudfSampleDriver</TargetName> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <TargetName>WpdWudfSampleDriver</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);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_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);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_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);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_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);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ClCompile> + <ResourceCompile> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_INC_PATH)</AdditionalIncludeDirectories> + </ResourceCompile> + <Midl> + <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(SDK_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>WpdWudfSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Link> + <ModuleDefinitionFile>WpdWudfSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Link> + <ModuleDefinitionFile>WpdWudfSampleDriver.def</ModuleDefinitionFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Link> + <ModuleDefinitionFile>WpdWudfSampleDriver.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="WpdWudfSampleDriver.idl" /> + <ResourceCompile Include="WpdWudfSampleDriver.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/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters new file mode 100644 index 00000000..1e97b94a --- /dev/null +++ b/wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters @@ -0,0 +1,92 @@ +<?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>{86FFE4D5-28B8-426F-95B1-81FDB79E3429}</UniqueIdentifier> + </Filter> + <Filter Include="Header Files"> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + <UniqueIdentifier>{1B74CA7F-AADA-48C5-A07E-613B12552F0A}</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>{32ED42ED-1EA9-4DEC-BC65-D8A230B6FE7B}</UniqueIdentifier> + </Filter> + <Filter Include="Driver Files"> + <Extensions>inf;inv;inx;mof;mc;</Extensions> + <UniqueIdentifier>{86D6608F-9073-44EF-A27B-7885C3084EAB}</UniqueIdentifier> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="Device.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="Driver.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="WpdNetworkConfig.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="WpdStorage.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="WpdWudfSampleDriver.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <Midl Include="WpdWudfSampleDriver.idl"> + <Filter>Source Files</Filter> + </Midl> + <None Include="WpdWudfSampleDriver.def"> + <Filter>Source Files</Filter> + </None> + </ItemGroup> + <ItemGroup> + <FilesToPackage Include=".\Debug\\WpdWudfSampleDriver.inf"> + <Filter>Driver Files</Filter> + </FilesToPackage> + <Inf Include="WpdWudfSampleDriver.inx"> + <Filter>Driver Files</Filter> + </Inf> + </ItemGroup> + <ItemGroup> + <ResourceCompile Include="WpdWudfSampleDriver.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/WpdWudfSampleDriver/helpers.cpp b/wpd/WpdWudfSampleDriver/helpers.cpp new file mode 100644 index 00000000..497ae61c --- /dev/null +++ b/wpd/WpdWudfSampleDriver/helpers.cpp @@ -0,0 +1,1600 @@ +#include "stdafx.h" +#include "helpers.tmh" + +// Define the value for the registered WAVE format this driver uses +#ifndef WAVE_FORMAT_MSAUDIO3 + #define WAVE_FORMAT_MSAUDIO3 0x0162 +#endif + +#define VCARD_FORMAT "BEGIN:VCARD\r\nVERSION:2.1\r\nN:%ws;%ws\r\nFN:%ws\r\nORG:%ws\r\nTITLE:%ws\r\nTEL;HOME;VOICE:%ws\r\nTEL;WORK;VOICE:%ws\r\nTEL;CELL;VOICE:%ws\r\nTEL;WORK;FAX:%ws\r\nADR;HOME;ENCODING=QUOTED-PRINTABLE:;;%ws=0D=0A%ws;%ws;,;%ws;;REV:20051206T185151Z\r\nEND:VCARD\r\n" + +const PROPERTYKEY* g_SupportedPropertiesForFormatAll[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_FOLDER_CONTENT_TYPES_ALLOWED, + &WPD_OBJECT_ORIGINAL_FILE_NAME, + &WPD_OBJECT_NON_CONSUMABLE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeDeviceContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_FUNCTIONAL_OBJECT_CATEGORY, + &WPD_DEVICE_FIRMWARE_VERSION, + &WPD_DEVICE_POWER_LEVEL, + &WPD_DEVICE_POWER_SOURCE, + &WPD_DEVICE_PROTOCOL, + &WPD_DEVICE_MODEL, + &WPD_DEVICE_SERIAL_NUMBER, + &WPD_DEVICE_MANUFACTURER, + &WPD_DEVICE_TYPE, + &WPD_DEVICE_FRIENDLY_NAME, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeStorageContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_FOLDER_CONTENT_TYPES_ALLOWED, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_STORAGE_CAPACITY, + &WPD_STORAGE_FREE_SPACE_IN_BYTES, + &WPD_FUNCTIONAL_OBJECT_CATEGORY, + &WPD_STORAGE_TYPE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeImageContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_MEDIA_HEIGHT, + &WPD_MEDIA_WIDTH, + &WPD_OBJECT_DATE_CREATED, + &WPD_OBJECT_ORIGINAL_FILE_NAME, + &WPD_OBJECT_SIZE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeMusicContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_MEDIA_TITLE, + &WPD_MEDIA_ARTIST, + &WPD_MEDIA_DURATION, + &WPD_OBJECT_SIZE, + &WPD_OBJECT_DATE_AUTHORED, + &WPD_OBJECT_DATE_MODIFIED, + &WPD_MUSIC_ALBUM, + &WPD_MEDIA_GENRE, + &WPD_MUSIC_TRACK, + &WPD_OBJECT_ORIGINAL_FILE_NAME, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeVideoContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_MEDIA_TITLE, + &WPD_MEDIA_DURATION, + &WPD_OBJECT_SIZE, + &WPD_MEDIA_HEIGHT, + &WPD_MEDIA_WIDTH, + &WPD_OBJECT_DATE_AUTHORED, + &WPD_OBJECT_DATE_MODIFIED, + &WPD_OBJECT_ORIGINAL_FILE_NAME, + &WPD_VIDEO_SCAN_TYPE, + &WPD_VIDEO_BITRATE, + &WPD_VIDEO_FOURCC_CODE, + &WPD_OBJECT_GENERATE_THUMBNAIL_FROM_RESOURCE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeContactContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_CONTACT_DISPLAY_NAME, + &WPD_CONTACT_PRIMARY_PHONE, + &WPD_CONTACT_MOBILE_PHONE, + &WPD_CONTACT_BUSINESS_PHONE, + &WPD_OBJECT_ORIGINAL_FILE_NAME, + &WPD_OBJECT_SIZE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForRenderingInformation[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_FUNCTIONAL_OBJECT_CATEGORY, + &WPD_RENDERING_INFORMATION_PROFILES, +}; + +const PROPERTYKEY* g_SupportedPropertiesForNetworkConfiguration[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_FOLDER_CONTENT_TYPES_ALLOWED, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_FUNCTIONAL_OBJECT_CATEGORY, +}; + +const PROPERTYKEY* g_SupportedPropertiesForNetworkAssociation[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, +}; + + +const PROPERTYKEY* g_SupportedPropertiesForMicrosoftWFC[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, +}; + +const PROPERTYKEY* g_SupportedPropertiesForFakeMemoContentFormat[] = +{ + &WPD_OBJECT_ID, + &WPD_OBJECT_PERSISTENT_UNIQUE_ID, + &WPD_OBJECT_PARENT_ID, + &WPD_OBJECT_NAME, + &WPD_OBJECT_CONTENT_TYPE, + &WPD_OBJECT_FORMAT, + &WPD_OBJECT_CAN_DELETE, + &WPD_OBJECT_ISHIDDEN, + &WPD_OBJECT_ISSYSTEM, + &WPD_OBJECT_NON_CONSUMABLE, + &WPD_OBJECT_SIZE, + &WPD_OBJECT_DATE_AUTHORED, + &WPD_OBJECT_DATE_MODIFIED, + &WPD_OBJECT_ORIGINAL_FILE_NAME, +}; + +KeyAndAttributesEntry g_FixedAttributesTable[] = +{ + // Properties for all objects, regardless of format + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_ALL, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for generic objects + {&FakeContent_Format, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_FOLDER_CONTENT_TYPES_ALLOWED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeContent_Format, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for the device object + {&FakeDeviceContent_Format, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_FUNCTIONAL_OBJECT_CATEGORY, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_FIRMWARE_VERSION, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_POWER_LEVEL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_POWER_SOURCE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_PROTOCOL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_MODEL, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_SERIAL_NUMBER, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_MANUFACTURER, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_NETWORK_IDENTIFIER, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_FRIENDLY_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&FakeDeviceContent_Format, &WPD_DEVICE_SYNC_PARTNER, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + // Properties for storage objects + {&FakeStorageContent_Format, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_FOLDER_CONTENT_TYPES_ALLOWED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_STORAGE_CAPACITY, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_STORAGE_FREE_SPACE_IN_BYTES, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_FUNCTIONAL_OBJECT_CATEGORY, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeStorageContent_Format, &WPD_STORAGE_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Image objects + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_MEDIA_HEIGHT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_MEDIA_WIDTH, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_DATE_CREATED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_EXIF, &WPD_OBJECT_SIZE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Music objects + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MEDIA_TITLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MEDIA_ARTIST, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MEDIA_DURATION, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_SIZE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_DATE_AUTHORED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_DATE_MODIFIED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MUSIC_ALBUM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MEDIA_GENRE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_MUSIC_TRACK, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMA, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + // Properties for video objects + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_MEDIA_TITLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_MEDIA_DURATION, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_SIZE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_MEDIA_HEIGHT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_MEDIA_WIDTH, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_DATE_AUTHORED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_DATE_MODIFIED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_VIDEO_SCAN_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_VIDEO_BITRATE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_VIDEO_FOURCC_CODE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_WMV, &WPD_OBJECT_GENERATE_THUMBNAIL_FROM_RESOURCE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for contact objects + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_CONTACT_DISPLAY_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_CONTACT_PRIMARY_PHONE, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_CONTACT_MOBILE_PHONE, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_CONTACT_BUSINESS_PHONE, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_VCARD2, &WPD_OBJECT_SIZE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Rendering Information object + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_FUNCTIONAL_OBJECT_CATEGORY, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION, &WPD_RENDERING_INFORMATION_PROFILES, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Network Configuration object + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_FOLDER_CONTENT_TYPES_ALLOWED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION, &WPD_FUNCTIONAL_OBJECT_CATEGORY, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Network Association object + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION, &WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + // Properties for Microsoft WFC object + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&WPD_OBJECT_FORMAT_MICROSOFT_WFC, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + // Properties for Memo object + {&FakeMemoContent_Format, &WPD_OBJECT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_PERSISTENT_UNIQUE_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_PARENT_ID, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_FORMAT, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_CONTENT_TYPE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_CAN_DELETE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_ISHIDDEN, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_ISSYSTEM, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_NON_CONSUMABLE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_SIZE, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_DATE_AUTHORED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_DATE_MODIFIED, UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast}, + {&FakeMemoContent_Format, &WPD_OBJECT_ORIGINAL_FILE_NAME, UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast}, +}; + +HRESULT AddPropertyKeyArrayToCollection( + _In_reads_(cKeys) const PROPERTYKEY** ppKeys, + const DWORD cKeys, + _In_ IPortableDeviceKeyCollection* pCollection) +{ + + HRESULT hr = S_OK; + + if (hr == S_OK) + { + // Add the keys + for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++) + { + hr = pCollection->Add(*ppKeys[dwIndex]); + CHECK_HR(hr, "Failed to add key at index %d", dwIndex); + if (FAILED(hr)) + { + break; + } + } + } + + return hr; +} + +HRESULT AddFixedAttributesByType( + FakeDevicePropertyAttributesType AttributesType, + _In_ IPortableDeviceValues* pAttributes) +{ + + HRESULT hr = S_OK; + + if(pAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + // Initialize our default values for the static attributes + DWORD dwForm = WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED; + BOOL bCanRead = TRUE; + BOOL bCanWrite = FALSE; + BOOL bCanDelete = FALSE; + BOOL bFastProperty = TRUE; + + // Adjust the attributes for the specific property type if needed + if(AttributesType == UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast) + { + bCanWrite = TRUE; + } + + // Add the static attributes for this property. + if(hr == S_OK) + { + if (hr == S_OK) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, dwForm); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM"); + } + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, bCanRead); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ"); + } + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, bCanWrite); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE"); + } + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, bCanDelete); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE"); + } + if (hr == S_OK) + { + hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, bFastProperty); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY"); + } + } + + return hr; +} + +HRESULT AddFixedPropertyAttributes( + _In_ REFGUID guidObjectFormat, + _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; + } + + if (hr == S_OK) + { + for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_FixedAttributesTable); dwIndex++) + { + if((*g_FixedAttributesTable[dwIndex].pFormat == guidObjectFormat) && + (IsEqualPropertyKey(*g_FixedAttributesTable[dwIndex].pKey, key))) + { + hr = AddFixedAttributesByType(g_FixedAttributesTable[dwIndex].type, pAttributes); + CHECK_HR(hr, "Failed to add fixed attributes for %ws.%d on format %ws", (LPWSTR)CComBSTR(key.fmtid), key.pid, (LPWSTR)CComBSTR(guidObjectFormat)); + break; + } + } + } + + return hr; +} + +HRESULT AddSupportedProperties( + _In_ REFGUID guidObjectFormatOrCategory, + _COM_Outptr_ IPortableDeviceKeyCollection** ppKeys) +{ + HRESULT hr = S_OK; + + if(ppKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + *ppKeys = NULL; + CComPtr<IPortableDeviceKeyCollection> pCollection; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceKeyCollection, + (VOID**) &pCollection); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection"); + } + + if (SUCCEEDED(hr)) + { + hr = AddSupportedProperties(guidObjectFormatOrCategory, pCollection); + CHECK_HR(hr, "Failed to add supported properties"); + } + + if (SUCCEEDED(hr)) + { + hr = pCollection->QueryInterface(IID_PPV_ARGS(ppKeys)); + CHECK_HR(hr, "Failed to QI IPortableDeviceKeyCollection for IPortableDeviceKeyCollection"); + } + + return hr; +} + +HRESULT AddSupportedProperties( + _In_ REFGUID guidObjectFormatOrCategory, + _In_ IPortableDeviceKeyCollection* pKeys) +{ + HRESULT hr = S_OK; + + if(pKeys == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_ALL) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFormatAll, + ARRAYSIZE(g_SupportedPropertiesForFormatAll), + pKeys); + } else if (guidObjectFormatOrCategory == FakeContent_Format) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_EXIF) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeImageContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeImageContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_WMA) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeMusicContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeMusicContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_WMV) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeVideoContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeVideoContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_VCARD2) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeContactContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeContactContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_FUNCTIONAL_CATEGORY_RENDERING_INFORMATION) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForRenderingInformation, + ARRAYSIZE(g_SupportedPropertiesForRenderingInformation), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_FUNCTIONAL_CATEGORY_NETWORK_CONFIGURATION) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForNetworkConfiguration, + ARRAYSIZE(g_SupportedPropertiesForNetworkConfiguration), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_NETWORK_ASSOCIATION) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForNetworkAssociation, + ARRAYSIZE(g_SupportedPropertiesForNetworkAssociation), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_OBJECT_FORMAT_MICROSOFT_WFC) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForMicrosoftWFC, + ARRAYSIZE(g_SupportedPropertiesForMicrosoftWFC), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeStorageContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeStorageContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == WPD_FUNCTIONAL_CATEGORY_DEVICE) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeDeviceContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeDeviceContentFormat), + pKeys); + } + else if (guidObjectFormatOrCategory == FakeMemoContent_Format) + { + hr = AddPropertyKeyArrayToCollection(g_SupportedPropertiesForFakeMemoContentFormat, + ARRAYSIZE(g_SupportedPropertiesForFakeMemoContentFormat), + pKeys); + } + + return hr; +} + +HRESULT GetPreferredAudioProfile( + _COM_Outptr_ IPortableDeviceValues** ppProfile) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pProfile; + + if(ppProfile == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppProfile = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pProfile); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Set the value for WPD_OBJECT_FORMAT to indicate this profile applies to WMA objects + if (SUCCEEDED(hr)) + { + hr = pProfile->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_WMA); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + + // Set the preferred value for WPD_MEDIA_TOTAL_BITRATE + if (SUCCEEDED(hr)) + { + hr = pProfile->SetUnsignedIntegerValue(WPD_MEDIA_TOTAL_BITRATE, 192000); + CHECK_HR(hr, "Failed to set WPD_MEDIA_TOTAL_BITRATE"); + } + + // Set the preferred value for WPD_AUDIO_CHANNEL_COUNT + if (SUCCEEDED(hr)) + { + hr = pProfile->SetUnsignedIntegerValue(WPD_AUDIO_CHANNEL_COUNT, 2); + CHECK_HR(hr, "Failed to set WPD_AUDIO_CHANNEL_COUNT"); + } + + // Set the preferred value for WPD_AUDIO_FORMAT_CODE + if (SUCCEEDED(hr)) + { + hr = pProfile->SetUnsignedIntegerValue(WPD_AUDIO_FORMAT_CODE, WAVE_FORMAT_MSAUDIO3); + CHECK_HR(hr, "Failed to set WPD_AUDIO_FORMAT_CODE"); + } + + // Set the output result + if (SUCCEEDED(hr)) + { + hr = pProfile->QueryInterface(IID_PPV_ARGS(ppProfile)); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues"); + } + + return hr; +} + +HRESULT GetAudioProfile2( + _COM_Outptr_ IPortableDeviceValues** ppProfile) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pProfile; + CComPtr<IPortableDeviceValues> pTotalBitRate; + + if(ppProfile == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppProfile = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pProfile); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pTotalBitRate); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Set the value for WPD_OBJECT_FORMAT to indicate this profile applies to WMA objects + if (SUCCEEDED(hr)) + { + hr = pProfile->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_WMA); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT"); + } + + // Set the value for WPD_MEDIA_TOTAL_BITRATE + if (SUCCEEDED(hr)) + { + // First, set the values for the range which will be contained in pTotalBitRate + hr = pTotalBitRate->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_RANGE); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM for WPD_MEDIA_TOTAL_BITRATE"); + + if (SUCCEEDED(hr)) + { + hr = pTotalBitRate->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MIN, 64000); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MIN for WPD_MEDIA_TOTAL_BITRATE"); + } + if (SUCCEEDED(hr)) + { + hr = pTotalBitRate->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_MAX, 256000); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_MAX for WPD_MEDIA_TOTAL_BITRATE"); + } + if (SUCCEEDED(hr)) + { + hr = pTotalBitRate->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_RANGE_STEP, 1000); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_RANGE_STEP for WPD_MEDIA_TOTAL_BITRATE"); + } + + // Now set the bit rate property to be pTotalBitRate + if (SUCCEEDED(hr)) + { + hr = pProfile->SetIPortableDeviceValuesValue(WPD_MEDIA_TOTAL_BITRATE, pTotalBitRate); + CHECK_HR(hr, "Failed to set WPD_MEDIA_TOTAL_BITRATE"); + } + } + + // Set the value for WPD_AUDIO_CHANNEL_COUNT + if (SUCCEEDED(hr)) + { + hr = pProfile->SetUnsignedIntegerValue(WPD_AUDIO_CHANNEL_COUNT, 2); + CHECK_HR(hr, "Failed to set WPD_AUDIO_CHANNEL_COUNT"); + } + + // Set the value for WPD_AUDIO_FORMAT_CODE + if (SUCCEEDED(hr)) + { + hr = pProfile->SetUnsignedIntegerValue(WPD_AUDIO_FORMAT_CODE, WAVE_FORMAT_MSAUDIO3); + CHECK_HR(hr, "Failed to set WPD_AUDIO_FORMAT_CODE"); + } + + // Set the output result + if (SUCCEEDED(hr)) + { + hr = pProfile->QueryInterface(IID_PPV_ARGS(ppProfile)); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues"); + } + + return hr; +} + +HRESULT GetVideoProfile( + _COM_Outptr_ IPortableDeviceValues** ppProfile) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pProfile; + CComPtr<IPortableDeviceValues> pFourCCCode; + + if(ppProfile == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + *ppProfile = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pProfile); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pFourCCCode); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); + } + + // Set the value for WPD_OBJECT_FORMAT to indicate this profile applies to WMV objects + if (SUCCEEDED(hr)) + { + hr = pProfile->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_WMV); + CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT to WPD_OBJECT_FORMAT_WMV for the rendering profile"); + } + + + // Set the value for WPD_VIDEO_FOURCC_CODE + if (SUCCEEDED(hr)) + { + CComPtr<IPortableDevicePropVariantCollection> pFourCCCodeEnumElements; + + hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDevicePropVariantCollection, + (VOID**) &pFourCCCodeEnumElements); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); + + if (SUCCEEDED(hr)) + { + hr = pFourCCCode->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_ENUMERATION); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM for WPD_VIDEO_FOURCC_CODE"); + } + + if (SUCCEEDED(hr)) + { + // Only 1 sample value is set here, add more as appropriate for your device + PROPVARIANT pvValue; + PropVariantInit(&pvValue); + pvValue.vt = VT_UI4; + pvValue.ulVal = MAKEFOURCC('W', 'M', 'V', '3'); // No need to PropVariantClear as we are assigning a value + hr = pFourCCCodeEnumElements->Add(&pvValue); + CHECK_HR(hr, "Failed to populate the FourCC Code Enumeration Elements"); + } + + if (SUCCEEDED(hr)) + { + hr = pFourCCCode->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_ATTRIBUTE_ENUMERATION_ELEMENTS, pFourCCCodeEnumElements); + CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_ENUMERATION_ELEMENTS for WPD_VIDEO_FOURCC_CODE"); + } + + // Now set the Video FourCC Code property to be pFourCCCode + if (SUCCEEDED(hr)) + { + hr = pProfile->SetIPortableDeviceValuesValue(WPD_VIDEO_FOURCC_CODE, pFourCCCode); + CHECK_HR(hr, "Failed to add the WPD_VIDEO_FOURCC_CODE attributes to the WPD_OBJECT_FORMAT_WMV rendering profile"); + } + } + + // Set the output result + if (SUCCEEDED(hr)) + { + hr = pProfile->QueryInterface(IID_PPV_ARGS(ppProfile)); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues"); + } + + return hr; +} + +HRESULT SetRenderingProfiles( + _In_ IPortableDeviceValues* pValues) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pPreferredAudioProfile; + CComPtr<IPortableDeviceValues> pAudioProfile2; + CComPtr<IPortableDeviceValues> pVideoProfile; + + CComPtr<IPortableDeviceValuesCollection> pProfiles; + + if(pValues == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, ("Cannot have NULL parameter")); + return hr; + } + + // Create the collection to hold the profiles + if (hr == S_OK) + { + hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValuesCollection, + (VOID**) &pProfiles); + CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValuesCollection"); + } + + // Get the preferred audio profile + if (hr == S_OK) + { + hr = GetPreferredAudioProfile(&pPreferredAudioProfile); + CHECK_HR(hr, "Failed to get preferred audio profile properties"); + } + + // Add the profile + if (hr == S_OK) + { + hr = pProfiles->Add(pPreferredAudioProfile); + CHECK_HR(hr, "Failed to add preferred audio profile to profile collection"); + } + + // Get the second audio profile + if (hr == S_OK) + { + hr = GetAudioProfile2(&pAudioProfile2); + CHECK_HR(hr, "Failed to get second audio profile properties"); + } + + // Add the profile + if (hr == S_OK) + { + hr = pProfiles->Add(pAudioProfile2); + CHECK_HR(hr, "Failed to add second audio profile to profile collection"); + } + + // Get the video profile + if (hr == S_OK) + { + hr = GetVideoProfile(&pVideoProfile); + CHECK_HR(hr, "Failed to get video profile properties"); + } + + // Add the profile + if (hr == S_OK) + { + hr = pProfiles->Add(pVideoProfile); + CHECK_HR(hr, "Failed to add second audio profile to profile collection"); + } + + // Set the WPD_RENDERING_INFORMATION_PROFILES + if (hr == S_OK) + { + hr = pValues->SetIPortableDeviceValuesCollectionValue(WPD_RENDERING_INFORMATION_PROFILES, pProfiles); + CHECK_HR(hr, "Failed to set WPD_RENDERING_INFORMATION_PROFILES"); + } + + return hr; +} + +DWORD GetResourceSize( + 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( + 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 IsValidContentType( + _In_ REFGUID guidObjectContentType, + _In_ CAtlArray<GUID>& RestrictedTypes) +{ + HRESULT hr = S_OK; + + size_t numElems = RestrictedTypes.GetCount(); + if(numElems > 0) + { + BOOL bContentTypeAllowed = FALSE; + for(size_t typeIndex = 0; typeIndex < numElems; typeIndex++) + { + if(RestrictedTypes[typeIndex] == guidObjectContentType) + { + bContentTypeAllowed = TRUE; + } + } + if(!bContentTypeAllowed) + { + hr = E_INVALIDARG; + CHECK_HR(hr, "Parent Object does not allow creation of content type %ws", CComBSTR(guidObjectContentType)); + } + } + + 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; + + 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) && 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 PostWpdEventWithProgress( + _In_ IPortableDeviceValues* pCommandParams, + _In_ IPortableDeviceValues* pEventParams, + _In_ REFGUID guidEvent, + const DWORD dwOperationState, + const DWORD dwOperationProgress) +{ + HRESULT hr = S_OK; + + if((pCommandParams == NULL) || (pEventParams == NULL)) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL parameter"); + return hr; + } + + hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, guidEvent); + CHECK_HR(hr, "Failed to set WPD_EVENT_PARAMETER_EVENT_ID"); + + if (hr == S_OK) + { + hr = pEventParams->SetUnsignedIntegerValue(WPD_EVENT_PARAMETER_OPERATION_STATE, dwOperationState); + CHECK_HR(hr, "Failed to set WPD_EVENT_PARAMETER_OPERATION_STATE"); + } + + if (hr == S_OK) + { + hr = pEventParams->SetUnsignedIntegerValue(WPD_EVENT_PARAMETER_OPERATION_PROGRESS, dwOperationProgress); + CHECK_HR(hr, "Failed to set WPD_EVENT_PARAMETER_OPERATION_PROGRESS"); + } + + if (hr == S_OK) + { + hr = PostWpdEvent(pCommandParams, pEventParams); + CHECK_HR(hr, "Failed to post event with progress"); + } + + return hr; +} + + +BOOL ExistsInCollection( + _In_ REFGUID guid, + _In_ IPortableDevicePropVariantCollection* pCollection) +{ + HRESULT hr = S_OK; + + BOOL bFound = FALSE; + DWORD dwNumGuids = 0; + + if(pCollection != NULL) + { + hr = pCollection->GetCount(&dwNumGuids); + if (SUCCEEDED(hr)) + { + // Loop through each guid in the collection + for (DWORD dwIndex = 0; dwIndex < dwNumGuids; dwIndex++) + { + PROPVARIANT pv = {0}; + PropVariantInit(&pv); + hr = pCollection->GetAt(dwIndex, &pv); + if (SUCCEEDED(hr)) + { + if ((pv.puuid != NULL) && (pv.vt == VT_CLSID)) + { + bFound = IsEqualGUID(guid, *pv.puuid); + } + } + + PropVariantClear(&pv); + + if (bFound == TRUE) + { + break; + } + } + } + } + + return bFound; +} + +HRESULT GetAtlStringValue( + _In_ REFPROPERTYKEY Key, + _In_ IPortableDeviceValues* pValues, + _Out_ CAtlStringW& strValue) +{ + HRESULT hr = S_OK; + LPWSTR wszValue = NULL; + strValue = L""; + + if (pValues == NULL) + { + hr = E_POINTER; + return hr; + } + + hr = pValues->GetStringValue(Key, &wszValue); + if (hr == S_OK) + { + strValue = wszValue; + } + + if (wszValue != NULL) + { + CoTaskMemFree(wszValue); + wszValue = NULL; + } + + return hr; +} + +HRESULT CreateVCard( + _In_ IPortableDeviceValues* pValues, + _Out_ CAtlStringA& strVCard) +{ + CAtlStringW strLastName; // WPD_CONTACT_LAST_NAME + CAtlStringW strFirstName; // WPD_CONTACT_FIRST_NAME + CAtlStringW strDisplayName; // WPD_CONTACT_DISPLAY_NAME + CAtlStringW strCompanyName; // WPD_CONTACT_COMPANY_NAME + CAtlStringW strRole; // WPD_CONTACT_ROLE + CAtlStringW strPrimaryPhoneNumber; // WPD_CONTACT_PRIMARY_PHONE or WPD_CONTACT_PERSONAL_PHONE + CAtlStringW strBusinessPhoneNumber; // WPD_CONTACT_BUSINESS_PHONE + CAtlStringW strMobilePhoneNumber; // WPD_CONTACT_MOBILE_PHONE + CAtlStringW strPrimaryFaxPhoneNumber; // WPD_CONTACT_PRIMARY_FAX + CAtlStringW strAddressLine1; // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE1 + CAtlStringW strAddressLine2; // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE2 + CAtlStringW strAddressCity; // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_CITY + CAtlStringW strAddressPostalCode; // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_POSTAL_CODE + + if (pValues != NULL) + { + // Read the contact property values from IPortableDeviceValues + // NOTE: ALL values are not required to be present to create a valid VCARD file. If no properties are + // found then a blank VCARD will be created. + GetAtlStringValue(WPD_CONTACT_LAST_NAME, pValues, strLastName); // WPD_CONTACT_LAST_NAME + GetAtlStringValue(WPD_CONTACT_FIRST_NAME, pValues, strFirstName); // WPD_CONTACT_FIRST_NAME + GetAtlStringValue(WPD_CONTACT_DISPLAY_NAME, pValues, strDisplayName); // WPD_CONTACT_DISPLAY_NAME + GetAtlStringValue(WPD_CONTACT_COMPANY_NAME, pValues, strCompanyName); // WPD_CONTACT_COMPANY_NAME + GetAtlStringValue(WPD_CONTACT_ROLE, pValues, strRole); // WPD_CONTACT_ROLE + GetAtlStringValue(WPD_CONTACT_PRIMARY_PHONE, pValues, strPrimaryPhoneNumber); // WPD_CONTACT_PRIMARY_PHONE or WPD_CONTACT_PERSONAL_PHONE + GetAtlStringValue(WPD_CONTACT_BUSINESS_PHONE, pValues, strBusinessPhoneNumber); // WPD_CONTACT_BUSINESS_PHONE + GetAtlStringValue(WPD_CONTACT_MOBILE_PHONE, pValues, strMobilePhoneNumber); // WPD_CONTACT_MOBILE_PHONE + GetAtlStringValue(WPD_CONTACT_PRIMARY_FAX, pValues, strPrimaryFaxPhoneNumber); // WPD_CONTACT_PRIMARY_FAX + GetAtlStringValue(WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE1, pValues, strAddressLine1); // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE1 + GetAtlStringValue(WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE2, pValues, strAddressLine2); // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_LINE2 + GetAtlStringValue(WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_CITY, pValues, strAddressCity); // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_CITY + GetAtlStringValue(WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_POSTAL_CODE, pValues, strAddressPostalCode); // WPD_CONTACT_PERSONAL_POSTAL_ADDRESS_POSTAL_CODE + } + + // Create the VCARD from the properties found in the IPortableDeviceValues + strVCard.Format(VCARD_FORMAT, strLastName.GetString(), strFirstName.GetString(), + strDisplayName.GetString(), + strCompanyName.GetString(), + strRole.GetString(), + strPrimaryPhoneNumber.GetString(), + strBusinessPhoneNumber.GetString(), + strMobilePhoneNumber.GetString(), + strPrimaryFaxPhoneNumber.GetString(), + strAddressLine1.GetString(), strAddressLine2.GetString(), + strAddressCity.GetString(), strAddressPostalCode.GetString()); + return S_OK; +} + +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 GetCommonResourceAttributes( + _COM_Outptr_ IPortableDeviceValues** ppAttributes) +{ + HRESULT hr = S_OK; + CComPtr<IPortableDeviceValues> pAttributes; + + if(ppAttributes == NULL) + { + hr = E_POINTER; + CHECK_HR(hr, "Cannot have NULL attributes parameter"); + return hr; + } + + *ppAttributes = NULL; + + if (SUCCEEDED(hr)) + { + hr = CoCreateInstance(CLSID_PortableDeviceValues, + NULL, + CLSCTX_INPROC_SERVER, + IID_IPortableDeviceValues, + (VOID**) &pAttributes); + CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); + } + + // Add the attributes that are common to all our resources. + if (SUCCEEDED(hr)) + { + // Add a default value for size. This will be overridden by the content objects with the actual value. + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, FAKE_DATA_SIZE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_READ, TRUE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_READ"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_WRITE, FALSE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_WRITE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_DELETE, FALSE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_DELETE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE"); + } + if (SUCCEEDED(hr)) + { + hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE); + CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE"); + } + + // Return the resource attributes + if (SUCCEEDED(hr)) + { + hr = pAttributes->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppAttributes); + CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues"); + } + return hr; +} + + diff --git a/wpd/WpdWudfSampleDriver/helpers.h b/wpd/WpdWudfSampleDriver/helpers.h new file mode 100644 index 00000000..d2e2cf18 --- /dev/null +++ b/wpd/WpdWudfSampleDriver/helpers.h @@ -0,0 +1,188 @@ +#pragma once + +#define FAKE_DATA_SIZE (5 * 1024 * 1024 + 1831) +#define OPTIMAL_BUFFER_SIZE (2 * 1024 * 1024) + +// {9b2dce3f-cf02-4643-ae09-2bcf0012ac6d} +DEFINE_GUID(FakeContent_Format, 0x9b2dce3f, 0xcf02, 0x4643, 0xae, 0x09, 0x2b, 0xcf, 0x00, 0x12, 0xac, 0x6d); +// {8E829938-D838-479E-8489-5EC84986EE3B} +DEFINE_GUID(FakeDeviceContent_Format, 0x8E829938, 0xD838, 0x479E, 0x84, 0x89, 0x5E, 0xC8, 0x49, 0x86, 0xEE, 0x3B); +// {BDC7BBF8-3AAC-458F-92C9-7CD236186552} +DEFINE_GUID(FakeStorageContent_Format, 0xBDC7BBF8, 0x3AAC, 0x458F, 0x92, 0xC9, 0x7C, 0xD2, 0x36, 0x18, 0x65, 0x52); +// We will define a custom format for memo objects: {C6F2ECC0-C351-42D6-AE20-837CE1EF433C} +DEFINE_GUID(FakeMemoContent_Format, 0xC6F2ECC0, 0xC351, 0x42D6, 0xAE, 0x20, 0x83, 0x7C, 0xE1, 0xEF, 0x43, 0x3C); + +// {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); +// {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); + +#ifndef SAFE_RELEASE + #define SAFE_RELEASE(p) if( NULL != p ) { ( p )->Release(); p = NULL; } +#endif + +typedef enum tagFakeDevicePropertyAttributesType +{ + UnspecifiedForm_CanRead_CanWrite_CannotDelete_Fast, + UnspecifiedForm_CanRead_CannotWrite_CannotDelete_Fast, +} FakeDevicePropertyAttributesType; + +typedef struct tagKeyAndAttributesEntry +{ + const GUID* pFormat; + const PROPERTYKEY* pKey; + FakeDevicePropertyAttributesType type; +} KeyAndAttributesEntry; + +HRESULT AddFixedAttributesByType( + FakeDevicePropertyAttributesType AttributesType, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT AddFixedPropertyAttributes( + _In_ REFGUID guidObjectFormat, + _In_ REFPROPERTYKEY key, + _In_ IPortableDeviceValues* pAttributes); + +HRESULT AddSupportedProperties( + _In_ REFGUID guidObjectFormatOrCategory, + _COM_Outptr_ IPortableDeviceKeyCollection** ppKeys); + +HRESULT AddSupportedProperties( + _In_ REFGUID guidObjectFormatOrCategory, + _In_ IPortableDeviceKeyCollection* pKeys); + +HRESULT SetRenderingProfiles( + _In_ IPortableDeviceValues* pValues); + +HRESULT AddExtraSupportedProperties( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceKeyCollection* pKeys); + +HRESULT AddExtraPropertyValues( + _In_ LPCWSTR pszObjectID, + _In_ IPortableDeviceValues* pValues); + +DWORD GetResourceSize( + UINT uiResource); + +PBYTE GetResourceData( + UINT uiResource); + +HRESULT IsValidContentType( + _In_ REFGUID guidObjectContentType, + _In_ CAtlArray<GUID>& RestrictedTypes); + +HRESULT GetClientContext( + _In_ IPortableDeviceValues* pParams, + _In_ LPCWSTR pszContextKey, + _COM_Outptr_ IUnknown** ppContext); + +HRESULT GetClientEventCookie( + _In_ IPortableDeviceValues* pParams, + _Outptr_result_maybenull_ LPWSTR* ppszEventCookie); + +HRESULT PostWpdEvent( + _In_ IPortableDeviceValues* pCommandParams, + _In_ IPortableDeviceValues* pEventParams); + +HRESULT PostWpdEventWithProgress( + _In_ IPortableDeviceValues* pCommandParams, + _In_ IPortableDeviceValues* pEventParams, + _In_ REFGUID guidEvent, + const DWORD dwOperationState, + const DWORD dwOperationProgress); + +BOOL ExistsInCollection(_In_ REFGUID guid, _In_ IPortableDevicePropVariantCollection* pCollection); + +class PropVariantWrapper : public tagPROPVARIANT +{ +public: + PropVariantWrapper() + { + PropVariantInit(this); + } + + PropVariantWrapper(_In_ LPCWSTR pszSrc) + { + PropVariantInit(this); + + *this = pszSrc; + } + + virtual ~PropVariantWrapper() + { + Clear(); + } + + void Clear() + { + PropVariantClear(this); + } + + PropVariantWrapper& operator= (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(HRESULT hr) + { + Clear(); + vt = VT_ERROR; + scode = hr; + } + + void SetBoolValue(bool bValue) + { + Clear(); + vt = VT_BOOL; + if(bValue) + { + boolVal = VARIANT_TRUE; + } + else + { + boolVal = VARIANT_FALSE; + } + } +}; + +HRESULT CreateVCard(_In_ IPortableDeviceValues* pValues, _Out_ CAtlStringA& strVCard); + +HRESULT UpdateDeviceFriendlyName( + _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension, + _In_ LPCWSTR wszDeviceFriendlyName); + +HRESULT GetCommonResourceAttributes( + _COM_Outptr_ IPortableDeviceValues** ppAttributes); diff --git a/wpd/WpdWudfSampleDriver/resource.h b/wpd/WpdWudfSampleDriver/resource.h new file mode 100644 index 00000000..e0594a3f --- /dev/null +++ b/wpd/WpdWudfSampleDriver/resource.h @@ -0,0 +1,16 @@ +#pragma once +#define IDR_WpdWudfSampleDriver 101 + +#define IDR_WPD_SAMPLEDRIVER_IMAGE 3000 +#define IDR_WPD_SAMPLEDRIVER_IMAGE_THUMBNAIL 3001 +#define IDR_WPD_SAMPLEDRIVER_MUSIC 3002 +#define IDR_WPD_SAMPLEDRIVER_DEVICE_ICON 3003 +#define IDR_WPD_SAMPLEDRIVER_AUDIO_ANNOTATION 3004 +#define IDR_WPD_SAMPLEDRIVER_VIDEO 3005 +#define IDR_WPD_SAMPLEDRIVER_CONTACT_PHOTO 3006 +#define IDR_WPD_SAMPLEDRIVER_INTERNAL_STORAGE_ICON 3007 +#define IDR_WPD_SAMPLEDRIVER_EXTERNAL_STORAGE_ICON 3008 +#define IDR_WPD_SAMPLEDRIVER_MEMO 3009 +#define IDR_WPD_SAMPLEDRIVER_MEMO_ICON 3010 +#define IDR_WPD_SAMPLEDRIVER_MEMO_FOLDER_ICON 3011 + diff --git a/wpd/WpdWudfSampleDriver/stdafx.h b/wpd/WpdWudfSampleDriver/stdafx.h new file mode 100644 index 00000000..7a57563e --- /dev/null +++ b/wpd/WpdWudfSampleDriver/stdafx.h @@ -0,0 +1,118 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#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> + +#include <initguid.h> +#include <propkeydef.h> + +// +// Driver specific tracing #defines +// Declared here as some headers below use these macros +// +// TODO: Change these values to be appropriate for your driver. +// +#define MYDRIVER_TRACING_ID L"Microsoft\\WPD\\WudfSampleDriver" + +// +// TODO: Choose a different trace control GUID +// +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(WudfSampleDriverCtlGuid,(92f71133,1850,4757,899f,96f28bae2f0b), \ + 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 "WpdWudfSampleDriver.h" +#include "PortableDeviceTypes.h" +#include "PortableDeviceClassExtension.h" +#include "PortableDevice.h" +#include "ContextMap.h" +#include "helpers.h" +#include "FakeContent.h" +#include "FakeImageContent.h" +#include "FakeMusicContent.h" +#include "FakeVideoContent.h" +#include "FakeContactContent.h" +#include "FakeMemoContent.h" +#include "FakeFolderContent.h" +#include "RenderingInformationFakeContent.h" +#include "NetworkConfigFakeContent.h" +#include "DeviceObjectFakeContent.h" +#include "StorageObjectFakeContent.h" +#include "FakeDevice.h" +#include "WpdObjectEnum.h" +#include "WpdObjectManagement.h" +#include "WpdObjectProperties.h" +#include "WpdObjectPropertiesBulk.h" +#include "WpdObjectResources.h" +#include "WpdCapabilities.h" +#include "WpdStorage.h" +#include "WpdNetworkConfig.h" +#include "WpdBaseDriver.h" + +extern HINSTANCE g_hInstance; + |
