summaryrefslogtreecommitdiff
path: root/wpd/WpdWudfSampleDriver
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /wpd/WpdWudfSampleDriver
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'wpd/WpdWudfSampleDriver')
-rw-r--r--wpd/WpdWudfSampleDriver/ContextMap.h135
-rw-r--r--wpd/WpdWudfSampleDriver/Device.cpp365
-rw-r--r--wpd/WpdWudfSampleDriver/Device.h119
-rw-r--r--wpd/WpdWudfSampleDriver/DeviceObjectFakeContent.h410
-rw-r--r--wpd/WpdWudfSampleDriver/Driver.cpp199
-rw-r--r--wpd/WpdWudfSampleDriver/Driver.h48
-rw-r--r--wpd/WpdWudfSampleDriver/FakeContactContent.h442
-rw-r--r--wpd/WpdWudfSampleDriver/FakeContent.h576
-rw-r--r--wpd/WpdWudfSampleDriver/FakeDevice.h2415
-rw-r--r--wpd/WpdWudfSampleDriver/FakeFolderContent.h234
-rw-r--r--wpd/WpdWudfSampleDriver/FakeImageContent.h402
-rw-r--r--wpd/WpdWudfSampleDriver/FakeMemoContent.h347
-rw-r--r--wpd/WpdWudfSampleDriver/FakeMusicContent.h354
-rw-r--r--wpd/WpdWudfSampleDriver/FakeVideoContent.h368
-rw-r--r--wpd/WpdWudfSampleDriver/NetworkConfigFakeContent.h378
-rw-r--r--wpd/WpdWudfSampleDriver/Queue.cpp334
-rw-r--r--wpd/WpdWudfSampleDriver/Queue.h93
-rw-r--r--wpd/WpdWudfSampleDriver/ReadMe.md20
-rw-r--r--wpd/WpdWudfSampleDriver/RenderingInformationFakeContent.h138
-rw-r--r--wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wavbin0 -> 77240 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleContactPhoto.pngbin0 -> 16494 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleDeviceIcon.icobin0 -> 47076 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.icobin0 -> 34732 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleImage.jpgbin0 -> 182165 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpgbin0 -> 2539 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.icobin0 -> 34132 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleMemo.txt1
-rw-r--r--wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.icobin0 -> 74683 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleMemoIcon.icobin0 -> 38634 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleMusic.wmabin0 -> 2624518 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/SampleVideo.wmvbin0 -> 63836 bytes
-rw-r--r--wpd/WpdWudfSampleDriver/Stdafxsrc.cpp1
-rw-r--r--wpd/WpdWudfSampleDriver/StorageObjectFakeContent.h327
-rw-r--r--wpd/WpdWudfSampleDriver/WpdBaseDriver.cpp456
-rw-r--r--wpd/WpdWudfSampleDriver/WpdBaseDriver.h113
-rw-r--r--wpd/WpdWudfSampleDriver/WpdCapabilities.cpp899
-rw-r--r--wpd/WpdWudfSampleDriver/WpdCapabilities.h60
-rw-r--r--wpd/WpdWudfSampleDriver/WpdNetworkConfig.cpp122
-rw-r--r--wpd/WpdWudfSampleDriver/WpdNetworkConfig.h21
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectEnum.cpp386
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectEnum.h105
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectManagement.cpp1160
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectManagement.h131
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectProperties.cpp445
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectProperties.h36
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.cpp1027
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectPropertiesBulk.h134
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectResources.cpp944
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectResources.h129
-rw-r--r--wpd/WpdWudfSampleDriver/WpdStorage.cpp102
-rw-r--r--wpd/WpdWudfSampleDriver/WpdStorage.h24
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.cpp68
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.def9
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.idl24
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.inx82
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rc31
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.rgs26
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.sln28
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj407
-rw-r--r--wpd/WpdWudfSampleDriver/WpdWudfSampleDriver.vcxproj.Filters92
-rw-r--r--wpd/WpdWudfSampleDriver/helpers.cpp1600
-rw-r--r--wpd/WpdWudfSampleDriver/helpers.h188
-rw-r--r--wpd/WpdWudfSampleDriver/resource.h16
-rw-r--r--wpd/WpdWudfSampleDriver/stdafx.h118
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
new file mode 100644
index 00000000..fa21dd53
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleAudioAnnotation.wav
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleContactPhoto.png b/wpd/WpdWudfSampleDriver/SampleContactPhoto.png
new file mode 100644
index 00000000..7979e772
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleContactPhoto.png
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico b/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico
new file mode 100644
index 00000000..33a1d1a5
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleDeviceIcon.ico
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico b/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico
new file mode 100644
index 00000000..5598b83b
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleExternalStorageIcon.ico
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleImage.jpg b/wpd/WpdWudfSampleDriver/SampleImage.jpg
new file mode 100644
index 00000000..d018ec4e
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleImage.jpg
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg b/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg
new file mode 100644
index 00000000..39738839
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleImageThumbnail.jpg
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico b/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico
new file mode 100644
index 00000000..e1496876
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleInternalStorageIcon.ico
Binary files differ
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
new file mode 100644
index 00000000..9c07ff66
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleMemoFolderIcon.ico
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico b/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico
new file mode 100644
index 00000000..1bde7cc8
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleMemoIcon.ico
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleMusic.wma b/wpd/WpdWudfSampleDriver/SampleMusic.wma
new file mode 100644
index 00000000..ae9b8f40
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleMusic.wma
Binary files differ
diff --git a/wpd/WpdWudfSampleDriver/SampleVideo.wmv b/wpd/WpdWudfSampleDriver/SampleVideo.wmv
new file mode 100644
index 00000000..657d57ab
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/SampleVideo.wmv
Binary files differ
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;
+