summaryrefslogtreecommitdiff
path: root/wpd/WpdMultiTransportDriver
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/WpdMultiTransportDriver
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'wpd/WpdMultiTransportDriver')
-rw-r--r--wpd/WpdMultiTransportDriver/Device.cpp552
-rw-r--r--wpd/WpdMultiTransportDriver/Device.h97
-rw-r--r--wpd/WpdMultiTransportDriver/Driver.cpp215
-rw-r--r--wpd/WpdMultiTransportDriver/Driver.h47
-rw-r--r--wpd/WpdMultiTransportDriver/Queue.cpp417
-rw-r--r--wpd/WpdMultiTransportDriver/Queue.h149
-rw-r--r--wpd/WpdMultiTransportDriver/ReadMe.md17
-rw-r--r--wpd/WpdMultiTransportDriver/Stdafxsrc.cpp1
-rw-r--r--wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp250
-rw-r--r--wpd/WpdMultiTransportDriver/WpdBaseDriver.h39
-rw-r--r--wpd/WpdMultiTransportDriver/WpdCapabilities.cpp906
-rw-r--r--wpd/WpdMultiTransportDriver/WpdCapabilities.h64
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.cpp61
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.def9
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.idl24
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx83
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rc15
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rgs26
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln28
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj344
-rw-r--r--wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters77
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectEnum.cpp416
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectEnum.h103
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectProperties.cpp1421
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectProperties.h79
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectResources.cpp674
-rw-r--r--wpd/WpdMultiTransportDriver/WpdObjectResources.h111
-rw-r--r--wpd/WpdMultiTransportDriver/resource.h3
-rw-r--r--wpd/WpdMultiTransportDriver/stdafx.h266
29 files changed, 6494 insertions, 0 deletions
diff --git a/wpd/WpdMultiTransportDriver/Device.cpp b/wpd/WpdMultiTransportDriver/Device.cpp
new file mode 100644
index 00000000..9e1af476
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Device.cpp
@@ -0,0 +1,552 @@
+#include "stdafx.h"
+#include "Device.h"
+#include "WpdMultiTransportDriver_i.c"
+
+#include "Device.tmh"
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnD0Entry(_In_ IWDFDevice* /*pDevice*/,
+ WDF_POWER_DEVICE_STATE /*previousState*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnD0Exit(_In_ IWDFDevice* /*pDevice*/,
+ WDF_POWER_DEVICE_STATE /*newState*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(VOID)
+CDevice::OnSurpriseRemoval(_In_ IWDFDevice* /*pDevice*/)
+{
+ return;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnQueryRemove(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnQueryStop(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(VOID)
+CDevice::OnSelfManagedIoCleanup(_In_ IWDFDevice* /*pDevice*/)
+{
+ return;
+}
+
+STDMETHODIMP_(VOID)
+CDevice::OnSelfManagedIoFlush(_In_ IWDFDevice* /*pDevice*/)
+{
+ return;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnSelfManagedIoInit(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnSelfManagedIoSuspend(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnSelfManagedIoRestart(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnSelfManagedIoStop(_In_ IWDFDevice* /*pDevice*/)
+{
+ return S_OK;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnPrepareHardware(_In_ IWDFDevice* pDevice)
+{
+ HRESULT hr = S_OK;
+
+ if (m_pWpdBaseDriver != NULL)
+ {
+ hr = m_pWpdBaseDriver->Initialize();
+ CHECK_HR(hr, "Failed to Initialize the driver class");
+ }
+
+ // Initialize the WPD Class Extension. This will enable the appropriate WPD interface GUID,
+ // as well as do any additional initialization (e.g. enabling Legacy Compatibility layers for those drivers
+ // which requested support in their INF).
+ if (hr == S_OK && m_pPortableDeviceClassExtension == NULL)
+ {
+ CComPtr<IPortableDeviceValues> pOptions;
+ CComPtr<IPortableDevicePropVariantCollection> pContentTypes;
+
+ hr = CoCreateInstance(CLSID_PortableDeviceClassExtension,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceClassExtension,
+ (VOID**)&m_pPortableDeviceClassExtension);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceClassExtension");
+
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pOptions);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+
+ if (hr == S_OK)
+ {
+ CComPtr<IPortableDeviceValues> pIDs;
+
+ // ATTENTION: The following GUID value is provided for illustrative
+ // purposes only.
+ //
+ // Rather than hard-coding a GUID value in your driver, the driver
+ // must obtain a GUID value from the device. The GUID value on the
+ // device can be provisioned by the driver (upon first-connect) by
+ // using CoCreateGUID and setting that value into non-volatile storage
+ // on the device. The same GUID value will then be reported by each
+ // of your device's transports. To avoid a provisioning race condition,
+ // always read the value from the device after provisioning. Only
+ // provision the GUID once. Thereafter, always use the value provided
+ // by the device.
+ GUID guidFUID = { 0x245e5e81, 0x2c17, 0x40a4, { 0x8b, 0x10, 0xe9, 0x43, 0xc5, 0x4c, 0x97, 0xb2 } };
+
+ // Initialize the PortableDeviceClassExtension with a list of supported content types for the
+ // connected device. This will ensure that the correct application compatibility settings will
+ // be applied for your device.
+
+ // Get supported content types
+ if (hr == S_OK)
+ {
+ hr = GetSupportedContentTypes(&pContentTypes);
+ CHECK_HR(hr, "Failed to get supported content types");
+ }
+
+ // Add the supported types to the options
+ if (hr == S_OK)
+ {
+ hr = pOptions->SetIPortableDevicePropVariantCollectionValue(WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES, pContentTypes);
+ CHECK_HR(hr, "Failed to set WPD_CLASS_EXTENSION_OPTIONS_SUPPORTED_CONTENT_TYPES");
+ }
+
+ if (hr == S_OK)
+ {
+ m_pWpdBaseDriver->m_pQueueCallback = NULL;
+
+ HRESULT hrTemp = m_pPortableDeviceClassExtension->QueryInterface(
+ __uuidof(IQueueCallbackDeviceIoControl),
+ (void**)&m_pWpdBaseDriver->m_pQueueCallback
+ );
+ CHECK_HR(hrTemp, "Failed to obtain IQueueCallbackDeviceIoControl interface from class extension");
+
+ if (hrTemp == S_OK)
+ {
+ // Enable the Multi-Transport Mode option
+ hr = pOptions->SetBoolValue(WPD_CLASS_EXTENSION_OPTIONS_MULTITRANSPORT_MODE, TRUE);
+ CHECK_HR(hr, "Failed to enable multi-transport mode");
+
+ // Create a PnP ID value collection
+ if (hr == S_OK)
+ {
+ hr = CreateIDValues(DEVICE_MANUFACTURER_VALUE,
+ DEVICE_MODEL_VALUE,
+ DEVICE_FIRMWARE_VERSION_VALUE,
+ guidFUID,
+ &pIDs);
+ CHECK_HR(hr, "Failed to Create the ID value collection");
+ }
+
+ // Add the PnP ID value collection to the options
+ if (hr == S_OK)
+ {
+ hr = pOptions->SetIPortableDeviceValuesValue(WPD_CLASS_EXTENSION_OPTIONS_DEVICE_IDENTIFICATION_VALUES, pIDs);
+ CHECK_HR(hr, "Failed to set WPD_CLASS_EXTENSION_OPTIONS_DEVICE_IDENTIFICATION_VALUES");
+ }
+
+ // Add the transport bandwidth (in kilobits per second units) to the options
+ // (0 indicates bandwidth unknown)
+ if (hr == S_OK)
+ {
+ // Set the transport bandwidth (optional)
+ hr = pOptions->SetUnsignedIntegerValue(WPD_CLASS_EXTENSION_OPTIONS_TRANSPORT_BANDWIDTH, 0L);
+ CHECK_HR(hr, "Failed to set WPD_CLASS_EXTENSION_OPTIONS_TRANSPORT_BANDWIDTH");
+ }
+ }
+ }
+
+ if (hr == S_OK)
+ {
+ hr = m_pPortableDeviceClassExtension->Initialize(pDevice, pOptions);
+ CHECK_HR(hr, "Failed to Initialize portable device class extension object");
+ }
+
+ // Since users commonly have the abiltity to customize their device even when it is not
+ // connected to the PC, we need to make sure the PC is current when the driver loads.
+ //
+ // Send the latest device friendly name to the PortableDeviceClassExtension component
+ // so the system is always updated with the current device name.
+ //
+ // This call should also be made after a successful property set operation of
+ // WPD_DEVICE_FRIENDLY_NAME.
+
+ LPWSTR wszDeviceFriendlyName = NULL;
+
+ if (hr == S_OK)
+ {
+ hr = GetDeviceFriendlyName(&wszDeviceFriendlyName);
+ CHECK_HR(hr, "Failed to get device's friendly name");
+ }
+
+ if (hr == S_OK && wszDeviceFriendlyName != NULL)
+ {
+ hr = UpdateDeviceFriendlyName(m_pPortableDeviceClassExtension, wszDeviceFriendlyName);
+ CHECK_HR(hr, "Failed to update device's friendly name");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszDeviceFriendlyName);
+ }
+ }
+ }
+
+ return hr;
+}
+
+STDMETHODIMP_(HRESULT)
+CDevice::OnReleaseHardware(_In_ IWDFDevice* /*pDevice*/)
+{
+ if (m_pWpdBaseDriver != NULL)
+ {
+ m_pWpdBaseDriver->Uninitialize();
+ }
+
+ if (m_pPortableDeviceClassExtension != NULL)
+ {
+ m_pPortableDeviceClassExtension = NULL;
+ }
+
+ return S_OK;
+}
+
+HRESULT CDevice::GetSupportedContentTypes(
+ _Outptr_ IPortableDevicePropVariantCollection** ppContentTypes)
+{
+ HRESULT hr = S_OK;
+ CComPtr<IPortableDeviceValues> pParams;
+ CComPtr<IPortableDeviceValues> pResults;
+
+ if (ppContentTypes == NULL)
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ // CoCreate a collection to store the WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES command parameters.
+ if(SUCCEEDED(hr))
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pParams);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // CoCreate a collection to store the WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES command results.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pResults);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // Set the params
+ if(SUCCEEDED(hr))
+ {
+ hr = pParams->SetGuidValue(WPD_PROPERTY_COMMON_COMMAND_CATEGORY, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES.fmtid);
+ CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_CATEGORY"));
+ }
+ if(SUCCEEDED(hr))
+ {
+ hr = pParams->SetUnsignedIntegerValue(WPD_PROPERTY_COMMON_COMMAND_ID, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES.pid);
+ CHECK_HR(hr, ("Failed to set WPD_PROPERTY_COMMON_COMMAND_ID"));
+ }
+ if(SUCCEEDED(hr))
+ {
+ hr = pParams->SetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, WPD_FUNCTIONAL_CATEGORY_ALL);
+ CHECK_HR(hr, ("Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY"));
+ }
+
+ // Make the call
+ if(SUCCEEDED(hr))
+ {
+ hr = m_pWpdBaseDriver->DispatchWpdMessage(pParams, pResults);
+ CHECK_HR(hr, ("Failed to dispatch message to get supported content types"));
+ }
+
+ // Get the results
+ if(SUCCEEDED(hr))
+ {
+ hr = pResults->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, ppContentTypes);
+ CHECK_HR(hr, ("Failed to get WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES"));
+ }
+
+ return hr;
+}
+
+HRESULT CDevice::GetDeviceFriendlyName(
+ _Outptr_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_INVALIDARG;
+ return hr;
+ }
+
+ *pwszDeviceFriendlyName = NULL;
+
+ // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command parameters.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pParams);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // CoCreate a collection to store the WPD_COMMAND_OBJECT_PROPERTIES_GET command results.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pResults);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // CoCreate a collection to store the requested property keys. In our case, we are requesting just the device friendly name
+ // (WPD_DEVICE_FRIENDLY_NAME)
+ if (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"));
+ }
+
+ // Get the results
+ 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;
+}
+
+HRESULT
+CDevice::CreateIDValues(
+ _In_ LPCWSTR pszManufacturer,
+ _In_ LPCWSTR pszModel,
+ _In_opt_ LPCWSTR pszVersion,
+ _In_ REFGUID guidFUID,
+ _COM_Outptr_ IPortableDeviceValues** ppValues)
+{
+ HRESULT hr = S_OK;
+ CComPtr<IPortableDeviceValues> pValues;
+
+ *ppValues = NULL;
+
+ // Create the object to hold the ID values
+ hr = CoCreateInstance(
+ CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**)&pValues);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, pszManufacturer);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_MODEL, pszModel);
+ }
+
+ if (SUCCEEDED(hr) && pszVersion)
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, pszVersion);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pValues->SetGuidValue(WPD_DEVICE_FUNCTIONAL_UNIQUE_ID, guidFUID);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppValues = pValues.Detach();
+ }
+
+ return hr;
+}
+
+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;
+}
diff --git a/wpd/WpdMultiTransportDriver/Device.h b/wpd/WpdMultiTransportDriver/Device.h
new file mode 100644
index 00000000..db76d087
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Device.h
@@ -0,0 +1,97 @@
+#pragma once
+
+#include "resource.h"
+#include "WpdMultiTransportDriver.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);
+
+ HRESULT CreateIDValues(
+ _In_ LPCWSTR pszManufacturer,
+ _In_ LPCWSTR pszModel,
+ _In_opt_ LPCWSTR pszVersion,
+ _In_ REFGUID guidFUID,
+ _COM_Outptr_ IPortableDeviceValues** ppValues);
+
+private:
+ WpdBaseDriver* m_pWpdBaseDriver;
+ CComPtr<IPortableDeviceClassExtension> m_pPortableDeviceClassExtension;
+};
+
diff --git a/wpd/WpdMultiTransportDriver/Driver.cpp b/wpd/WpdMultiTransportDriver/Driver.cpp
new file mode 100644
index 00000000..584297de
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Driver.cpp
@@ -0,0 +1,215 @@
+#include "stdafx.h"
+#include "Driver.h"
+#include "Device.h"
+#include "Queue.h"
+
+CDriver::CDriver()
+{
+}
+
+HRESULT
+CDriver::OnDeviceAdd(
+ _In_ IWDFDriver* pDriver,
+ _In_ IWDFDeviceInitialize* pDeviceInit
+ )
+/*++
+
+Routine Description:
+
+ The framework calls this function when a device is being added to
+ the driver stack.
+
+Arguments:
+
+ IWDFDriver - Framework interface. The driver uses this
+ interface to create device objects.
+ IWDFDeviceInitialize - Framework interface. The driver uses this
+ interface to set device parameters before
+ creating the device obeject.
+
+Return Value:
+
+ HRESULT S_OK - Device added successfully
+
+--*/
+{
+ HRESULT hr = S_OK;
+ CComPtr<IUnknown> pDeviceCallback;
+
+ 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 the default queue callback object
+ //
+ CComPtr<IUnknown> pIUnknown;
+ if(S_OK == hr)
+ {
+ hr = CDefaultQueue::CreateInstance(&pIUnknown);
+ }
+
+ //
+ // Configure the default queue.
+ //
+ if(S_OK == hr)
+ {
+ CComPtr<IWDFIoQueue> pDefaultQueue;
+ hr = pIWDFDevice->CreateIoQueue(
+ pIUnknown,
+ TRUE, // bDefaultQueue
+ WdfIoQueueDispatchParallel,
+ TRUE, // bPowerManaged
+ FALSE, // bAllowZeroLengthRequests
+ &pDefaultQueue);
+ }
+ pIUnknown = NULL;
+
+ //
+ // Create the WPD queue callback object
+ //
+ if(S_OK == hr)
+ {
+ hr = CQueue::CreateInstance(&pIUnknown);
+ }
+
+ //
+ // Configure the WPD queue.
+ //
+ if(S_OK == hr)
+ {
+ hr = pIWDFDevice->CreateIoQueue(
+ pIUnknown,
+ FALSE, // bDefaultQueue
+ WdfIoQueueDispatchSequential,
+ TRUE, // bPowerManaged
+ FALSE, // bAllowZeroLengthRequests
+ &pWpdBaseDriver->m_pWpdQueue);
+ }
+
+ 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/WpdMultiTransportDriver/Driver.h b/wpd/WpdMultiTransportDriver/Driver.h
new file mode 100644
index 00000000..c4c07ad8
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Driver.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "resource.h"
+#include "WpdMultiTransportDriver.h"
+
+class ATL_NO_VTABLE CDriver :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public CComCoClass<CDriver, &CLSID_WpdMultiTransportDriver>,
+ public IDriverEntry,
+ public IObjectCleanup
+{
+public:
+ CDriver();
+
+ DECLARE_REGISTRY_RESOURCEID(IDR_WpdMultiTransportDriver)
+
+ 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(WpdMultiTransportDriver), CDriver)
+
diff --git a/wpd/WpdMultiTransportDriver/Queue.cpp b/wpd/WpdMultiTransportDriver/Queue.cpp
new file mode 100644
index 00000000..9d6c4571
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Queue.cpp
@@ -0,0 +1,417 @@
+// 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 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 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;
+}
+
+// CDefaultQueue
+
+STDMETHODIMP_ (void)
+CDefaultQueue::OnCreateFile(
+ _In_ IWDFIoQueue* pQueue,
+ _In_ IWDFIoRequest* pRequest,
+ _In_ IWDFFile* pFileObject
+ )
+{
+ UNREFERENCED_PARAMETER(pFileObject);
+
+ CComPtr<WpdBaseDriver> pWpdBaseDriver;
+ CComPtr<IWDFDevice> pDevice;
+ HRESULT hr = S_OK;
+
+ pQueue->GetDevice(&pDevice);
+
+ hr = GetWpdBaseDriver(pDevice, &pWpdBaseDriver);
+ CHECK_HR(hr, "Failed to get WpdBaseDriver");
+
+ if (hr == S_OK)
+ {
+ hr = pRequest->ForwardToIoQueue(pWpdBaseDriver->m_pWpdQueue);
+ CHECK_HR(hr, "Failed to get WpdBaseDriver");
+ }
+
+ if (FAILED(hr))
+ pRequest->Complete(hr);
+
+ return;
+}
+
+STDMETHODIMP_ (void)
+CDefaultQueue::OnDeviceIoControl(
+ _In_ IWDFIoQueue* pQueue,
+ _In_ IWDFIoRequest* pRequest,
+ ULONG ControlCode,
+ SIZE_T InputBufferSizeInBytes,
+ SIZE_T OutputBufferSizeInBytes
+ )
+{
+ CComPtr<WpdBaseDriver> pWpdBaseDriver;
+ CComPtr<IWDFDevice> pDevice;
+ HRESULT hr = S_OK;
+
+ pQueue->GetDevice(&pDevice);
+
+ hr = GetWpdBaseDriver(pDevice, &pWpdBaseDriver);
+ CHECK_HR(hr, "Failed to get WpdBaseDriver");
+
+ if (hr == S_OK)
+ {
+ if (IS_WPD_IOCTL(ControlCode))
+ {
+ hr = pRequest->ForwardToIoQueue(pWpdBaseDriver->m_pWpdQueue);
+ CHECK_HR(hr, "Failed to forward to WPD queue");
+ }
+ else if (pWpdBaseDriver->m_pQueueCallback)
+ {
+ pWpdBaseDriver->m_pQueueCallback->OnDeviceIoControl(
+ pQueue,
+ pRequest,
+ ControlCode,
+ InputBufferSizeInBytes,
+ OutputBufferSizeInBytes
+ );
+ }
+ else
+ {
+ hr = E_UNEXPECTED;
+ CHECK_HR(hr, "Unable to handle IOCTL code '0x%lx'", ControlCode);
+ }
+ }
+
+ if (FAILED(hr))
+ pRequest->Complete(hr);
+
+ return;
+}
+
+/******************************************************************************
+ * 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;
+}
+
+// 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
+ )
+{
+ HRESULT hr = S_OK;
+ DWORD dwBytesWritten = 0;
+ UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
+ UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);
+
+ 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/WpdMultiTransportDriver/Queue.h b/wpd/WpdMultiTransportDriver/Queue.h
new file mode 100644
index 00000000..abf14e2c
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Queue.h
@@ -0,0 +1,149 @@
+// Queue.h : Declaration of the CQueue
+
+#pragma once
+#include "resource.h" // main symbols
+#include "WpdMultiTransportDriver.h"
+
+class ATL_NO_VTABLE CDefaultQueue :
+ public CComObjectRootEx<CComMultiThreadModel>,
+ public IQueueCallbackDeviceIoControl,
+ public IQueueCallbackCreate
+{
+public:
+ CDefaultQueue()
+ {
+
+ }
+
+ DECLARE_NOT_AGGREGATABLE(CDefaultQueue)
+
+ BEGIN_COM_MAP(CDefaultQueue)
+ COM_INTERFACE_ENTRY(IQueueCallbackDeviceIoControl)
+ COM_INTERFACE_ENTRY(IQueueCallbackCreate)
+ END_COM_MAP()
+
+public:
+ static
+ HRESULT CreateInstance(
+ _COM_Outptr_ IUnknown** ppUkwn)
+ {
+ *ppUkwn = NULL;
+ CComObject< CDefaultQueue> *pMyQueue = NULL;
+ HRESULT hr = CComObject<CDefaultQueue>::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
+ );
+};
+
+
+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);
+
+ CComPtr<IWpdSerializer> m_pWpdSerializer;
+ CComAutoCriticalSection m_CriticalSection;
+};
+
diff --git a/wpd/WpdMultiTransportDriver/ReadMe.md b/wpd/WpdMultiTransportDriver/ReadMe.md
new file mode 100644
index 00000000..89700605
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/ReadMe.md
@@ -0,0 +1,17 @@
+WPD multi-transport sample driver
+=================================
+
+The WpdMultiTransportDriver sample demonstrates how you could extend the WpdHelloWorldDriver for a device that supports multiple transports. A transport is a protocol over which a portable device communicates with a computer. Example transports include Internet Protocol (IP), Bluetooth, and USB.
+
+A number of portable devices now support multiple transports. For example, a number of cell phones support both Bluetooth and USB. Before Windows 7, if a user connected a portable device that supported multiple transports to their computer, the Windows Device Manager displayed a unique node for each transport. This implied that multiple devices had been installed and left the user confused. To resolve this, Windows now supports a multitransport driver model. This model ensures that only one node appears for each device.
+
+For a complete description of this sample and its underlying code and functionality, refer to the [WPD MultiTransport Driver](http://msdn.microsoft.com/en-us/library/windows/hardware/ff597709) 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/WpdMultiTransportDriver/Stdafxsrc.cpp b/wpd/WpdMultiTransportDriver/Stdafxsrc.cpp
new file mode 100644
index 00000000..5105a28d
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/Stdafxsrc.cpp
@@ -0,0 +1 @@
+#include "Stdafx.h" \ No newline at end of file
diff --git a/wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp b/wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp
new file mode 100644
index 00000000..f57892a3
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdBaseDriver.cpp
@@ -0,0 +1,250 @@
+#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_RESOURCES)
+ {
+ hr = m_ObjectResources.DispatchWpdMessage(CommandKey, pParams, pResults);
+ }
+ else if (CommandKey.fmtid == WPD_CATEGORY_CAPABILITIES)
+ {
+ hr = m_Capabilities.DispatchWpdMessage(CommandKey, 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 received.
+ // the return code for the actual command's results is stored in the
+ // WPD_PROPERTY_COMMON_HRESULT property.
+ hr = S_OK;
+
+ return hr;
+}
+
+/**
+ * This method is called to initialize the driver object.
+ * This is where the driver would set up it's I/O libraries
+ * and so on.
+ */
+HRESULT WpdBaseDriver::Initialize()
+{
+ return S_OK;
+}
+
+/**
+ * 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()
+{
+}
+
+/**
+ * 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);
+
+ // Since our persistent unique identifier are identical to our object
+ // identifiers, we just return it back to the caller.
+ if (hr == S_OK)
+ {
+ pvObjectID.pwszVal = AtlAllocTaskWideString(pvPersistentID.pwszVal);
+ }
+
+ 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/WpdMultiTransportDriver/WpdBaseDriver.h b/wpd/WpdMultiTransportDriver/WpdBaseDriver.h
new file mode 100644
index 00000000..a7462df2
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdBaseDriver.h
@@ -0,0 +1,39 @@
+#pragma once
+
+class WpdBaseDriver :
+ public IUnknown
+{
+public:
+ WpdBaseDriver();
+ virtual ~WpdBaseDriver();
+
+ HRESULT Initialize();
+ VOID Uninitialize();
+
+ HRESULT DispatchWpdMessage(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+private:
+ HRESULT OnGetObjectIDsFromPersistentUniqueIDs(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+public: // IUnknown
+ ULONG __stdcall AddRef();
+
+ _At_(this, __drv_freesMem(Mem))
+ ULONG __stdcall Release();
+
+ HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);
+
+public:
+ WpdObjectEnumerator m_ObjectEnum;
+ WpdObjectProperties m_ObjectProperties;
+ WpdObjectResources m_ObjectResources;
+ WpdCapabilities m_Capabilities;
+ CComPtr<IWDFIoQueue> m_pWpdQueue;
+ CComPtr<IQueueCallbackDeviceIoControl> m_pQueueCallback;
+
+private:
+ ULONG m_cRef;
+};
+
diff --git a/wpd/WpdMultiTransportDriver/WpdCapabilities.cpp b/wpd/WpdMultiTransportDriver/WpdCapabilities.cpp
new file mode 100644
index 00000000..9dd3faed
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdCapabilities.cpp
@@ -0,0 +1,906 @@
+#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_PROPERTIES
+ WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED,
+ WPD_COMMAND_OBJECT_PROPERTIES_GET,
+ WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL,
+ WPD_COMMAND_OBJECT_PROPERTIES_SET,
+ WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES,
+ WPD_COMMAND_OBJECT_PROPERTIES_DELETE,
+
+ // WPD_CATEGORY_OBJECT_RESOURCES
+ WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED,
+ WPD_COMMAND_OBJECT_RESOURCES_OPEN,
+ WPD_COMMAND_OBJECT_RESOURCES_READ,
+ WPD_COMMAND_OBJECT_RESOURCES_CLOSE,
+ WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES,
+
+ // WPD_CATEGORY_CAPABILITIES
+ WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS,
+ WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS,
+ WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES,
+ WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS,
+ WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES,
+ WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS,
+ WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES,
+ WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES,
+};
+
+const GUID g_SupportedFunctionalCategories[] =
+{
+ WPD_FUNCTIONAL_CATEGORY_DEVICE,
+ WPD_FUNCTIONAL_CATEGORY_STORAGE,
+};
+
+WpdCapabilities::WpdCapabilities()
+{
+
+}
+
+WpdCapabilities::~WpdCapabilities()
+{
+
+}
+
+HRESULT WpdCapabilities::DispatchWpdMessage(_In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+
+ if (Command.fmtid != WPD_CATEGORY_CAPABILITIES)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid));
+ }
+
+ if (hr == S_OK)
+ {
+ if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS))
+ {
+ hr = OnGetSupportedCommands(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported commands");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS))
+ {
+ hr = OnGetCommandOptions(pParams, pResults);
+ CHECK_HR(hr, "Failed to get command options");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES))
+ {
+ hr = OnGetFunctionalCategories(pParams, pResults);
+ CHECK_HR(hr, "Failed to get functional categories");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS))
+ {
+ hr = OnGetFunctionalObjects(pParams, pResults);
+ CHECK_HR(hr, "Failed to get functional objects");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES))
+ {
+ hr = OnGetSupportedContentTypes(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported content types");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS))
+ {
+ hr = OnGetSupportedFormats(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported formats");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES))
+ {
+ hr = OnGetSupportedFormatProperties(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported format properties");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES))
+ {
+ hr = OnGetFixedPropertyAttributes(pParams, pResults);
+ CHECK_HR(hr, "Failed to get fixed property attributes");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS))
+ {
+ hr = OnGetSupportedEvents(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported events");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS))
+ {
+ hr = OnGetEventOptions(pParams, pResults);
+ CHECK_HR(hr, "Failed to get event options");
+ }
+ else
+ {
+ hr = E_NOTIMPL;
+ CHECK_HR(hr, "This object does not support this command id %d", Command.pid);
+ }
+ }
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_COMMANDS
+ * command.
+ *
+ * The parameters sent to us are:
+ * - none.
+ *
+ * The driver should:
+ * - Return all commands supported by this driver as an
+ * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS.
+ * This includes custom commands, if any.
+ *
+ * Note that certain commands require a "command target" to function correctly.
+ * (e.g. delete object command) It is understood that not all objects are necessarily
+ * valid targets (e.g. you cannot delete the device object).
+ */
+HRESULT WpdCapabilities::OnGetSupportedCommands(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ CComPtr<IPortableDeviceKeyCollection> pCommands;
+ UNREFERENCED_PARAMETER(pParams);
+
+ // CoCreate a collection to store the supported commands.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceKeyCollection,
+ (VOID**) &pCommands);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
+ }
+
+ // Add the supported commands to the collection.
+ if (hr == S_OK)
+ {
+ 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;
+ }
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS, pCommands);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_COMMANDS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_COMMAND_OPTIONS
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_COMMAND: a collection of property keys containing a single value,
+ * which identifies the specific command options are requested to return.
+ *
+ * The driver should:
+ * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, containing
+ * the relevant options. If no options are available for this command, the driver should
+ * return an IPortableDeviceValues with no elements in it.
+ */
+HRESULT WpdCapabilities::OnGetCommandOptions(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ PROPERTYKEY Command = WPD_PROPERTY_NULL;
+ CComPtr<IPortableDeviceValues> pOptions;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the command whose options have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_COMMAND, &Command);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_COMMAND");
+ }
+
+ // CoCreate a collection to store the command options.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pOptions);
+ CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
+ }
+
+ // Add command options to the collection
+ if (hr == S_OK)
+ {
+ // If your driver supports command options, then they should be added here
+ // to the command options collection 'pOptions'.
+ 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");
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS, pOptions);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_COMMAND_OPTIONS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FUNCTIONAL_CATEGORIES
+ * command.
+ *
+ * The parameters sent to us are:
+ * - none.
+ *
+ * The driver should:
+ * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in
+ * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, containing
+ * the supported functional categories for this device.
+ */
+HRESULT WpdCapabilities::OnGetFunctionalCategories(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ CComPtr<IPortableDevicePropVariantCollection> pFunctionalCategories;
+
+ UNREFERENCED_PARAMETER(pParams);
+
+ // CoCreate a collection to store the supported functional categories.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pFunctionalCategories);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // Add the supported functional categories to the collection.
+ if (hr == S_OK)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedFunctionalCategories); dwIndex++)
+ {
+ PROPVARIANT pv = {0};
+ PropVariantInit(&pv);
+ // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs
+
+ pv.vt = VT_CLSID;
+ pv.puuid = (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;
+ }
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES, pFunctionalCategories);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORIES");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_FUNCTIONAL_OBJECTS
+ * command. It is sent when the caller is interesting in finding the object IDs for all
+ * functional objects belonging to the specified functional category.
+ * Note: the number of functional objects is expected to be very small (less than 8 for the
+ * whole device).
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY - a GUID value containing the category
+ * the caller is looking for. If the value is WPD_FUNCTIONAL_CATEGORY_ALL, then the driver
+ * must return all functional objects, no matter which category they belong to.
+ *
+ * The driver should:
+ * - Return an IPortableDevicePropVariantCollection (of type VT_LPWSTR) in
+ * WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, containing
+ * the ids of the functional objects who belong to the specified functional category.
+ * If there are no objects in the specified category, the driver should return an
+ * empty collection.
+ */
+HRESULT WpdCapabilities::OnGetFunctionalObjects(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID guidFunctionalCategory = GUID_NULL;
+ CComPtr<IPortableDevicePropVariantCollection> pFunctionalObjects;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the functional category whose functional object identifiers have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY");
+ }
+
+ // CoCreate a collection to store the supported functional object identifiers.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pFunctionalObjects);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // Add the supported functional object identifiers for the specified functional
+ // category to the collection.
+ if (hr == S_OK)
+ {
+ PROPVARIANT pv = {0};
+ PropVariantInit(&pv);
+ // Don't call PropVariantClear, since we did not allocate the memory for these object identifiers
+
+ // Add WPD_DEVICE_OBJECT_ID to the functional object identifiers collection
+ if (hr == S_OK)
+ {
+ if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_DEVICE) ||
+ (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL))
+ {
+ pv.vt = VT_LPWSTR;
+ pv.pwszVal = WPD_DEVICE_OBJECT_ID;
+ hr = pFunctionalObjects->Add(&pv);
+ CHECK_HR(hr, "Failed to add device object ID");
+ }
+ }
+
+ // Add STORAGE_OBJECT_ID to the functional object identifiers collection
+ if (hr == S_OK)
+ {
+ if ((guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE) ||
+ (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_ALL))
+ {
+ pv.vt = VT_LPWSTR;
+ pv.pwszVal = STORAGE_OBJECT_ID;
+ hr = pFunctionalObjects->Add(&pv);
+ CHECK_HR(hr, "Failed to add storage object ID");
+ }
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS, pFunctionalObjects);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_OBJECTS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_CONTENT_TYPES
+ * command. This message is sent when the client needs to know the possible content types supported
+ * by the specified functional category.
+ * If the driver has multiple functional objects that may support different content types,
+ * the driver should simply merge them together and report all possible types in one list here.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY - a GUID value containing the functional category
+ * whose content types the caller is interested in. If the value is WPD_FUNCTIONAL_CATEGORY_ALL, then the driver
+ * must return a list of all content types supported by the device.
+ *
+ * The driver should:
+ * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in
+ * WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, containing
+ * the content types supported by the specified functional category.
+ * If there are no objects in the specified category, the driver should return an
+ * empty collection.
+ */
+HRESULT WpdCapabilities::OnGetSupportedContentTypes(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID guidFunctionalCategory = GUID_NULL;
+ CComPtr<IPortableDevicePropVariantCollection> pContentTypes;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the functional category whose supported content types have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY, &guidFunctionalCategory);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FUNCTIONAL_CATEGORY");
+ }
+
+ // CoCreate a collection to store the supported content types.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pContentTypes);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // Add the supported content types for the specified functional
+ // category to the collection.
+ if (hr == S_OK)
+ {
+ PROPVARIANT pv = {0};
+ PropVariantInit(&pv);
+ // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs
+
+ // Add supported content types for known functional categories
+ if (guidFunctionalCategory == WPD_FUNCTIONAL_CATEGORY_STORAGE)
+ {
+ // Add WPD_CONTENT_TYPE_DOCUMENT to the supported content type collection
+ pv.vt = VT_CLSID;
+ pv.puuid = (CLSID*)&WPD_CONTENT_TYPE_DOCUMENT;
+ hr = pContentTypes->Add(&pv);
+ CHECK_HR(hr, "Failed to add WPD_CONTENT_TYPE_DOCUMENT");
+
+ if (hr == S_OK)
+ {
+ // Add WPD_CONTENT_TYPE_FOLDER to the supported content type collection
+ pv.vt = VT_CLSID;
+ pv.puuid = (CLSID*)&WPD_CONTENT_TYPE_FOLDER;
+ hr = pContentTypes->Add(&pv);
+ CHECK_HR(hr, "Failed to add WPD_CONTENT_TYPE_FOLDER");
+ }
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES, pContentTypes);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_CONTENT_TYPES");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMATS
+ * command. This message is sent when the client needs to know the possible formats supported
+ * by the specified content type (e.g. for image objects, the driver may choose to support JPEG and BMP files).
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE - a GUID value containing the content type
+ * whose formats the caller is interested in. If the value is WPD_CONTENT_TYPE_ALL, then the driver
+ * must return a list of all formats supported by the device.
+ *
+ * The driver should:
+ * - Return an IPortableDevicePropVariantCollection (of type VT_CLSID) in
+ * WPD_PROPERTY_CAPABILITIES_FORMATS, indicating the formats supported by the
+ * specified content type.
+ * If there are no formats supported by the specified content type, the driver should return an
+ * empty collection.
+ */
+HRESULT WpdCapabilities::OnGetSupportedFormats(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID guidContentType = GUID_NULL;
+ CComPtr<IPortableDevicePropVariantCollection> pFormats;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the content type whose supported formats have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE, &guidContentType);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_CONTENT_TYPE");
+ }
+
+ // CoCreate a collection to store the supported formats.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pFormats);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // Add the supported formats for the specified content type to the collection.
+ if (hr == S_OK)
+ {
+ PROPVARIANT pv = {0};
+ PropVariantInit(&pv);
+ // Don't call PropVariantClear, since we did not allocate the memory for these GUIDs
+
+ if ((guidContentType == WPD_CONTENT_TYPE_DOCUMENT) ||
+ ((guidContentType == WPD_CONTENT_TYPE_ALL)))
+ {
+ // Add WPD_OBJECT_FORMAT_TEXT to the supported formats collection
+ pv.vt = VT_CLSID;
+ pv.puuid = (CLSID*)&WPD_OBJECT_FORMAT_TEXT;
+ hr = pFormats->Add(&pv);
+ CHECK_HR(hr, "Failed to add WPD_OBJECT_FORMAT_TEXT");
+ }
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_FORMATS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_FORMATS, pFormats);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_FORMATS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_FORMAT_PROPERTIES
+ * command. This message is sent when the client needs to know the typical properties for objects of
+ * a given format.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_FORMAT - a GUID value specifying the format the caller is interested in.
+ *
+ * The driver should:
+ * - Return an IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS,
+ * containing the property keys.
+ */
+HRESULT WpdCapabilities::OnGetSupportedFormatProperties(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID guidObjectFormat = GUID_NULL;
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object format whose supported properties have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT");
+ }
+
+ // CoCreate a collection to store the supported properties.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceKeyCollection,
+ (VOID**) &pKeys);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
+ }
+
+ // Add the supported properties for the specified object format to the collection.
+ if (hr == S_OK)
+ {
+ hr = AddSupportedPropertyKeys(guidObjectFormat, pKeys);
+ CHECK_HR(hr, "Failed to get supported properties for a format");
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, pKeys);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_FIXED_PROPERTY_ATTRIBUTES
+ * command. This message is sent when the client needs to know the property attributes that
+ * are the same for all objects of the given format.
+ *
+ * Typically, a driver treats objects of a given format the same. Many properties therefore will
+ * have attributes that are identical across all objects of that format.
+ * These can be returned here. There are some attributes which may be differ per object instance,
+ * which are not returned here.
+ * See WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_FORMAT - a GUID value specifying the format the caller is interested in.
+ * - WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS - a collection of property keys containing a single value,
+ * which is the key identifying the specific property attributes we are requested to return.
+ *
+ * The driver should:
+ * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES
+ * containing the fixed property attributes.
+ */
+HRESULT WpdCapabilities::OnGetFixedPropertyAttributes(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID guidObjectFormat = GUID_NULL;
+ PROPERTYKEY key = WPD_PROPERTY_NULL;
+ CComPtr<IPortableDeviceValues> pAttributes;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object format whose fixed property attributes have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_FORMAT, &guidObjectFormat);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_FORMAT");
+ }
+
+ // Get the property whose fixed property attributes have been requested
+ if(hr == S_OK)
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS, &key);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_PROPERTY_KEYS");
+ }
+
+ // CoCreate a collection to store the fixed property attributes.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pAttributes);
+ CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
+ }
+
+ // Add the fixed property attributes for the specified object format and property
+ if (hr == S_OK)
+ {
+ hr = GetFixedPropertyAttributesForFormat(guidObjectFormat, key, pAttributes);
+ CHECK_HR(hr, "Failed to get fixed property attributes");
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES, pAttributes);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_PROPERTY_ATTRIBUTES");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_SUPPORTED_EVENTS
+ * command.
+ *
+ * The parameters sent to us are:
+ * - none.
+ *
+ * The driver should:
+ * - Return all events supported by this driver should be returned as an
+ * IPortableDeviceKeyCollection in WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS.
+ * That includes custom commands, if any.
+ */
+HRESULT WpdCapabilities::OnGetSupportedEvents(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ CComPtr<IPortableDevicePropVariantCollection> pEvents;
+ UNREFERENCED_PARAMETER(pParams);
+
+ // CoCreate a collection to store the supported events.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pEvents);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // Add the supported events to the collection.
+ if (hr == S_OK)
+ {
+ // If your driver supports events, then they should be added here
+ // to the supported events collection 'pEvents'.
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS, pEvents);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_SUPPORTED_EVENTS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_CAPABILITIES_GET_EVENT_OPTIONS
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_CAPABILITIES_EVENT: a GUID value indicating the Event whose options should be returned.
+ *
+ * The driver should:
+ * - Return an IPortableDeviceValues in WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, containing
+ * the relevant options.
+ */
+HRESULT WpdCapabilities::OnGetEventOptions(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ GUID Event = GUID_NULL;
+ CComPtr<IPortableDeviceValues> pOptions;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the event whose options have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetGuidValue(WPD_PROPERTY_CAPABILITIES_EVENT, &Event);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_CAPABILITIES_EVENT");
+ }
+
+ // CoCreate a collection to store the event options.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pOptions);
+ CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues");
+ }
+
+ // Add event options to the collection
+ if (hr == S_OK)
+ {
+ // If your driver supports event options, then they should be added here
+ // to the event options collection 'pOptions'.
+ }
+
+ // Set the WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS, pOptions);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_CAPABILITIES_EVENT_OPTIONS");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to populate supported PROPERTYKEYs for the
+ * specified object format.
+ *
+ * The parameters sent to us are:
+ * guidObjectFormat - object format whose supported properties are being requested.
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add supported PROPERTYKEYs pertaining to the specified object format.
+ */
+HRESULT WpdCapabilities::AddSupportedPropertyKeys(
+ _In_ REFGUID guidObjectFormat,
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ HRESULT hr = S_OK;
+
+ if (pKeys == NULL)
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ if (guidObjectFormat == WPD_OBJECT_FORMAT_TEXT)
+ {
+ AddCommonPropertyKeys(pKeys);
+ AddFilePropertyKeys(pKeys);
+ }
+ else if (guidObjectFormat == WPD_OBJECT_FORMAT_ALL)
+ {
+ AddCommonPropertyKeys(pKeys);
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to populate fixed property attributes
+ *
+ * The parameters sent to us are:
+ * guidObjectFormat - the object format whose property attributes are being requested.
+ * Key - the property whose attributes are being requested
+ * pAttributes - an IPortableDeviceValues which will contain the resulting property attributes
+ *
+ * The driver should:
+ * Read the property attributes for the specified property for the specified object format and
+ * populate pAttributes with the results.
+ */
+HRESULT WpdCapabilities::GetFixedPropertyAttributesForFormat(
+ _In_ REFGUID guidObjectFormat,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes)
+{
+ HRESULT hr = S_OK;
+
+ if (pAttributes == NULL)
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ UNREFERENCED_PARAMETER(guidObjectFormat);
+ UNREFERENCED_PARAMETER(Key);
+
+ //
+ // Since ALL of our properties have the same attributes, we are ignoring the
+ // passed in guidObjectFormat and Key parameters. These parameters allow you to
+ // customize fixed property attributes for properties for specific formats.
+ //
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM");
+ }
+
+ return hr;
+}
+
diff --git a/wpd/WpdMultiTransportDriver/WpdCapabilities.h b/wpd/WpdMultiTransportDriver/WpdCapabilities.h
new file mode 100644
index 00000000..ef2902ef
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdCapabilities.h
@@ -0,0 +1,64 @@
+#pragma once
+
+class WpdCapabilities
+{
+public:
+ WpdCapabilities();
+ virtual ~WpdCapabilities();
+
+ HRESULT Initialize();
+
+ 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:
+ HRESULT AddSupportedPropertyKeys(_In_ REFGUID guidObjectFormat,
+ _In_ IPortableDeviceKeyCollection* pKeys);
+
+ HRESULT GetFixedPropertyAttributesForFormat(_In_ REFGUID guidObjectFormat,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes);
+};
+
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.cpp b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.cpp
new file mode 100644
index 00000000..d027ba72
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.cpp
@@ -0,0 +1,61 @@
+#include "stdafx.h"
+#include "resource.h"
+#include "WpdMultiTransportDriver.h"
+
+#include "WpdMultiTransportDriver.tmh"
+
+HINSTANCE g_hInstance = NULL;
+
+class CWpdMultiTransportDriverModule : public CAtlDllModuleT< CWpdMultiTransportDriverModule >
+{
+public :
+ DECLARE_REGISTRY_APPID_RESOURCEID(IDR_WpdMultiTransportDriver, "{72D557A2-0914-454F-83A0-350530788B62}")
+ DECLARE_LIBID(LIBID_WpdMultiTransportDriverLib)
+};
+
+CWpdMultiTransportDriverModule _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/WpdMultiTransportDriver/WpdMultiTransportDriver.def b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.def
new file mode 100644
index 00000000..2ee10614
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.def
@@ -0,0 +1,9 @@
+; WpdMultiTransportDriver.def : Declares the module parameters.
+
+LIBRARY "WpdMultiTransportDriver.DLL"
+
+EXPORTS
+ DllCanUnloadNow PRIVATE
+ DllGetClassObject PRIVATE
+ DllRegisterServer PRIVATE
+ DllUnregisterServer PRIVATE
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.idl b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.idl
new file mode 100644
index 00000000..6b615e8f
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.idl
@@ -0,0 +1,24 @@
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+import "wudfddi.idl";
+
+[
+ uuid(EE383031-D737-45FD-BE5F-9A2EFE863627),
+ version(1.0),
+ helpstring("Windows Portable Device Multi-Transport Sample Driver Type Library")
+]
+library WpdMultiTransportDriverLib
+{
+ importlib("stdole2.tlb");
+ [
+ uuid(0CA6D3F4-9C49-4B81-BA8F-98F43AE6E592),
+ helpstring("WpdMultiTransportDriver Class")
+ ]
+ coclass WpdMultiTransportDriver
+ {
+ [default] interface IDriverEntry;
+ };
+};
+
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx
new file mode 100644
index 00000000..af3932d9
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.inx
@@ -0,0 +1,83 @@
+;
+; WpdMultiTransportDriver.inf
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=WPD
+ClassGuid={EEC5AD98-8080-425f-922A-DABF3DE3F69A}
+Provider=%Provider%
+CatalogFile=WpdMultiTransportDriver.cat
+DriverVer=01/24/2005,1.1.1.1
+
+[Manufacturer]
+%MSFTWUDF%=Microsoft,NT$ARCH$
+
+[Microsoft.NT$ARCH$]
+%BasicDeviceName%=Basic_Install,WUDF\MultiTransport
+
+[SourceDisksFiles]
+WudfUpdate_$UMDFCOINSTALLERVERSION$.dll=1
+WpdMultiTransportDriver.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=WpdMultiTransportDriver, WpdMultiTransportDriver_Install
+UmdfServiceOrder=WpdMultiTransportDriver
+UmdfKernelModeClientPolicy=AllowKernelModeClients
+
+[CoInstallers_CopyFiles]
+WudfUpdate_$UMDFCOINSTALLERVERSION$.dll
+
+[WpdMultiTransportDriver_Install]
+UmdfLibraryVersion=$UMDFVERSION$
+DriverCLSID="{0CA6D3F4-9C49-4B81-BA8F-98F43AE6E592}"
+ServiceBinary=%12%\UMDF\WpdMultiTransportDriver.dll
+
+[Device_AddReg]
+; Enable WIA support for legacy WIA applications
+HKR,,"EnableLegacySupport",0x10001,1
+
+; 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]
+WpdMultiTransportDriver.dll
+
+
+; =================== Generic ==================================
+
+[Strings]
+MSFTWUDF="Microsoft Windows Portable Devices"
+Provider="Microsoft WPD"
+MediaDescription="Windows Portable Device Multi-Transport Sample Driver Installation Media"
+BasicDeviceName="Windows Portable Device Multi-Transport Sample Driver"
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rc b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rc
new file mode 100644
index 00000000..c7b89e95
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rc
@@ -0,0 +1,15 @@
+#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 Multi-Transport Sample Driver"
+#define VER_INTERNALNAME_STR "WpdMultiTransportDriver.dll"
+
+#include <common.ver>
+
+1 TYPELIB "WpdMultiTransportDriver.tlb"
+
+IDR_WpdMultiTransportDriver REGISTRY "WpdMultiTransportDriver.rgs"
+
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rgs b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rgs
new file mode 100644
index 00000000..3bd8066c
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.rgs
@@ -0,0 +1,26 @@
+HKCR
+{
+ WpdMultiTransportDriver.WpdMultiTransportDriver.1 = s 'WpdMultiTransportDriver Class'
+ {
+ CLSID = s '{0CA6D3F4-9C49-4B81-BA8F-98F43AE6E592}'
+ }
+ WpdMultiTransportDriver.WpdMultiTransportDriver = s 'WpdMultiTransportDriver Class'
+ {
+ CLSID = s '{0CA6D3F4-9C49-4B81-BA8F-98F43AE6E592}'
+ CurVer = s 'WpdMultiTransportDriver.WpdMultiTransportDriver.1'
+ }
+ NoRemove CLSID
+ {
+ ForceRemove {0CA6D3F4-9C49-4B81-BA8F-98F43AE6E592} = s 'WpdMultiTransportDriver Class'
+ {
+ ProgID = s 'WpdMultiTransportDriver.WpdMultiTransportDriver.1'
+ VersionIndependentProgID = s 'WpdMultiTransportDriver.WpdMultiTransportDriver.1'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Free'
+ }
+ 'TypeLib' = s '{EE383031-D737-45FD-BE5F-9A2EFE863627}'
+ }
+ }
+}
+
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.sln
new file mode 100644
index 00000000..a1edc764
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.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}") = "WpdMultiTransportDriver", "WpdMultiTransportDriver.vcxproj", "{37907AAA-43FE-46C8-A20E-522C4B9F6502}"
+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
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Debug|Win32.ActiveCfg = Debug|Win32
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Debug|Win32.Build.0 = Debug|Win32
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Release|Win32.ActiveCfg = Release|Win32
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Release|Win32.Build.0 = Release|Win32
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Debug|x64.ActiveCfg = Debug|x64
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Debug|x64.Build.0 = Debug|x64
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Release|x64.ActiveCfg = Release|x64
+ {37907AAA-43FE-46C8-A20E-522C4B9F6502}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj
new file mode 100644
index 00000000..046e9ebf
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj
@@ -0,0 +1,344 @@
+<?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>{37907AAA-43FE-46C8-A20E-522C4B9F6502}</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>{28A8601E-0288-45E2-A8FF-B23C3D163E06}</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="WpdMultiTransportDriver.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="Driver.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="Device.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="Queue.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="WpdBaseDriver.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="WpdObjectEnum.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="WpdObjectProperties.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="WpdObjectResources.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <ClCompile Include="WpdCapabilities.cpp">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ <AdditionalIncludeDirectories>;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreCompiledHeaderFile>Stdafx.h</PreCompiledHeaderFile>
+ <PreCompiledHeader>Use</PreCompiledHeader>
+ <PreCompiledHeaderOutputFile>$(IntDir)\Stdafx.h.pch</PreCompiledHeaderOutputFile>
+ </ClCompile>
+ <Inf Include="WpdMultiTransportDriver.inx">
+ <Architecture>$(InfArch)</Architecture>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <CopyOutput>.\$(IntDir)\WpdMultiTransportDriver.inf</CopyOutput>
+ </Inf>
+ <OtherWpp Include="WpdMultiTransportDriver.rc; WpdMultiTransportDriver.idl">
+ <WppEnabled>true</WppEnabled>
+ <WppDllMacro>true</WppDllMacro>
+ <WppScanConfigurationData>stdafx.h</WppScanConfigurationData>
+ </OtherWpp>
+ </ItemGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <TargetName>WpdMultiTransportDriver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <TargetName>WpdMultiTransportDriver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <TargetName>WpdMultiTransportDriver</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <TargetName>WpdMultiTransportDriver</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>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <TreatWarningAsError>true</TreatWarningAsError>
+ <WarningLevel>Level4</WarningLevel>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Midl>
+ <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SDK_INC_PATH);$(DDK_INC_PATH)</AdditionalIncludeDirectories>
+ </Midl>
+ <Link>
+ <AdditionalDependencies>%(AdditionalDependencies);$(SDK_LIB_PATH)\strsafe.lib;$(SDK_LIB_PATH)\kernel32.lib;$(SDK_LIB_PATH)\ole32.lib;$(SDK_LIB_PATH)\oleaut32.lib;$(SDK_LIB_PATH)\uuid.lib;$(SDK_LIB_PATH)\user32.lib;$(SDK_LIB_PATH)\advapi32.lib;$(SDK_LIB_PATH)\shlwapi.lib;$(SDK_LIB_PATH)\PortableDeviceGuids.lib</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Link>
+ <ModuleDefinitionFile>WpdMultiTransportDriver.def</ModuleDefinitionFile>
+ </Link>
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Link>
+ <ModuleDefinitionFile>WpdMultiTransportDriver.def</ModuleDefinitionFile>
+ </Link>
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Link>
+ <ModuleDefinitionFile>WpdMultiTransportDriver.def</ModuleDefinitionFile>
+ </Link>
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Link>
+ <ModuleDefinitionFile>WpdMultiTransportDriver.def</ModuleDefinitionFile>
+ </Link>
+ <ClCompile>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ </ClCompile>
+ </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="WpdMultiTransportDriver.idl" />
+ <ResourceCompile Include="WpdMultiTransportDriver.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/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters
new file mode 100644
index 00000000..e67dfaf4
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdMultiTransportDriver.vcxproj.Filters
@@ -0,0 +1,77 @@
+<?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>{27130F09-0148-4D17-AE7E-2E9EC7C0E975}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ <UniqueIdentifier>{6D2BBEB1-D05B-4EE1-AD68-9D02DA2AD058}</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>{0FD7410D-0B64-463C-97F0-1766C80EC6AA}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Driver Files">
+ <Extensions>inf;inv;inx;mof;mc;</Extensions>
+ <UniqueIdentifier>{6307E7D9-A903-4A0E-ACF9-24B41E12D183}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="Device.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Driver.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="WpdMultiTransportDriver.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WpdObjectEnum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WpdObjectProperties.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WpdObjectResources.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <Midl Include="WpdMultiTransportDriver.idl">
+ <Filter>Source Files</Filter>
+ </Midl>
+ <None Include="WpdMultiTransportDriver.def">
+ <Filter>Source Files</Filter>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include=".\Debug\\WpdMultiTransportDriver.inf">
+ <Filter>Driver Files</Filter>
+ </FilesToPackage>
+ <Inf Include="WpdMultiTransportDriver.inx">
+ <Filter>Driver Files</Filter>
+ </Inf>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="WpdMultiTransportDriver.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/WpdMultiTransportDriver/WpdObjectEnum.cpp b/wpd/WpdMultiTransportDriver/WpdObjectEnum.cpp
new file mode 100644
index 00000000..1fb4cab3
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectEnum.cpp
@@ -0,0 +1,416 @@
+#include "stdafx.h"
+#include "WpdObjectEnum.tmh"
+
+WpdObjectEnumerator::WpdObjectEnumerator()
+{
+
+}
+
+WpdObjectEnumerator::~WpdObjectEnumerator()
+{
+
+}
+
+HRESULT WpdObjectEnumerator::DispatchWpdMessage(_In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+
+ if (Command.fmtid != WPD_CATEGORY_OBJECT_ENUMERATION)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid));
+ }
+
+ if (hr == S_OK)
+ {
+ if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_START_FIND.pid)
+ {
+ hr = OnStartFind(pParams, pResults);
+ CHECK_HR(hr, "Failed to begin enumeration");
+ }
+ else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_FIND_NEXT.pid)
+ {
+ hr = OnFindNext(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to find next object");
+ }
+ }
+ else if (Command.pid == WPD_COMMAND_OBJECT_ENUMERATION_END_FIND.pid)
+ {
+ hr = OnEndFind(pParams, pResults);
+ CHECK_HR(hr, "Failed to end enumeration");
+ }
+ else
+ {
+ hr = E_NOTIMPL;
+ CHECK_HR(hr, "This object does not support this command id %d", Command.pid);
+ }
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_START_FIND
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID: the parent where we should start
+ * the enumeration.
+ * - WPD_PROPERTY_OBJECT_ENUMERATION_FILTER: the filter to use when doing
+ * enumeration. Since this parameter is optional, it may not exist.
+ * This driver currently ignores the filter parameter.
+ *
+ * The driver should:
+ * - Create a new context for this enumeration.
+ * - Set the string identifier in WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT for the newly created enumeration context.
+ * This value will be passed back during OnFindNext and OnEndFind.
+ */
+HRESULT WpdObjectEnumerator::OnStartFind(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszParentID = NULL;
+ ContextMap* pContextMap = NULL;
+ CAtlStringW strEnumContext;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier of the parent where the enumeration is starting from.
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID, &wszParentID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_PARENT_ID");
+ }
+
+ // Get the client context map so we can store an enumeration context for this enumeration
+ // operation.
+ if (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 and initialize a new enumeration context.
+ // Add the new enumertion context to the client context map. This context is used to
+ // keep track of this particular enumeration operation.
+ if (hr == S_OK)
+ {
+ WpdObjectEnumeratorContext* pEnumeratorContext = new WpdObjectEnumeratorContext();
+ if (pEnumeratorContext != NULL)
+ {
+ // Initialize the enumeration context
+ InitializeEnumerationContext(pEnumeratorContext, wszParentID);
+
+ // Add the enumeration context to the client context map.
+ pContextMap->Add(pEnumeratorContext, strEnumContext);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate enumeration context");
+ }
+ SAFE_RELEASE(pEnumeratorContext);
+ }
+
+ // Set the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT value in the results.
+ // This context identifier will be passed back during OnFindNext and OnEndFind to allow the driver to access it.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, strEnumContext);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszParentID);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+HRESULT WpdObjectEnumerator::OnFindNext(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszEnumContext = NULL;
+ DWORD dwNumObjectsRequested = 0;
+ ContextMap* pContextMap = NULL;
+ WpdObjectEnumeratorContext* pEnumeratorContext = NULL;
+ DWORD NumObjectsEnumerated = 0;
+
+ CComPtr<IPortableDevicePropVariantCollection> pObjectIDCollection;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the enumeration context identifier for this enumeration operation.
+ // The enumeration context identifier is needed to lookup the specific
+ // enumeration context in the client context map for this enumeration operation.
+ // NOTE that more than one enumeration may be in progress.
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT");
+ }
+
+ // Get the number of objects requested for this enumeration call.
+ // The driver should always attempt to meet this requested value.
+ // If there are fewer children than requested, the driver should return the remaining
+ // children and a return code of S_FALSE.
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED, &dwNumObjectsRequested);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_NUM_OBJECTS_REQUESTED");
+ }
+
+ // Get the client context map so we can retrieve the enumeration context for this enumeration
+ // operation.
+ 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");
+ }
+
+ if (hr == S_OK)
+ {
+ pEnumeratorContext = (WpdObjectEnumeratorContext*)pContextMap->GetContext(wszEnumContext);
+ if (pEnumeratorContext == NULL)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing enumeration context");
+ }
+ }
+
+ // CoCreate a collection to store the object identifiers being returned for this enumeration call.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDevicePropVariantCollection,
+ (VOID**) &pObjectIDCollection);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
+ }
+
+ // If the enumeration context reports that their are more objects to return, then continue, if not,
+ // return an empty results set.
+ if ((hr == S_OK) && (pEnumeratorContext != NULL) && pEnumeratorContext->HasMoreChildrenToEnumerate())
+ {
+ if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(L"") == 0)
+ {
+ // We are being asked for the WPD_DEVICE_OBJECT_ID
+ hr = AddStringValueToPropVariantCollection(pObjectIDCollection, WPD_DEVICE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to add 'DEVICE' object ID to enumeration collection");
+
+ // Update the the number of children we are returning for this enumeration call
+ NumObjectsEnumerated++;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
+ {
+ // We are being asked for direct children of the WPD_DEVICE_OBJECT_ID
+ hr = AddStringValueToPropVariantCollection(pObjectIDCollection, STORAGE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to add storage object ID to enumeration collection");
+
+ // Update the the number of children we are returning for this enumeration call
+ NumObjectsEnumerated++;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(STORAGE_OBJECT_ID) == 0)
+ {
+ // We are being asked for direct children of the STORAGE_OBJECT_ID
+ hr = AddStringValueToPropVariantCollection(pObjectIDCollection, DOCUMENTS_FOLDER_OBJECT_ID);
+ CHECK_HR(hr, "Failed to add documents folder object ID to enumeration collection");
+
+ // Update the the number of children we are returning for this enumeration call
+ NumObjectsEnumerated++;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(DOCUMENTS_FOLDER_OBJECT_ID) == 0)
+ {
+ // We are being asked for direct children of the DOCUMENTS_FOLDER_OBJECT_ID
+ hr = AddStringValueToPropVariantCollection(pObjectIDCollection, README_FILE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to add documents readme text file object ID to enumeration collection");
+
+ // Update the the number of children we are returning for this enumeration call
+ NumObjectsEnumerated++;
+ }
+ }
+
+ // Set the collection of object identifiers enumerated in the results
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS, pObjectIDCollection);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_ENUMERATION_OBJECT_IDS");
+ }
+
+ // If the enumeration context reports that their are no more objects to return then return S_FALSE indicating to the
+ // caller that we are finished.
+ if (hr == S_OK)
+ {
+ if (pEnumeratorContext != NULL)
+ {
+ // Update the number of children we have enumerated and returned to the caller
+ pEnumeratorContext->m_ChildrenEnumerated += NumObjectsEnumerated;
+
+ // Check the number requested against the number enumerated and set the HRESULT
+ // accordingly.
+ if (NumObjectsEnumerated < dwNumObjectsRequested)
+ {
+ // We returned less than the number of objects requested to the caller
+ hr = S_FALSE;
+ }
+ else
+ {
+ // We returned exactly the number of objects requested to the caller
+ hr = S_OK;
+ }
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszEnumContext);
+
+ SAFE_RELEASE(pContextMap);
+ SAFE_RELEASE(pEnumeratorContext);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_ENUMERATION_END_FIND
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT: the context the driver returned to
+ * the client in OnStartFind.
+ *
+ * The driver should:
+ * - Destroy any data associated with this context.
+ */
+HRESULT WpdObjectEnumerator::OnEndFind(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszEnumContext = NULL;
+ ContextMap* pContextMap = NULL;
+
+ UNREFERENCED_PARAMETER(pResults);
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the enumeration context identifier for this enumeration operation. We will
+ // need this to lookup the specific enumeration context in the client context map.
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT, &wszEnumContext);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT");
+ }
+
+ // Get the client context map so we can retrieve the enumeration context for this enumeration
+ // operation using the WPD_PROPERTY_OBJECT_ENUMERATION_CONTEXT property value obtained above.
+ if (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");
+ }
+
+ // Destroy any data allocated/associated with the enumeration context and then remove it from the context map.
+ // We no longer need to keep this context around because the enumeration has been ended.
+ if (hr == S_OK)
+ {
+ pContextMap->Remove(wszEnumContext);
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszEnumContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+// Initialize the enumeration context
+VOID WpdObjectEnumerator::InitializeEnumerationContext(
+ _In_ WpdObjectEnumeratorContext* pEnumeratorContext,
+ _In_ LPCWSTR wszParentObjectID)
+{
+ if (pEnumeratorContext == NULL)
+ {
+ return;
+ }
+
+ // Initialize the enumeration context with the parent object identifier
+ pEnumeratorContext->m_strParentObjectID = wszParentObjectID;
+
+ // Our sample driver has a very simple object structure where we know
+ // how many children are under each parent.
+ // The eumeration context is initialized below with this information.
+ if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(L"") == 0)
+ {
+ // Clients passing an 'empty' string for the parent are asking for the
+ // 'DEVICE' object. We should return 1 child in this case.
+ pEnumeratorContext->m_TotalChildren = 1;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
+ {
+ // The device object contains 1 child (the storage object).
+ pEnumeratorContext->m_TotalChildren = 1;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(STORAGE_OBJECT_ID) == 0)
+ {
+ // The storage object contains 1 child (the documents folder object).
+ pEnumeratorContext->m_TotalChildren = 1;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(DOCUMENTS_FOLDER_OBJECT_ID) == 0)
+ {
+ // The documents folder object contains 1 child (the readme text file object).
+ pEnumeratorContext->m_TotalChildren = 1;
+ }
+ else if (pEnumeratorContext->m_strParentObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0)
+ {
+ // The readme text file object contains no children.
+ pEnumeratorContext->m_TotalChildren = 0;
+ }
+ else
+ {
+ // Invalid, or non-existing objects contain no children.
+ pEnumeratorContext->m_TotalChildren = 0;
+ }
+}
+
+HRESULT WpdObjectEnumerator::AddStringValueToPropVariantCollection(
+ _In_ IPortableDevicePropVariantCollection* pCollection,
+ _In_ LPCWSTR wszValue)
+{
+ HRESULT hr = S_OK;
+
+ if ((pCollection == NULL) ||
+ (wszValue == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ PROPVARIANT pv = {0};
+ PropVariantInit(&pv);
+
+ pv.vt = VT_LPWSTR;
+ pv.pwszVal = (LPWSTR)wszValue;
+
+ // The wszValue will be copied into the collection, keeping the ownership
+ // of the string belonging to the caller.
+ // Don't call PropVariantClear, since we did not allocate the memory for these string values
+
+ hr = pCollection->Add(&pv);
+
+ return hr;
+}
+
diff --git a/wpd/WpdMultiTransportDriver/WpdObjectEnum.h b/wpd/WpdMultiTransportDriver/WpdObjectEnum.h
new file mode 100644
index 00000000..8d38526b
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectEnum.h
@@ -0,0 +1,103 @@
+#pragma once
+
+// This class is used to store the context for a specific enumeration.
+class WpdObjectEnumeratorContext : public IUnknown
+{
+public:
+ WpdObjectEnumeratorContext() :
+ m_cRef(1),
+ m_TotalChildren(0),
+ m_ChildrenEnumerated(0)
+ {
+
+ }
+
+ ~WpdObjectEnumeratorContext()
+ {
+
+ }
+
+public: // IUnknown
+ ULONG __stdcall AddRef()
+ {
+ InterlockedIncrement((long*) &m_cRef);
+ return m_cRef;
+ }
+
+ _At_(this, __drv_freesMem(Mem))
+ ULONG __stdcall Release()
+ {
+ ULONG ulRefCount = m_cRef - 1;
+
+ if (InterlockedDecrement((long*) &m_cRef) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return ulRefCount;
+ }
+
+ HRESULT __stdcall QueryInterface(
+ REFIID riid,
+ void** ppv)
+ {
+ HRESULT hr = S_OK;
+
+ if(riid == IID_IUnknown)
+ {
+ *ppv = static_cast<IUnknown*>(this);
+ AddRef();
+ }
+ else
+ {
+ *ppv = NULL;
+ hr = E_NOINTERFACE;
+ }
+
+ return hr;
+ }
+
+private:
+ DWORD m_cRef;
+
+public:
+ bool HasMoreChildrenToEnumerate()
+ {
+ return ((m_TotalChildren - m_ChildrenEnumerated) > 0);
+ }
+
+// WpdObjectEnumeratorContext specific data
+public:
+ CAtlStringW m_strParentObjectID; // object identifier of the object whose children are being enumerated
+ DWORD m_TotalChildren; // number of bytes transferred from the resource to the caller
+ DWORD m_ChildrenEnumerated; // number of children returned during the enumeration operation
+};
+
+class WpdObjectEnumerator
+{
+public:
+ WpdObjectEnumerator();
+ virtual ~WpdObjectEnumerator();
+
+ HRESULT 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:
+ VOID InitializeEnumerationContext(
+ _In_ WpdObjectEnumeratorContext* pEnumeratorContext,
+ _In_ LPCWSTR wszParentObjectID);
+
+ HRESULT AddStringValueToPropVariantCollection(
+ _In_ IPortableDevicePropVariantCollection* pCollection,
+ _In_ LPCWSTR wszValue);
+};
diff --git a/wpd/WpdMultiTransportDriver/WpdObjectProperties.cpp b/wpd/WpdMultiTransportDriver/WpdObjectProperties.cpp
new file mode 100644
index 00000000..79dc00b4
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectProperties.cpp
@@ -0,0 +1,1421 @@
+#include "stdafx.h"
+#include "WpdObjectProperties.tmh"
+
+const PROPERTYKEY g_SupportedCommonProperties[] =
+{
+ WPD_OBJECT_ID,
+ WPD_OBJECT_PERSISTENT_UNIQUE_ID,
+ WPD_OBJECT_PARENT_ID,
+ WPD_OBJECT_NAME,
+ WPD_OBJECT_FORMAT,
+ WPD_OBJECT_CONTENT_TYPE,
+ WPD_OBJECT_CAN_DELETE,
+};
+
+const PROPERTYKEY g_SupportedDeviceProperties[] =
+{
+ WPD_DEVICE_FIRMWARE_VERSION,
+ WPD_DEVICE_POWER_LEVEL,
+ WPD_DEVICE_POWER_SOURCE,
+ WPD_DEVICE_PROTOCOL,
+ WPD_DEVICE_MODEL,
+ WPD_DEVICE_SERIAL_NUMBER,
+ WPD_DEVICE_SUPPORTS_NON_CONSUMABLE,
+ WPD_DEVICE_MANUFACTURER,
+ WPD_DEVICE_FRIENDLY_NAME,
+ WPD_DEVICE_TYPE,
+ WPD_FUNCTIONAL_OBJECT_CATEGORY,
+};
+
+const PROPERTYKEY g_SupportedStorageProperties[] =
+{
+ WPD_STORAGE_TYPE,
+ WPD_STORAGE_FILE_SYSTEM_TYPE,
+ WPD_STORAGE_CAPACITY,
+ WPD_STORAGE_FREE_SPACE_IN_BYTES,
+ WPD_STORAGE_SERIAL_NUMBER,
+ WPD_STORAGE_DESCRIPTION,
+ WPD_FUNCTIONAL_OBJECT_CATEGORY,
+};
+
+const PROPERTYKEY g_SupportedCommonFileProperties[] =
+{
+ WPD_OBJECT_ORIGINAL_FILE_NAME,
+ WPD_OBJECT_SIZE,
+ WPD_OBJECT_DATE_MODIFIED,
+ WPD_OBJECT_DATE_CREATED,
+};
+
+const PROPERTYKEY g_SupportedCommonFolderProperties[] =
+{
+ WPD_OBJECT_ORIGINAL_FILE_NAME,
+ WPD_OBJECT_DATE_MODIFIED,
+ WPD_OBJECT_DATE_CREATED,
+};
+
+WpdObjectProperties::WpdObjectProperties()
+{
+
+}
+
+WpdObjectProperties::~WpdObjectProperties()
+{
+
+}
+
+HRESULT WpdObjectProperties::DispatchWpdMessage(
+ _In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+
+ if (Command.fmtid != WPD_CATEGORY_OBJECT_PROPERTIES)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid));
+ }
+
+ if (hr == S_OK)
+ {
+ if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED))
+ {
+ hr = OnGetSupportedProperties(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported properties");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET))
+ {
+ hr = OnGetPropertyValues(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to get properties");
+ }
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL))
+ {
+ hr = OnGetAllPropertyValues(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to get all properties");
+ }
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_SET))
+ {
+ hr = OnSetPropertyValues(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to set properties");
+ }
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES))
+ {
+ hr = OnGetPropertyAttributes(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to get property attributes");
+ }
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_PROPERTIES_DELETE))
+ {
+ hr = OnDeleteProperties(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to delete properties");
+ }
+ }
+ else
+ {
+ hr = E_NOTIMPL;
+ CHECK_HR(hr, "This object does not support this command id %d", Command.pid);
+ }
+ }
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have
+ * been requested.
+ *
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties.
+ * Since this parameter is optional, it may not exist.
+ * ! This driver currently ignores the filter parameter. !
+ *
+ * The driver should:
+ * - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS
+ */
+HRESULT WpdObjectProperties::OnGetSupportedProperties(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier whose supported properties have been requested
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing string value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
+ }
+
+ // CoCreate a collection to store the supported property keys.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceKeyCollection,
+ (VOID**) &pKeys);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
+ }
+
+ // Add supported property keys for the specified object to the collection
+ if (hr == S_OK)
+ {
+ hr = AddSupportedPropertyKeys(wszObjectID, pKeys);
+ CHECK_HR(hr, "Failed to add supported property keys for object '%ws'", wszObjectID);
+ }
+
+ // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS value in the results.
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, pKeys);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which
+ * specific property values we are requested to return.
+ *
+ * The driver should:
+ * - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be
+ * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
+ * - S_OK should be returned if all properties were read successfully.
+ * - S_FALSE should be returned if any property read failed.
+ * - Any error return indicates that the driver did not fill in any results, and the caller will
+ * not attempt to unpack any property values.
+ */
+HRESULT WpdObjectProperties::OnGetPropertyValues(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ CComPtr<IPortableDeviceValues> pValues;
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier whose property values have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
+ }
+
+ // Get the list of property keys for the property values the caller wants to retrieve from the specified object
+ if (hr == S_OK)
+ {
+ hr = pParams->GetIPortableDeviceKeyCollectionValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &pKeys);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS");
+ }
+
+ // CoCreate a collection to store the property values.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pValues);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // Read the specified properties on the specified object and add the property values to the collection.
+ if (hr == S_OK)
+ {
+ hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues);
+ CHECK_HR(hr, "Failed to get property values for object '%ws'", wszObjectID);
+ }
+
+ // S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ).
+ // S_FALSE means that 1 or more property values could not be retrieved successfully.
+ // The value for the specified property should be set to an error HRESULT of
+ // the reason why the property could not be read.
+ // (e.g. If the property being requested is not supported on the object then an error of
+ // HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) should be set as the value.
+ if (SUCCEEDED(hr))
+ {
+ // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results.
+ HRESULT hrTemp = S_OK;
+ hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"));
+
+ if(FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
+ *
+ * The driver should:
+ * - Return all property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES. If any property read failed, the corresponding value should be
+ * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
+ * - S_OK should be returned if all properties were read successfully.
+ * - S_FALSE should be returned if any property read failed.
+ * - Any error return indicates that the driver did not fill in any results, and the caller will
+ * not attempt to unpack any property values.
+ */
+HRESULT WpdObjectProperties::OnGetAllPropertyValues(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ CComPtr<IPortableDeviceValues> pValues;
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier whose property values have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
+ }
+
+ // CoCreate a collection to store the property values.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pValues);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // CoCreate a collection to store the property keys we are going to use
+ // to request the property values of.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceKeyCollection,
+ (VOID**) &pKeys);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
+ }
+
+ // First we make a request for ALL supported property keys for the specified object.
+ // Next, we delegate to our helper function GetPropertyValuesForObject( ) passing
+ // the entire property key collection. This will reuse existing implementation
+ // in our driver to perform the GetAllPropertyValues operation.
+ if (hr == S_OK)
+ {
+ hr = AddSupportedPropertyKeys(wszObjectID, pKeys);
+ CHECK_HR(hr, "Failed to get ALL supported properties for object '%ws'", wszObjectID);
+ if (hr == S_OK)
+ {
+ hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues);
+ CHECK_HR(hr, "Failed to get property values for object '%ws'", wszObjectID);
+ }
+ }
+
+ // S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ).
+ // S_FALSE means that 1 or more property values could not be retrieved successfully.
+ // The value for the specified property key should be set to the error HRESULT of
+ // the reason why the property could not be read.
+ // (i.e. an error of HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) if a property value was
+ // requested and is not supported by the specified object.)
+ if (SUCCEEDED(hr))
+ {
+ // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results
+ HRESULT hrTemp = S_OK;
+ hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"));
+
+ if(FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return.
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which
+ * specific property values we are requested to write.
+ *
+ * The driver should:
+ * - Write all requested property values. For each property, a write result should be returned in the
+ * write result property store.
+ * - If any property write failed, the corresponding write result value should be
+ * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
+ * - S_OK should be returned if all properties were written successfully.
+ * - S_FALSE should be returned if any property write failed.
+ * - Any error return indicates that the driver did not write any results, and the caller will
+ * not attempt to unpack any property write results.
+ */
+HRESULT WpdObjectProperties::OnSetPropertyValues(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ DWORD cValues = 0;
+ CComPtr<IPortableDeviceValues> pValues;
+ CComPtr<IPortableDeviceValues> pWriteResults;
+ CComPtr<IPortableDeviceValues> pEventParams;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier whose property values are being set
+ if (hr == S_OK)
+ {
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
+ }
+
+ // Get the caller-supplied property values requested to be set on the object
+ if (hr == S_OK)
+ {
+ hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES");
+ }
+
+ // CoCreate a collection to store the property set operation results.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pWriteResults);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // Set the property values on the specified object
+ if (hr == S_OK)
+ {
+ // Since this driver does not support setting any properties, all property set operation
+ // results will be set to E_ACCESSDENIED.
+ if (hr == S_OK)
+ {
+ hr = pValues->GetCount(&cValues);
+ CHECK_HR(hr, "Failed to get total number of values");
+ }
+
+ if (hr == S_OK)
+ {
+ for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++)
+ {
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ hr = pValues->GetAt(dwIndex, &Key, NULL);
+ CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d", dwIndex);
+
+ if (hr == S_OK)
+ {
+ hr = pWriteResults->SetErrorValue(Key, E_ACCESSDENIED);
+ CHECK_HR(hr, "Failed to set error result value at index %d", dwIndex);
+ }
+ }
+ }
+
+ // Since we have set failures for the property set operations we must let the application
+ // know by returning S_FALSE. This will instruct the application to look at the
+ // property set operation results for failure values.
+ if (hr == S_OK)
+ {
+ hr = S_FALSE;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS value in the results
+ HRESULT hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS, pWriteResults);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS"));
+
+ if (FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property attributes we want to return.
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys containing a single value,
+ * which is the key identifying the specific property attributes we are requested to return.
+ *
+ * The driver should:
+ * - Return the requested property attributes. If any property attributes failed to be retrieved,
+ * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the
+ * HRESULT reason for the failure.
+ * - S_OK should be returned if all property attributes were read successfully.
+ * - S_FALSE should be returned if any property attribute failed.
+ * - Any error return indicates that the driver did not fill in any results, and the caller will
+ * not attempt to unpack any property values.
+ */
+HRESULT WpdObjectProperties::OnGetPropertyAttributes(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ CComPtr<IPortableDeviceValues> pAttributes;
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the object identifier whose property attributes have been requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID");
+ }
+
+ // Get the list of property keys whose attributes are being requested
+ if (hr == S_OK)
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &Key);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS");
+ }
+
+ // CoCreate a collection to store the property attributes.
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pAttributes);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ // Get the attributes for the specified properties on the specified object and add them
+ // to the collection.
+ if (hr == S_OK)
+ {
+ hr = GetPropertyAttributesForObject(wszObjectID, Key, pAttributes);
+ CHECK_HR(hr, "Failed to get property attributes");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES value in the results
+ HRESULT hrTemp = S_OK;
+ hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributes);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES"));
+
+ if(FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_DELETE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose properties should be deleted.
+ * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys indicating which
+ * properties to delete.
+ *
+ * The driver should:
+ * - Delete the specified properties from the object.
+ * - S_OK should be returned if all specified properties were successfully deleted.
+ * - E_ACCESSDENIED should be returned if the client attempts to delete a property which is not deletable (i.e.
+ * WPD_PROPERTY_ATTRIBUTE_CAN_DELETE is FALSE for that property.)
+ */
+HRESULT WpdObjectProperties::OnDeleteProperties(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = E_ACCESSDENIED;
+
+ UNREFERENCED_PARAMETER(pParams);
+ UNREFERENCED_PARAMETER(pResults);
+
+ // This driver has no properties which can be deleted.
+
+ return hr;
+}
+
+/**
+ * This method is called to populate supported PROPERTYKEYs found on objects.
+ *
+ * The parameters sent to us are:
+ * wszObjectID - the object whose supported property keys are being requested
+ * pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the specified object.
+ */
+HRESULT AddSupportedPropertyKeys(
+ _In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ HRESULT hr = S_OK;
+ CAtlStringW strObjectID = wszObjectID;
+
+ // Add Common PROPERTYKEYs for ALL WPD objects
+ AddCommonPropertyKeys(pKeys);
+
+ if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
+ {
+ // Add the PROPERTYKEYs for the 'DEVICE' object
+ AddDevicePropertyKeys(pKeys);
+ }
+
+ if (strObjectID.CompareNoCase(STORAGE_OBJECT_ID) == 0)
+ {
+ // Add the PROPERTYKEYs for the storage object
+ AddStoragePropertyKeys(pKeys);
+ }
+
+ if (strObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0)
+ {
+ // Add the PROPERTYKEYs for the file object
+ AddFilePropertyKeys(pKeys);
+ }
+
+ if (strObjectID.CompareNoCase(DOCUMENTS_FOLDER_OBJECT_ID) == 0)
+ {
+ // Add the PROPERTYKEYs for the folder object
+ AddFolderPropertyKeys(pKeys);
+ }
+
+ // Add other PROPERTYKEYs for other supported objects...
+
+ return hr;
+}
+
+/**
+ * This method is called to populate common PROPERTYKEYs found on ALL objects.
+ *
+ * The parameters sent to us are:
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the ALL objects.
+ */
+VOID AddCommonPropertyKeys(
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ if (pKeys != NULL)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++)
+ {
+ HRESULT hr = S_OK;
+ hr = pKeys->Add(g_SupportedCommonProperties[dwIndex] );
+ CHECK_HR(hr, "Failed to add common property");
+ }
+ }
+}
+
+/**
+ * This method is called to populate common PROPERTYKEYs found on the DEVICE object.
+ *
+ * The parameters sent to us are:
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the DEVICE object.
+ */
+VOID AddDevicePropertyKeys(
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ if (pKeys != NULL)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++)
+ {
+ HRESULT hr = S_OK;
+ hr = pKeys->Add(g_SupportedDeviceProperties[dwIndex] );
+ CHECK_HR(hr, "Failed to add device property");
+ }
+ }
+}
+
+/**
+ * This method is called to populate common PROPERTYKEYs found on storage objects.
+ *
+ * The parameters sent to us are:
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the storage objects.
+ */
+VOID AddStoragePropertyKeys(
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ if (pKeys != NULL)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedStorageProperties); dwIndex++)
+ {
+ HRESULT hr = S_OK;
+ hr = pKeys->Add(g_SupportedStorageProperties[dwIndex] );
+ CHECK_HR(hr, "Failed to add storage property");
+ }
+ }
+}
+
+/**
+ * This method is called to populate common PROPERTYKEYs found on file objects.
+ *
+ * The parameters sent to us are:
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the file objects.
+ */
+VOID AddFilePropertyKeys(
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ if (pKeys != NULL)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonFileProperties); dwIndex++)
+ {
+ HRESULT hr = S_OK;
+ hr = pKeys->Add(g_SupportedCommonFileProperties[dwIndex] );
+ CHECK_HR(hr, "Failed to add common file property");
+ }
+ }
+}
+
+/**
+ * This method is called to populate common PROPERTYKEYs found on folder objects.
+ *
+ * The parameters sent to us are:
+ * pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the file objects.
+ */
+VOID AddFolderPropertyKeys(
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ if (pKeys != NULL)
+ {
+ for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonFolderProperties); dwIndex++)
+ {
+ HRESULT hr = S_OK;
+ hr = pKeys->Add(g_SupportedCommonFolderProperties[dwIndex] );
+ CHECK_HR(hr, "Failed to add common folder property");
+ }
+ }
+}
+
+/**
+ * This method is called to populate property values for the object specified.
+ *
+ * The parameters sent to us are:
+ * wszObjectID - the object whose properties are being requested.
+ * pKeys - the list of property keys of the properties to request from the object
+ * pValues - an IPortableDeviceValues which will contain the property values retreived from the object
+ *
+ * The driver should:
+ * Read the specified properties for the specified object and populate pValues with the
+ * results.
+ */
+HRESULT WpdObjectProperties::GetPropertyValuesForObject(
+ _In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys,
+ _In_ IPortableDeviceValues* pValues)
+{
+ HRESULT hr = S_OK;
+ CAtlStringW strObjectID = wszObjectID;
+ DWORD cKeys = 0;
+
+ if ((wszObjectID == NULL) ||
+ (pKeys == NULL) ||
+ (pValues == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ hr = pKeys->GetCount(&cKeys);
+ CHECK_HR(hr, "Failed to number of PROPERTYKEYs in collection");
+
+ if (hr == S_OK)
+ {
+ // Get values for the DEVICE object
+ if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
+ {
+ for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
+ {
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ hr = pKeys->GetAt(dwIndex, &Key);
+ CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex);
+
+ if (hr == S_OK)
+ {
+ // Preset the property value to 'error not supported'. The actual value
+ // will replace this value, if read from the device.
+ pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
+
+ // Set DEVICE object properties
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_FIRMWARE_VERSION))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, DEVICE_FIRMWARE_VERSION_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_FIRMWARE_VERSION");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_LEVEL))
+ {
+ hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, DEVICE_POWER_LEVEL_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_LEVEL");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_SOURCE))
+ {
+ hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, WPD_POWER_SOURCE_EXTERNAL);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_POWER_SOURCE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_PROTOCOL))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_PROTOCOL, DEVICE_PROTOCOL_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_PROTOCOL");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_MODEL))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_MODEL, DEVICE_MODEL_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_MODEL");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_SERIAL_NUMBER))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_SERIAL_NUMBER");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_SUPPORTS_NON_CONSUMABLE))
+ {
+ hr = pValues->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, DEVICE_SUPPORTS_NONCONSUMABLE_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_SUPPORTS_NON_CONSUMABLE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_MANUFACTURER))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, DEVICE_MANUFACTURER_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_MANUFACTURER");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_FRIENDLY_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, DEVICE_FRIENDLY_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_FRIENDLY_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_DEVICE_TYPE))
+ {
+ hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, WPD_DEVICE_TYPE_GENERIC);
+ CHECK_HR(hr, "Failed to set WPD_DEVICE_TYPE");
+ }
+
+ // Set general properties for DEVICE
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ID, WPD_DEVICE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_NAME, WPD_DEVICE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, WPD_DEVICE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, L"");
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
+ {
+ hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
+ {
+ hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_DEVICE);
+ CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY");
+ }
+ }
+ }
+ }
+ else if (strObjectID.CompareNoCase(STORAGE_OBJECT_ID) == 0)
+ {
+ for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
+ {
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ hr = pKeys->GetAt(dwIndex, &Key);
+ CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex);
+
+ if (hr == S_OK)
+ {
+ // Preset the property value to 'error not supported'. The actual value
+ // will replace this value, if read from the device.
+ pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
+
+ // Set storage object properties
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_SERIAL_NUMBER))
+ {
+ hr = pValues->SetStringValue(WPD_STORAGE_SERIAL_NUMBER, STORAGE_SERIAL_NUMBER_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_SERIAL_NUMBER");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_FREE_SPACE_IN_BYTES))
+ {
+ hr = pValues->SetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, (STORAGE_FREE_SPACE_IN_BYTES_VALUE - GetObjectSize(README_FILE_OBJECT_ID)));
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_FREE_SPACE_IN_BYTES");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_CAPACITY))
+ {
+ hr = pValues->SetUnsignedLargeIntegerValue(WPD_STORAGE_CAPACITY, STORAGE_CAPACITY_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_CAPACITY");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_TYPE))
+ {
+ hr = pValues->SetUnsignedIntegerValue(WPD_STORAGE_TYPE, WPD_STORAGE_TYPE_FIXED_ROM);
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_TYPE");
+ }
+
+ // Set general properties for storage
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ID, STORAGE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_NAME, STORAGE_OBJECT_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, STORAGE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
+ {
+ hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
+ {
+ hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_STORAGE);
+ CHECK_HR(hr, "Failed to set WPD_FUNCTIONAL_OBJECT_CATEGORY");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_FILE_SYSTEM_TYPE))
+ {
+ hr = pValues->SetStringValue(WPD_STORAGE_FILE_SYSTEM_TYPE, STORAGE_FILE_SYSTEM_TYPE_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_FILE_SYSTEM_TYPE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_STORAGE_DESCRIPTION))
+ {
+ hr = pValues->SetStringValue(WPD_STORAGE_DESCRIPTION, STORAGE_DESCRIPTION_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_STORAGE_DESCRIPTION");
+ }
+ }
+ }
+ }
+ else if (strObjectID.CompareNoCase(DOCUMENTS_FOLDER_OBJECT_ID) == 0)
+ {
+ for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
+ {
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ hr = pKeys->GetAt(dwIndex, &Key);
+ CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex);
+
+ if (hr == S_OK)
+ {
+ // Preset the property value to 'error not supported'. The actual value
+ // will replace this value, if read from the device.
+ pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
+
+ // Set general properties for the folder object
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ID, DOCUMENTS_FOLDER_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, DOCUMENTS_FOLDER_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, STORAGE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_NAME, DOCUMENTS_FOLDER_OBJECT_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ORIGINAL_FILE_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, DOCUMENTS_FOLDER_OBJECT_ORIGINAL_FILE_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FOLDER);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
+ {
+ hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_DATE_MODIFIED))
+ {
+ PROPVARIANT pvDateModified = {0};
+ SYSTEMTIME systemtime = {0};
+
+ systemtime.wMonth = 6;
+ systemtime.wDay = 26;
+ systemtime.wYear = 2006;
+ systemtime.wHour = 5;
+
+ // Initialize the Date Modified PROPVARIANT value
+ PropVariantInit(&pvDateModified);
+
+ pvDateModified.vt = VT_DATE;
+ if (SystemTimeToVariantTime(&systemtime, &pvDateModified.date) == TRUE)
+ {
+ hr = pValues->SetValue(WPD_OBJECT_DATE_MODIFIED, &pvDateModified);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_DATE_MODIFIED");
+ }
+ else
+ {
+ LONG lError = GetLastError();
+ hr = HRESULT_FROM_WIN32(lError);
+ }
+
+ PropVariantClear(&pvDateModified);
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_DATE_CREATED))
+ {
+ PROPVARIANT pvDateCreated = {0};
+ SYSTEMTIME systemtime = {0};
+
+ systemtime.wMonth = 1;
+ systemtime.wDay = 24;
+ systemtime.wYear = 2006;
+ systemtime.wHour = 12;
+
+ // Initialize the Date Created PROPVARIANT value
+ PropVariantInit(&pvDateCreated);
+
+ pvDateCreated.vt = VT_DATE;
+ if (SystemTimeToVariantTime(&systemtime, &pvDateCreated.date) == TRUE)
+ {
+ hr = pValues->SetValue(WPD_OBJECT_DATE_CREATED, &pvDateCreated);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_DATE_CREATED");
+ }
+ else
+ {
+ LONG lError = GetLastError();
+ hr = HRESULT_FROM_WIN32(lError);
+ }
+
+ PropVariantClear(&pvDateCreated);
+ }
+ }
+ }
+ }
+ else if (strObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0)
+ {
+ for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
+ {
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ hr = pKeys->GetAt(dwIndex, &Key);
+ CHECK_HR(hr, "Failed to get PROPERTYKEY at index %d in collection", dwIndex);
+
+ if (hr == S_OK)
+ {
+ // Preset the property value to 'error not supported'. The actual value
+ // will replace this value, if read from the device.
+ pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ID, README_FILE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, README_FILE_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PERSISTENT_UNIQUE_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, DOCUMENTS_FOLDER_OBJECT_ID);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_PARENT_ID");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_NAME, README_FILE_OBJECT_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_ORIGINAL_FILE_NAME))
+ {
+ hr = pValues->SetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, README_FILE_OBJECT_ORIGINAL_FILE_NAME_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_ORIGINAL_FILE_NAME");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, GetObjectFormat(strObjectID));
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_FORMAT");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
+ {
+ hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, GetObjectContentType(strObjectID));
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CONTENT_TYPE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
+ {
+ hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_CAN_DELETE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_SIZE))
+ {
+ hr = pValues->SetUnsignedLargeIntegerValue(WPD_OBJECT_SIZE, GetObjectSize(strObjectID));
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_SIZE");
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_DATE_MODIFIED))
+ {
+ PROPVARIANT pvDateModified = {0};
+ SYSTEMTIME systemtime = {0};
+
+ systemtime.wMonth = 6;
+ systemtime.wDay = 26;
+ systemtime.wYear = 2006;
+ systemtime.wHour = 5;
+
+ // Initialize the Date Modified PROPVARIANT value
+ PropVariantInit(&pvDateModified);
+
+ pvDateModified.vt = VT_DATE;
+ if (SystemTimeToVariantTime(&systemtime, &pvDateModified.date) == TRUE)
+ {
+ hr = pValues->SetValue(WPD_OBJECT_DATE_MODIFIED, &pvDateModified);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_DATE_MODIFIED");
+ }
+ else
+ {
+ LONG lError = GetLastError();
+ hr = HRESULT_FROM_WIN32(lError);
+ }
+
+ PropVariantClear(&pvDateModified);
+ }
+
+ if (IsEqualPropertyKey(Key, WPD_OBJECT_DATE_CREATED))
+ {
+ PROPVARIANT pvDateCreated = {0};
+ SYSTEMTIME systemtime = {0};
+
+ systemtime.wMonth = 1;
+ systemtime.wDay = 24;
+ systemtime.wYear = 2006;
+ systemtime.wHour = 12;
+
+ // Initialize the Date Created PROPVARIANT value
+ PropVariantInit(&pvDateCreated);
+
+ pvDateCreated.vt = VT_DATE;
+ if (SystemTimeToVariantTime(&systemtime, &pvDateCreated.date) == TRUE)
+ {
+ hr = pValues->SetValue(WPD_OBJECT_DATE_CREATED, &pvDateCreated);
+ CHECK_HR(hr, "Failed to set WPD_OBJECT_DATE_CREATED");
+ }
+ else
+ {
+ LONG lError = GetLastError();
+ hr = HRESULT_FROM_WIN32(lError);
+ }
+
+ PropVariantClear(&pvDateCreated);
+ }
+ }
+ }
+ }
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to populate property attributes for the object and property specified.
+ *
+ * The parameters sent to us are:
+ * wszObjectID - the object whose property attributes are being requested.
+ * Key - the property whose attributes are being requested
+ * pAttributes - an IPortableDeviceValues which will contain the resulting property attributes
+ *
+ * The driver should:
+ * Read the property attributes for the specified property on the specified object and
+ * populate pAttributes with the results.
+ */
+HRESULT WpdObjectProperties::GetPropertyAttributesForObject(
+ _In_ LPCWSTR wszObjectID,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes)
+{
+ HRESULT hr = S_OK;
+
+ if ((wszObjectID == NULL) ||
+ (pAttributes == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ UNREFERENCED_PARAMETER(wszObjectID);
+ UNREFERENCED_PARAMETER(Key);
+
+ //
+ // Since ALL of our properties have the same attributes, we are ignoring the
+ // passed in wszObjectID and Key parameters. These parameters allow you to
+ // customize attributes for properties on specific objects. (i.e. WPD_OBJECT_ORIGINAL_FILE_NAME
+ // may be READ/WRITE on some objects and READONLY on others. )
+ //
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_DELETE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_READ");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_CAN_WRITE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_ATTRIBUTE_FORM");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to return the total size of the specified object
+ *
+ * The parameters sent to us are:
+ * strObjectID - the object whose total size is being requested.
+ *
+ * The driver should:
+ * Calculate or read the total size of the object and return it to the caller.
+ */
+ULONGLONG GetObjectSize(_In_ LPCWSTR wszObjectID)
+{
+ ULONGLONG FileObjectSize = 0;
+
+ if (_wcsicmp(wszObjectID, README_FILE_OBJECT_ID) == 0)
+ {
+ size_t cbFileObjectContents = 0;
+ if (SUCCEEDED(StringCbLengthA(README_FILE_OBJECT_CONTENTS, STRSAFE_MAX_CCH*sizeof(CHAR), &cbFileObjectContents)))
+ {
+ // StringCbLength() returns the size of the string excluding the null terminator,
+ // so we will account for it in our size calculation.
+ FileObjectSize = cbFileObjectContents + sizeof(CHAR);
+ }
+ }
+
+ return FileObjectSize;
+}
+
+/**
+ * This method is called to return the WPD format of the specified object
+ *
+ * The parameters sent to us are:
+ * strObjectID - the object whose WPD format is being requested.
+ *
+ * The driver should:
+ * Read the native format of the object and return a WPD format to the caller.
+ */
+GUID GetObjectFormat(_In_ LPCWSTR wszObjectID)
+{
+ GUID FileObjectFormat = WPD_OBJECT_FORMAT_UNSPECIFIED;
+
+ if (_wcsicmp(wszObjectID, README_FILE_OBJECT_ID) == 0)
+ {
+ FileObjectFormat = WPD_OBJECT_FORMAT_TEXT;
+ }
+
+ return FileObjectFormat;
+}
+
+/**
+ * This method is called to return the WPD content type of the specified object
+ *
+ * The parameters sent to us are:
+ * strObjectID - the object whose WPD content type is being requested.
+ *
+ * The driver should:
+ * Read the native content type of the object and return a WPD content type to the caller.
+ */
+GUID GetObjectContentType(_In_ LPCWSTR wszObjectID)
+{
+ GUID FileObjectFormat = WPD_CONTENT_TYPE_UNSPECIFIED;
+
+ if (_wcsicmp(wszObjectID, README_FILE_OBJECT_ID) == 0)
+ {
+ FileObjectFormat = WPD_CONTENT_TYPE_DOCUMENT;
+ }
+
+ return FileObjectFormat;
+}
diff --git a/wpd/WpdMultiTransportDriver/WpdObjectProperties.h b/wpd/WpdMultiTransportDriver/WpdObjectProperties.h
new file mode 100644
index 00000000..d76be32d
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectProperties.h
@@ -0,0 +1,79 @@
+#pragma once
+
+#define DEVICE_PROTOCOL_VALUE L"Multi-Transport Protocol ver 1.00"
+#define DEVICE_FIRMWARE_VERSION_VALUE L"1.0.0.0"
+#define DEVICE_POWER_LEVEL_VALUE 100
+#define DEVICE_MODEL_VALUE L"Multi-Transport"
+#define DEVICE_FRIENDLY_NAME_VALUE L"Multi-Transport Hello World!"
+#define DEVICE_MANUFACTURER_VALUE L"Windows Portable Devices Group"
+#define DEVICE_SERIAL_NUMBER_VALUE L"01234567890123-45676890123456"
+#define DEVICE_SUPPORTS_NONCONSUMABLE_VALUE TRUE
+
+#define STORAGE_OBJECT_ID L"123ABC"
+#define STORAGE_CAPACITY_VALUE 1024 * 1024
+#define STORAGE_FREE_SPACE_IN_BYTES_VALUE STORAGE_CAPACITY_VALUE
+#define STORAGE_SERIAL_NUMBER_VALUE L"98765432109876-54321098765432"
+#define STORAGE_OBJECT_NAME_VALUE L"Internal Memory"
+#define STORAGE_FILE_SYSTEM_TYPE_VALUE L"FAT32"
+#define STORAGE_DESCRIPTION_VALUE L"Hello World! Memory Storage System"
+
+#define DOCUMENTS_FOLDER_OBJECT_ID L"XYZ456"
+#define DOCUMENTS_FOLDER_OBJECT_NAME_VALUE L"Documents Folder"
+#define DOCUMENTS_FOLDER_OBJECT_ORIGINAL_FILE_NAME_VALUE L"Documents"
+
+#define README_FILE_OBJECT_ID L"6543210"
+#define README_FILE_OBJECT_NAME_VALUE L"Sample ReadMe Text File"
+#define README_FILE_OBJECT_ORIGINAL_FILE_NAME_VALUE L"ReadMe.txt"
+#define README_FILE_OBJECT_CONTENTS "Hello World!\r\nThis is a text file transferred from the WPD Multi-Transport Hello World sample driver.\r\n"
+
+ULONGLONG GetObjectSize(_In_ LPCWSTR strObjectID);
+GUID GetObjectFormat(_In_ LPCWSTR strObjectID);
+GUID GetObjectContentType(_In_ LPCWSTR strObjectID);
+HRESULT AddSupportedPropertyKeys(_In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys);
+
+VOID AddCommonPropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys);
+VOID AddDevicePropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys);
+VOID AddStoragePropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys);
+VOID AddFilePropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys);
+VOID AddFolderPropertyKeys(_In_ IPortableDeviceKeyCollection* pKeys);
+
+class WpdObjectProperties
+{
+public:
+ WpdObjectProperties();
+ virtual ~WpdObjectProperties();
+
+ HRESULT Initialize();
+
+ HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetSupportedProperties(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetPropertyValues(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetAllPropertyValues(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnSetPropertyValues(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetPropertyAttributes(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnDeleteProperties(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+private:
+ HRESULT GetPropertyValuesForObject(_In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys,
+ _In_ IPortableDeviceValues* pValues);
+
+ HRESULT GetPropertyAttributesForObject(_In_ LPCWSTR wszObjectID,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes);
+};
diff --git a/wpd/WpdMultiTransportDriver/WpdObjectResources.cpp b/wpd/WpdMultiTransportDriver/WpdObjectResources.cpp
new file mode 100644
index 00000000..456af6dc
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectResources.cpp
@@ -0,0 +1,674 @@
+#include "stdafx.h"
+#include "WpdObjectResources.tmh"
+
+WpdObjectResources::WpdObjectResources()
+{
+
+}
+
+WpdObjectResources::~WpdObjectResources()
+{
+
+}
+
+HRESULT WpdObjectResources::DispatchWpdMessage(
+ _In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+
+ if (Command.fmtid != WPD_CATEGORY_OBJECT_RESOURCES)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid));
+ }
+
+ if (hr == S_OK)
+ {
+ if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED))
+ {
+ hr = OnGetSupportedResources(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported resources");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_OPEN))
+ {
+ hr = OnOpenResource(pParams, pResults);
+ CHECK_HR(hr, "Failed to open resource");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_READ))
+ {
+ hr = OnReadResource(pParams, pResults);
+ CHECK_HR(hr, "Failed to read resource");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CLOSE))
+ {
+ hr = OnCloseResource(pParams, pResults);
+ CHECK_HR(hr, "Failed to close resource");
+ }
+ else if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES))
+ {
+ hr = OnGetResourceAttributes(pParams, pResults);
+ CHECK_HR(hr, "Failed to get resource attributes");
+ }
+ else
+ {
+ hr = E_NOTIMPL;
+ CHECK_HR(hr, "This object does not support this command id %d", Command.pid);
+ }
+ }
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resources we want to return.
+ *
+ * The driver should:
+ * - Return all resources for this object in WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS.
+ */
+HRESULT WpdObjectResources::OnGetSupportedResources(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ // Create the collection to hold the resource keys
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceKeyCollection,
+ (VOID**) &pKeys);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = GetSupportedResourcesForObject(wszObjectID, pKeys);
+ CHECK_HR(hr, "Failed to get supported resources for object '%ws'", wszObjectID);
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, pKeys);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resource attributes we want to return.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of property keys containing a single value,
+ * which is the key identifying the specific resource we are requested to return attributes for.
+ *
+ * The driver should:
+ * - Return the requested property attributes in WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES.
+ * If any resource attributes failed to be retrieved,
+ * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the
+ * HRESULT reason for the failure.
+ * - S_OK should be returned if all resource attributes were read successfully.
+ * - S_FALSE should be returned if any resource attribute failed.
+ * - Any error return indicates that the driver did not fill in any results, and the caller will
+ * not attempt to unpack any property values.
+ *
+ */
+HRESULT WpdObjectResources::OnGetResourceAttributes(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ CComPtr<IPortableDeviceValues> pAttributes;
+
+ if (hr == S_OK)
+ {
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, &Key);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = CoCreateInstance(CLSID_PortableDeviceValues,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IPortableDeviceValues,
+ (VOID**) &pAttributes);
+ CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = GetResourceAttributesForObject(wszObjectID, Key, pAttributes);
+ CHECK_HR(hr, "Failed to get resource attributes");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ HRESULT hrTemp = S_OK;
+
+ hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, pAttributes);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES"));
+
+ if(FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_OPEN
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: the object identifier of the
+ * object which contains the specified resource
+ *
+ * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: the specified resource
+ * to open
+ *
+ * - WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE: the access mode to which to
+ * open the specified resource
+ *
+ * The driver should:
+ * - Create a new context for this resource operation.
+ * - Return an identifier for the context in WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT.
+ * - Set the optimal transfer size in WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE
+ *
+ */
+HRESULT WpdObjectResources::OnOpenResource(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszObjectID = NULL;
+ PROPERTYKEY Key = WPD_PROPERTY_NULL;
+ DWORD dwMode = STGM_READ;
+ CAtlStringW strStrObjectID;
+ CAtlStringW strResourceContext;
+ ContextMap* pContextMap = NULL;
+
+ // Get the Object identifier of the object which contains the specified resource
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &wszObjectID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ // Get the resource key
+ 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");
+ }
+
+ // Get the access mode
+ if (hr == S_OK)
+ {
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE, &dwMode);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE");
+ }
+
+ // Validate whether the params given to us are correct. In this case, we need to check that the object
+ // supports the resource requested, and can be opened in the requested access mode.
+ if (hr == S_OK)
+ {
+ // In this sample, we only have one object (README_FILE_OBJECT_ID) which supports a
+ // resource (WPD_RESOURCE_DEFAULT) for reading only.
+ // So if any other Object ID or any other resource is specified, it must be invalid.
+ strStrObjectID = wszObjectID;
+ if(strStrObjectID.CompareNoCase(README_FILE_OBJECT_ID) != 0)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Object [%ws] does not support resources", wszObjectID);
+ }
+ if (hr == S_OK)
+ {
+ if (!IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Only WPD_RESOURCE_DEFAULT is supported in this sample driver");
+ }
+ }
+ if (hr == S_OK)
+ {
+ if ((dwMode & STGM_WRITE) != 0)
+ {
+ hr = E_ACCESSDENIED;
+ CHECK_HR(hr, "This resource is not available for write access");
+ }
+ }
+ }
+
+ // 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 a new resource operation context, initialize it, and add it to the client context map.
+ if (hr == S_OK)
+ {
+ WpdObjectResourceContext* pResourceContext = new WpdObjectResourceContext();
+ if (pResourceContext != NULL)
+ {
+ // Initialize the resource context with ...
+ pResourceContext->m_strObjectID = wszObjectID;
+ pResourceContext->m_Resource = Key;
+ pResourceContext->m_BytesTransferred = 0;
+ pResourceContext->m_BytesTotal = GetObjectSize(wszObjectID);
+
+ // Add the resource context to the context map
+ pContextMap->Add(pResourceContext, strResourceContext);
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate resource context");
+ }
+ SAFE_RELEASE(pResourceContext);
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, strResourceContext);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Set the optimal buffer size
+ if (hr == S_OK)
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE, FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE value");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszObjectID);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_READ
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: the context the driver returned to
+ * the client in OnOpenResource.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ: the number of bytes to
+ * read from the resource.
+ *
+ * The driver should:
+ * - Read data associated with the resource and return it back to the caller in
+ * WPD_PROPERTY_OBJECT_RESOURCES_DATA.
+ * - Report the number of bytes actually read from the resource in
+ * WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ. This number may be smaller
+ * than WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ when reading the last
+ * chunk of data from the resource.
+ */
+HRESULT WpdObjectResources::OnReadResource(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszResourceContext = NULL;
+ DWORD dwNumBytesToRead = 0;
+ DWORD dwNumBytesRead = 0;
+ BYTE* pBuffer = NULL;
+ WpdObjectResourceContext* pResourceContext = NULL;
+ ContextMap* pContextMap = NULL;
+
+ // Get the enumeration context identifier for this enumeration operation. We will
+ // need this to lookup the specific enumeration context in the client context map.
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &wszResourceContext);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the number of bytes to read
+ if (hr == S_OK)
+ {
+ 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 (hr == S_OK)
+ {
+ pBuffer = reinterpret_cast<BYTE *>(CoTaskMemAlloc(dwNumBytesToRead));
+ if (pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate the destination buffer");
+ }
+ }
+
+ // Get the client context map so we can retrieve the resource context for this resource
+ // operation using the WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT property value obtained above.
+ if (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");
+ }
+
+ if (hr == S_OK)
+ {
+ pResourceContext = (WpdObjectResourceContext*)pContextMap->GetContext(wszResourceContext);
+ if (pResourceContext == NULL)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing resource context");
+ }
+ }
+
+ // Read the next chunk of data for this request
+ if (hr == S_OK && pBuffer != NULL)
+ {
+ hr = ReadDataFromResource(pResourceContext, pBuffer, dwNumBytesToRead, &dwNumBytesRead);
+ CHECK_HR(hr, "Failed to read %d bytes from resource", dwNumBytesToRead);
+ }
+
+ if (hr == S_OK && pBuffer != NULL)
+ {
+ hr = pResults->SetBufferValue(WPD_PROPERTY_OBJECT_RESOURCES_DATA, pBuffer, dwNumBytesRead);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_DATA");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ, dwNumBytesRead);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszResourceContext);
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pBuffer);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_CLOSE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: the context the driver returned to
+ * the client in OnOpenResource.
+ *
+ * The driver should:
+ * - Destroy any data associated with this context.
+ */
+HRESULT WpdObjectResources::OnCloseResource(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR wszResourceContext = NULL;
+ ContextMap* pContextMap = NULL;
+
+ UNREFERENCED_PARAMETER(pResults);
+
+ // First get ALL parameters for this command. If we cannot get ALL parameters
+ // then E_INVALIDARG should be returned and no further processing should occur.
+
+ // Get the resource context identifier for this resource operation. We will
+ // need this to lookup the specific resource context in the client context map.
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &wszResourceContext);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the client context map so we can retrieve the resource context for this resource
+ // operation using the WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT property value obtained above.
+ if (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");
+ }
+
+ // Destroy any data allocated/associated with the resource context and then remove it from the context map.
+ // We no longer need to keep this context around because the resource operation has been ended.
+ if (hr == S_OK)
+ {
+ pContextMap->Remove(wszResourceContext);
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(wszResourceContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called to populate PROPERTYKEYs found on objects.
+ *
+ * The parameters sent to us are:
+ * wszObjectID - the object whose supported resource keys are being requested
+ * pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
+ *
+ * The driver should:
+ * Add PROPERTYKEYs pertaining to the specified object.
+ */
+HRESULT WpdObjectResources::GetSupportedResourcesForObject(
+ _In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys)
+{
+ HRESULT hr = S_OK;
+ CAtlStringW strObjectID;
+
+ if ((wszObjectID == NULL) ||
+ (pKeys == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ strObjectID = wszObjectID;
+
+ if (strObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0)
+ {
+ hr = pKeys->Add(WPD_RESOURCE_DEFAULT);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_DEFAULT");
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to populate resource attributes found on a particular object
+ * resource.
+ *
+ * The parameters sent to us are:
+ * wszObjectID - the object whose resource attributes are being requested
+ * Key - the resource on the specified object whose attributes are being returned
+ * pAttributes - An IPortableDeviceValues to be populated with resource attributes.
+ *
+ * The driver should:
+ * Add attributes pertaining to the resource on the specified object.
+ */
+HRESULT WpdObjectResources::GetResourceAttributesForObject(
+ _In_ LPCWSTR wszObjectID,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes)
+{
+ HRESULT hr = S_OK;
+ CAtlStringW strObjectID;
+
+ if ((wszObjectID == NULL) ||
+ (pAttributes == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ strObjectID = wszObjectID;
+
+ if ((strObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0) && (IsEqualPropertyKey(Key, WPD_RESOURCE_DEFAULT)))
+ {
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_DELETE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetUnsignedLargeIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, GetObjectSize(strObjectID));
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_READ, TRUE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_READ");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_WRITE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_WRITE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetBoolValue(WPD_RESOURCE_ATTRIBUTE_CAN_DELETE, FALSE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_CAN_DELETE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetGuidValue(WPD_RESOURCE_ATTRIBUTE_FORMAT, GetObjectFormat(strObjectID));
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_FORMAT");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE, FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_READ_BUFFER_SIZE");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pAttributes->SetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE, FILE_OPTIMAL_WRITE_BUFFER_SIZE_VALUE);
+ CHECK_HR(hr, "Failed to set WPD_RESOURCE_ATTRIBUTE_OPTIMAL_WRITE_BUFFER_SIZE");
+ }
+ }
+
+ return hr;
+}
+
+/**
+ * This method is called to read data from a particular object
+ * resource.
+ *
+ * The parameters sent to us are:
+ * pResourceContext - the resource operation context
+ * pBuffer - the buffer to read the resource data into
+ * dwNumBytesToRead - number of bytes to read into the resource. This is also
+ * the total size of the passed in pBuffer.
+ * pdwNumBytesRead - On return, should contain the actual number of bytes read into pBuffer
+ *
+ * The driver should:
+ * - Read data from the specified resource
+ * - Update the resource operation context with transfer state information
+ * - Return the actual number of bytes written in pdwNumBytesRead.
+ */
+HRESULT WpdObjectResources::ReadDataFromResource(
+ _In_ WpdObjectResourceContext* pResourceContext,
+ _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer,
+ DWORD dwNumBytesToRead,
+ _Out_ DWORD* pdwNumBytesRead)
+{
+ HRESULT hr = S_OK;
+
+ if ((pResourceContext == NULL) ||
+ (pBuffer == NULL) ||
+ (pdwNumBytesRead == NULL))
+ {
+ hr = E_INVALIDARG;
+ return hr;
+ }
+
+ *pdwNumBytesRead = 0;
+ ZeroMemory(pBuffer, dwNumBytesToRead * sizeof(BYTE));
+
+ // If we have data left to transfer, then transfer up to dwNumBytesToRead
+ // if possible.
+ if (pResourceContext->m_BytesTotal >= pResourceContext->m_BytesTransferred)
+ {
+ dwNumBytesToRead = (DWORD)min((ULONGLONG)dwNumBytesToRead,(pResourceContext->m_BytesTotal - pResourceContext->m_BytesTransferred));
+ }
+
+ // Read the data from the resource
+ if (dwNumBytesToRead > 0)
+ {
+ // If we are reading from our single file resource, make sure you read
+ // from the proper source data contents.
+ if (pResourceContext->m_strObjectID.CompareNoCase(README_FILE_OBJECT_ID) == 0)
+ {
+ hr = StringCbCopyA((LPSTR)pBuffer, dwNumBytesToRead, README_FILE_OBJECT_CONTENTS);
+ CHECK_HR(hr, "StringCbCopyA failed, dwNumBytesToRead = %ld", dwNumBytesToRead);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // update the number of bytes transferred in the resource context
+ pResourceContext->m_BytesTransferred += dwNumBytesToRead;
+
+ // set the number of bytes actually read into to pBuffer
+ *pdwNumBytesRead = dwNumBytesToRead;
+ }
+
+ return hr;
+}
diff --git a/wpd/WpdMultiTransportDriver/WpdObjectResources.h b/wpd/WpdMultiTransportDriver/WpdObjectResources.h
new file mode 100644
index 00000000..e9d4fb63
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/WpdObjectResources.h
@@ -0,0 +1,111 @@
+#pragma once
+
+#define FILE_OPTIMAL_READ_BUFFER_SIZE_VALUE (2 * 1024 * 1024)
+#define FILE_OPTIMAL_WRITE_BUFFER_SIZE_VALUE (2 * 1024 * 1024)
+
+// This class is used to store the context for a specific resource operation.
+class WpdObjectResourceContext : public IUnknown
+{
+public:
+ WpdObjectResourceContext() :
+ m_cRef(1),
+ m_Resource(WPD_RESOURCE_DEFAULT),
+ m_BytesTransferred(0),
+ m_BytesTotal(0)
+ {
+
+ }
+
+ ~WpdObjectResourceContext()
+ {
+
+ }
+
+public: // IUnknown
+ ULONG __stdcall AddRef()
+ {
+ InterlockedIncrement((long*) &m_cRef);
+ return m_cRef;
+ }
+
+ _At_(this, __drv_freesMem(Mem))
+ ULONG __stdcall Release()
+ {
+ ULONG ulRefCount = m_cRef - 1;
+
+ if (InterlockedDecrement((long*) &m_cRef) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return ulRefCount;
+ }
+
+ HRESULT __stdcall QueryInterface(
+ REFIID riid,
+ void** ppv)
+ {
+ HRESULT hr = S_OK;
+
+ if(riid == IID_IUnknown)
+ {
+ *ppv = static_cast<IUnknown*>(this);
+ AddRef();
+ }
+ else
+ {
+ *ppv = NULL;
+ hr = E_NOINTERFACE;
+ }
+
+ return hr;
+ }
+
+private:
+ DWORD m_cRef;
+
+// WpdObjectResourceContext specific data
+public:
+ CAtlStringW m_strObjectID; // object identifier of the object whose resource is being transferred
+ PROPERTYKEY m_Resource; // the specific resource being transferred
+ ULONGLONG m_BytesTransferred; // number of bytes transferred from the resource to the caller
+ ULONGLONG m_BytesTotal; // total number of bytes of the resource data
+};
+
+class WpdObjectResources
+{
+public:
+ WpdObjectResources();
+ virtual ~WpdObjectResources();
+
+ HRESULT DispatchWpdMessage(_In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetSupportedResources(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnGetResourceAttributes(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnOpenResource(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnReadResource(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+
+ HRESULT OnCloseResource(_In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults);
+private:
+ HRESULT GetSupportedResourcesForObject(_In_ LPCWSTR wszObjectID,
+ _In_ IPortableDeviceKeyCollection* pKeys);
+
+ HRESULT GetResourceAttributesForObject(_In_ LPCWSTR wszObjectID,
+ _In_ REFPROPERTYKEY Key,
+ _In_ IPortableDeviceValues* pAttributes);
+
+ HRESULT ReadDataFromResource(_In_ WpdObjectResourceContext* pResourceContext,
+ _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE* pBuffer,
+ DWORD dwNumBytesToRead,
+ _Out_ DWORD* pdwNumBytesRead);
+};
diff --git a/wpd/WpdMultiTransportDriver/resource.h b/wpd/WpdMultiTransportDriver/resource.h
new file mode 100644
index 00000000..d59fe80e
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/resource.h
@@ -0,0 +1,3 @@
+#pragma once
+#define IDR_WpdMultiTransportDriver 101
+
diff --git a/wpd/WpdMultiTransportDriver/stdafx.h b/wpd/WpdMultiTransportDriver/stdafx.h
new file mode 100644
index 00000000..67b8525b
--- /dev/null
+++ b/wpd/WpdMultiTransportDriver/stdafx.h
@@ -0,0 +1,266 @@
+#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 <strsafe.h>
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlcoll.h>
+#include <atlstr.h>
+
+#ifndef SAFE_RELEASE
+ #define SAFE_RELEASE(p) if( NULL != p ) { ( p )->Release(); p = NULL; }
+#endif
+
+#include "WpdMultiTransportDriver.h"
+#include "PortableDeviceTypes.h"
+#include "PortableDeviceClassExtension.h"
+#include "PortableDevice.h"
+
+#include <initguid.h>
+#include <propkeydef.h>
+
+// {CDD18979-A7B0-4D5E-9EB2-0A826805CBBD}
+DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, 0xCDD18979, 0xA7B0, 0x4D5E, 0x9E, 0xB2, 0x0A, 0x82, 0x68, 0x05, 0xCB, 0xBD, 2);
+// {9BD949E5-59CF-41AE-90A9-BE1D044F578F}
+DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, 0x9BD949E5, 0x59CF, 0x41AE, 0x90, 0xA9, 0xBE, 0x1D, 0x04, 0x4F, 0x57, 0x8F, 2);
+// {4DF6C8C7-2CE5-457C-9F53-EFCECAA95C04}
+DEFINE_PROPERTYKEY(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, 0x4DF6C8C7, 0x2CE5, 0x457C, 0x9F, 0x53, 0xEF, 0xCE, 0xCA, 0xA9, 0x5C, 0x04, 2);
+
+class ContextMap : public IUnknown
+{
+public:
+ ContextMap() :
+ m_cRef(1)
+ {
+
+ }
+
+ ~ContextMap()
+ {
+ CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection);
+
+ IUnknown* pUnk = NULL;
+ POSITION elementPosition = NULL;
+
+ elementPosition = m_Map.GetStartPosition();
+ while(elementPosition != NULL)
+ {
+ pUnk = m_Map.GetNextValue(elementPosition);
+ if(pUnk != NULL)
+ {
+ pUnk->Release();
+ }
+ }
+ }
+
+public: // IUnknown
+ ULONG __stdcall AddRef()
+ {
+ InterlockedIncrement((long*) &m_cRef);
+ return m_cRef;
+ }
+
+ _At_(this, __drv_freesMem(Mem))
+ ULONG __stdcall Release()
+ {
+ ULONG ulRefCount = m_cRef - 1;
+
+ if (InterlockedDecrement((long*) &m_cRef) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return ulRefCount;
+ }
+
+ HRESULT __stdcall QueryInterface(
+ REFIID riid,
+ void** ppv)
+ {
+ HRESULT hr = S_OK;
+
+ if(riid == IID_IUnknown)
+ {
+ *ppv = static_cast<IUnknown*>(this);
+ AddRef();
+ }
+ else
+ {
+ *ppv = NULL;
+ hr = E_NOINTERFACE;
+ }
+ return hr;
+ }
+
+
+public: // Context accessor methods
+
+ // If successful, this method AddRef's the context and returns
+ // a context key
+ HRESULT Add(
+ _In_ IUnknown* pContext,
+ _Out_ CAtlStringW& key)
+ {
+ CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection);
+ HRESULT hr = S_OK;
+ GUID guidContext = GUID_NULL;
+ CComBSTR bstrContext;
+
+ key = L"";
+
+ // Create a unique context key
+ hr = CoCreateGuid(&guidContext);
+ if (hr == S_OK)
+ {
+ bstrContext = guidContext;
+ if(bstrContext.Length() > 0)
+ {
+ key = bstrContext;
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+
+ if (hr == S_OK)
+ {
+ // Insert this into the map
+ POSITION elementPosition = m_Map.SetAt(key, pContext);
+ if(elementPosition != NULL)
+ {
+ // AddRef since we are holding onto it
+ pContext->AddRef();
+ }
+ else
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ }
+ return hr;
+ }
+
+ void Remove(
+ _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;
+};
+
+HRESULT UpdateDeviceFriendlyName(
+ _In_ IPortableDeviceClassExtension* pPortableDeviceClassExtension,
+ _In_ PCWSTR wszDeviceFriendlyName);
+
+#include "WpdObjectEnum.h"
+#include "WpdObjectProperties.h"
+#include "WpdObjectResources.h"
+#include "WpdCapabilities.h"
+#include "WpdBaseDriver.h"
+
+extern HINSTANCE g_hInstance;
+
+//
+// Driver specific tracing #defines
+//
+// TODO: Change these values to be appropriate for your driver.
+//
+#define MYDRIVER_TRACING_ID L"Microsoft\\WPD\\MultiTransportDriver"
+
+//
+// TODO: Choose a different trace control GUID
+//
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(MultiTransportDriverCtlGuid,(300fbd95,366b,4d6a,b4d1,c426603ca2e6), \
+ 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)