diff options
| author | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-03-17 19:50:07 -0700 |
| commit | 97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch) | |
| tree | 46f3701832d70b420eb0fc0eb93261f9da45db3f /wpd/WpdMultiTransportDriver | |
| parent | ef1905bf1e8825bb31120dfb27e0daf3154d859a (diff) | |
Initial publish
Diffstat (limited to 'wpd/WpdMultiTransportDriver')
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) |
