summaryrefslogtreecommitdiff
path: root/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
committerDave Wilson <[email protected]>2015-03-17 19:50:07 -0700
commit97cf5197cf5b882b2c689d8dc2b555f2edf8f418 (patch)
tree46f3701832d70b420eb0fc0eb93261f9da45db3f /wpd/WpdWudfSampleDriver/WpdObjectResources.cpp
parentef1905bf1e8825bb31120dfb27e0daf3154d859a (diff)
Initial publish
Diffstat (limited to 'wpd/WpdWudfSampleDriver/WpdObjectResources.cpp')
-rw-r--r--wpd/WpdWudfSampleDriver/WpdObjectResources.cpp944
1 files changed, 944 insertions, 0 deletions
diff --git a/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp b/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp
new file mode 100644
index 00000000..6f7955d5
--- /dev/null
+++ b/wpd/WpdWudfSampleDriver/WpdObjectResources.cpp
@@ -0,0 +1,944 @@
+#include "stdafx.h"
+#include "WpdObjectResources.tmh"
+
+WpdObjectResources::WpdObjectResources()
+{
+
+}
+
+WpdObjectResources::~WpdObjectResources()
+{
+
+}
+
+HRESULT WpdObjectResources::Initialize(
+ _In_ FakeDevice *pFakeDevice)
+{
+ HRESULT hr = S_OK;
+
+ if(pFakeDevice == NULL)
+ {
+ hr = E_POINTER;
+ CHECK_HR(hr, "Cannot have NULL parameter");
+ return hr;
+ }
+ m_pFakeDevice = pFakeDevice;
+ return hr;
+}
+
+HRESULT WpdObjectResources::DispatchWpdMessage(
+ _In_ REFPROPERTYKEY Command,
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+
+ if (hr == S_OK)
+ {
+ if (Command.fmtid != WPD_CATEGORY_OBJECT_RESOURCES)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "This object does not support this command category %ws",CComBSTR(Command.fmtid));
+ }
+ }
+
+ if (hr == S_OK)
+ {
+ if (IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED))
+ {
+ hr = OnGetSupportedResources(pParams, pResults);
+ CHECK_HR(hr, "Failed to get supported resources");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES))
+ {
+ hr = OnGetAttributes(pParams, pResults);
+ if(FAILED(hr))
+ {
+ CHECK_HR(hr, "Failed to get resource attributes");
+ }
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_OPEN))
+ {
+ hr = OnOpen(pParams, pResults);
+ CHECK_HR(hr, "Failed to open resource");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_READ))
+ {
+ hr = OnRead(pParams, pResults);
+ CHECK_HR(hr, "Failed to read resource data");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_WRITE))
+ {
+ hr = OnWrite(pParams, pResults);
+ CHECK_HR(hr, "Failed to write resource data");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CLOSE))
+ {
+ hr = OnClose(pParams, pResults);
+ CHECK_HR(hr, "Failed to close resource");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_DELETE))
+ {
+ hr = OnDelete(pParams, pResults);
+ CHECK_HR(hr, "Failed to delete resources");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_CREATE_RESOURCE))
+ {
+ hr = OnCreate(pParams, pResults);
+ CHECK_HR(hr, "Failed to create resource");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_REVERT))
+ {
+ hr = OnRevert(pParams, pResults);
+ CHECK_HR(hr, "Failed to revert resource operation");
+ }
+ else if(IsEqualPropertyKey(Command, WPD_COMMAND_OBJECT_RESOURCES_SEEK))
+ {
+ hr = OnSeek(pParams, pResults);
+ CHECK_HR(hr, "Failed resource seek operation");
+ }
+ else
+ {
+ hr = E_NOTIMPL;
+ CHECK_HR(hr, "This object does not support this command id %d", Command.pid);
+ }
+ }
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_SUPPORTED
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resources we want to return.
+ *
+ * The driver should:
+ * - Return all resources for this object in WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS.
+ */
+HRESULT WpdObjectResources::OnGetSupportedResources(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszObjectID = NULL;
+
+ CComPtr<IPortableDeviceKeyCollection> pKeys;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ // Get the collection of resource keys
+ if (hr == S_OK)
+ {
+ hr = m_pFakeDevice->GetSupportedResources(pszObjectID, &pKeys);
+ CHECK_HR(hr, "Failed to get resource keys collection on [%ws]", pszObjectID);
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, pKeys);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_GET_ATTRIBUTES
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resource attributes we want to return.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of resource keys containing a single value,
+ * which is the key identifying the specific resource whose attributes we are requested to return.
+ *
+ * The driver should:
+ * - Return the requested resource attributes. If any resource attributes failed to be retrieved,
+ * the corresponding value should be set to type VT_ERROR with the 'scode' member holding the
+ * HRESULT reason for the failure.
+ * - S_OK should be returned if all resource attributes were read successfully.
+ * - S_FALSE should be returned if any resource attribute failed.
+ * - Any error return indicates that the driver did not fill in any results, and the caller will
+ * not attempt to unpack any resource attributes.
+ */
+HRESULT WpdObjectResources::OnGetAttributes(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszObjectID = NULL;
+ PROPERTYKEY Key = {0};
+
+ CComPtr<IPortableDeviceValues> pAttributeStore;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID);
+ if (hr != S_OK)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, &Key);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS");
+ }
+
+ if (hr == S_OK)
+ {
+ hr = m_pFakeDevice->GetResourceAttributes(pszObjectID, Key, &pAttributeStore);
+ CHECK_HR(hr, "Failed to get attributes on [%ws]", pszObjectID);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ HRESULT hrTemp = S_OK;
+
+ hrTemp = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, pAttributeStore);
+ CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES"));
+
+ if(FAILED(hrTemp))
+ {
+ hr = hrTemp;
+ }
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszObjectID);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_OPEN
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resource we are interested in.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE: specifies the access requested by the caller. It will
+ * be either STGM_READ or STGM_WRITE.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of resource keys containing a single value,
+ * which is the key identifying the specific resource the caller is interested in.
+ *
+ * The driver should:
+ * - Create a context associated with this resource. This context will be used by clients when reading/writing
+ * resource data. Generally, most drivers will also lock the resource here if necessary.
+ * The context identifier should be a string value returned in WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT.
+ * - Return the optimal transfer buffer size in WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE.
+ */
+HRESULT WpdObjectResources::OnOpen(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszObjectID = NULL;
+ PROPERTYKEY Key = {0};
+ DWORD dwMode = STGM_READ;
+ LPWSTR pszContext = NULL;
+ ContextMap* pContextMap = NULL;
+ BOOL bSupportsResource = FALSE;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID, &pszObjectID);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID");
+ }
+
+ // Get the resource key
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS, &Key);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS");
+ }
+
+ // Get the access mode
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE, &dwMode);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_ACCESS_MODE");
+ }
+
+ // Get the context map which the driver stored in pParams for convenience
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ // Validate whether this object supports the requested resource
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pFakeDevice->SupportsResource(pszObjectID, Key, &bSupportsResource);
+ CHECK_HR(hr, "Failed to check whether object supports resources");
+ }
+
+ if (SUCCEEDED(hr) && !bSupportsResource)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Object does not support this resource");
+ }
+
+ // Create the context
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateResourceContext(pContextMap, pszObjectID, Key, FALSE, &pszContext);
+ CHECK_HR(hr, "Failed to create resource context for %ws.%d on %ws", CComBSTR(Key.fmtid), Key.pid, pszObjectID);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, pszContext);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Set the optimal buffer size
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE value");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszObjectID);
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_READ
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the previsouly opened resource we are going to read from.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ: specifies the next number of bytes to read.
+ *
+ * The driver should:
+ * - Read up to the next WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ from the resource.
+ * - Return the number of bytes actually read in WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ.
+ */
+HRESULT WpdObjectResources::OnRead(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszContext = NULL;
+ DWORD dwNumBytesToRead = 0;
+ DWORD dwNumBytesRead = 0;
+ BYTE* pBuffer = NULL;
+ ResourceContext* pContext = NULL;
+
+ // Get the Context
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the number of bytes to read
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ, &dwNumBytesToRead);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_READ");
+ }
+
+ // Allocate the destination buffer
+ if (SUCCEEDED(hr))
+ {
+ pBuffer = reinterpret_cast<BYTE *>(CoTaskMemAlloc(dwNumBytesToRead));
+ if (pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate the destination buffer");
+ }
+ }
+
+ // Get the context for this transfer
+ if (SUCCEEDED(hr))
+ {
+ hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext);
+ CHECK_HR(hr, "Failed to get resource context");
+ }
+
+ // Read the next band of data for this transfer request
+ if (SUCCEEDED(hr) && pBuffer != NULL)
+ {
+ hr = m_pFakeDevice->ReadData(pContext->ObjectID,
+ pContext->Key,
+ pContext->NumBytesTransfered,
+ pBuffer,
+ dwNumBytesToRead,
+ &dwNumBytesRead);
+ CHECK_HR(hr, "Failed to read %d bytes from [%ws] on resource {%ws}.%d", dwNumBytesToRead, pContext->ObjectID, CComBSTR(pContext->Key.fmtid), pContext->Key.pid);
+ if (SUCCEEDED(hr))
+ {
+ pContext->NumBytesTransfered += dwNumBytesRead;
+ }
+ }
+
+ if (SUCCEEDED(hr) && pBuffer != NULL)
+ {
+ hr = pResults->SetBufferValue(WPD_PROPERTY_OBJECT_RESOURCES_DATA, pBuffer, dwNumBytesRead);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_DATA");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ, dwNumBytesRead);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_READ");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pBuffer);
+
+ SAFE_RELEASE(pContext);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_WRITE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the previsouly opened resource we are going to write to.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE: specifies the next number of bytes to write.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_DATA: specifies byte array where the data should be copied from.
+ *
+ * The driver should:
+ * - Write the next WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE to the resource.
+ * - Return the number of bytes actually written in WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN.
+ * It is normally considered an error if this value does not match WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE.
+ */
+HRESULT WpdObjectResources::OnWrite(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszContext = NULL;
+ DWORD dwNumBytesToWrite = 0;
+ DWORD dwNumBytesWritten = 0;
+ BYTE* pBuffer = NULL;
+ DWORD cbBuffer = 0;
+ ResourceContext* pContext = NULL;
+
+ // Get the Context string
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the number of bytes to write
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE, &dwNumBytesToWrite);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_TO_WRITE");
+ }
+
+ // Get the source buffer
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetBufferValue(WPD_PROPERTY_OBJECT_RESOURCES_DATA, &pBuffer, &cbBuffer);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_DATA");
+ }
+
+ // Get the resource context for this transfer
+ if (SUCCEEDED(hr))
+ {
+ hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext);
+ CHECK_HR(hr, "Faield to get resource context");
+ }
+
+ // Write the next band of data for this transfer request
+ if (SUCCEEDED(hr) && pBuffer != NULL)
+ {
+ hr = m_pFakeDevice->WriteData(pContext->ObjectID,
+ pContext->Key,
+ pContext->NumBytesTransfered,
+ pBuffer,
+ dwNumBytesToWrite,
+ &dwNumBytesWritten);
+ CHECK_HR(hr, "Failed to write %d bytes to [%ws] on resource {%ws}.%d", dwNumBytesToWrite, pContext->ObjectID, CComBSTR(pContext->Key.fmtid), pContext->Key.pid);
+ if (SUCCEEDED(hr))
+ {
+ pContext->NumBytesTransfered += dwNumBytesWritten;
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN, dwNumBytesWritten);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_NUM_BYTES_WRITTEN");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pBuffer);
+
+ SAFE_RELEASE(pContext);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_CLOSE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the resource context associated with a
+ * previously opened resource.
+ *
+ * The driver should:
+ * - Unlock the resource if necessary, and release any system and device resources associated with this WPD object resource.
+ */
+HRESULT WpdObjectResources::OnClose(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ // No results are expected to be returned
+ UNREFERENCED_PARAMETER(pResults);
+
+ HRESULT hr = S_OK;
+ LPWSTR pszContext = NULL;
+ ContextMap* pContextMap = NULL;
+ ResourceContext* pContext = NULL;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the context map which the driver stored in pParams for convenience
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ // If this was a creation request, enable the resource for the content
+ if (SUCCEEDED(hr))
+ {
+ // Get the context for this transfer
+ hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext);
+ CHECK_HR(hr, "Failed to get resource context");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ if (pContext->CreateRequest == TRUE)
+ {
+ hr = m_pFakeDevice->EnableResource(pContext->ObjectID, pContext->Key);
+ CHECK_HR(hr, "Failed to enable resource on object [%ws]", pContext->ObjectID);
+ }
+ }
+
+ //Free the context
+ SAFE_RELEASE(pContext);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = DestroyResourceContext(pContextMap, pszContext);
+ CHECK_HR(hr, "Failed to remove resource context [%ws]", pszContext);
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_DELETE
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_OBJECT_ID: identifies the object whose resources should be deleted.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_KEYS: a collection of keys indicating which
+ * resources to delete.
+ *
+ * The driver should:
+ * - Delete the specified resources from the object.
+ * - S_OK should be returned if all specified properties were successfully deleted.
+ * - E_ACCESSDENIED should be returned if the client attempts to delete a resource which is not deletable (i.e.
+ * WPD_RESOURCE_ATTRIBUTE_CAN_DELETE is FALSE for that resource.)
+ */
+HRESULT WpdObjectResources::OnDelete(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = E_ACCESSDENIED;
+
+ UNREFERENCED_PARAMETER(pParams);
+ UNREFERENCED_PARAMETER(pResults);
+
+ // This driver has no resources which can be deleted.
+ return hr;
+}
+
+HRESULT WpdObjectResources::OnCreate(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ HRESULT hr = S_OK;
+ LPWSTR pszObjectID = NULL;
+ LPWSTR pszContext = NULL;
+ ContextMap* pContextMap = NULL;
+ PROPERTYKEY ResourceKey = WPD_PROPERTY_NULL;
+ GUID guidObjectFormat = GUID_NULL;
+ CComPtr<IPortableDeviceValues> pResourceAttributes;
+
+ // Getthe Resource Attributes
+ hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES, &pResourceAttributes);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_RESOURCE_ATTRIBUTES");
+ }
+
+ // Get the Resource Key
+ if (SUCCEEDED(hr))
+ {
+ hr = pResourceAttributes->GetKeyValue(WPD_RESOURCE_ATTRIBUTE_RESOURCE_KEY, &ResourceKey);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_RESOURCE_ATTRIBUTE_RESOURCE_KEY");
+ }
+ }
+
+ if (SUCCEEDED(hr) && !IsEqualPropertyKey(ResourceKey, WPD_RESOURCE_CONTACT_PHOTO))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "CreateResource is only supported on WPD_RESOURCE_CONTACT_PHOTO");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Get the Object ID
+ hr = pResourceAttributes->GetStringValue(WPD_OBJECT_ID, &pszObjectID);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_OBJECT_ID");
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = m_pFakeDevice->GetContentFormat(pszObjectID, guidObjectFormat);
+ CHECK_HR(hr, "Failed to get content object format");
+
+ if (SUCCEEDED(hr) && !IsEqualGUID(guidObjectFormat, WPD_OBJECT_FORMAT_VCARD2))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "CreateResource is only supported on contact objects");
+ }
+ }
+
+ // Get the context map which the driver stored in pParams for convenience
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ // Create the context
+ if (SUCCEEDED(hr))
+ {
+ hr = CreateResourceContext(pContextMap, pszObjectID, ResourceKey, TRUE, &pszContext);
+ CHECK_HR(hr, "Failed to create resource context for %ws.%d on %ws", CComBSTR(ResourceKey.fmtid), ResourceKey.pid, pszObjectID);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, pszContext);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Set the optimal buffer size
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE, OPTIMAL_BUFFER_SIZE);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_OPTIMAL_TRANSFER_BUFFER_SIZE value");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszObjectID);
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_REVERT
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the resource context associated with a
+ * previously opened resource.
+ *
+ * The driver should:
+ * - Unlock and remove the resource if necessary, and release any system and device resources associated with this WPD object resource.
+ */
+HRESULT WpdObjectResources::OnRevert(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ // No results are expected to be returned
+ UNREFERENCED_PARAMETER(pResults);
+
+ HRESULT hr = S_OK;
+ LPWSTR pszContext = NULL;
+ ContextMap* pContextMap = NULL;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the context map which the driver stored in pParams for convenience
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, (IUnknown**)&pContextMap);
+ CHECK_HR(hr, "Failed to get PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
+ }
+
+ //Free the context
+ if (SUCCEEDED(hr))
+ {
+ hr = DestroyResourceContext(pContextMap, pszContext);
+ CHECK_HR(hr, "Failed to remove resource context [%ws]", pszContext);
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+
+ SAFE_RELEASE(pContextMap);
+
+ return hr;
+}
+
+/**
+ * This method is called when we receive a WPD_COMMAND_OBJECT_RESOURCES_SEEK
+ * command.
+ *
+ * The parameters sent to us are:
+ * - WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT: identifies the resource context associated with a
+ * previously opened resource.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET: Displacement to be added to the location indicated by the WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG parameter.
+ * - WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG: Specifies the origin of the displacement for the seek operation. Can be one of the following values:
+ * STREAM_SEEK_SET - Offset is from the beginning of the stream
+ * STREAM_SEEK_CUR - Offset is from the current position in the stream
+ * STREAM_SEEK_END - Offset is from the end of the stream
+ *
+ * The driver should:
+ * - Move the seek pointer for the resource to point to the specified position, so that
+ * subsequent read / write operations occur from the new position.
+ * - Return the new position as an offset relative to the start of the data stream in WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START.
+ */
+HRESULT WpdObjectResources::OnSeek(
+ _In_ IPortableDeviceValues* pParams,
+ _In_ IPortableDeviceValues* pResults)
+{
+ // No results are expected to be returned
+ UNREFERENCED_PARAMETER(pResults);
+
+ HRESULT hr = S_OK;
+ LPWSTR pszContext = NULL;
+ DWORD dwOrigin = 0;
+ LONGLONG lOffset = 0;
+
+ ResourceContext* pContext = NULL;
+
+ // Get the Object ID
+ hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT, &pszContext);
+ if (FAILED(hr))
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_CONTEXT");
+ }
+
+ // Get the offset
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetSignedLargeIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET, &lOffset);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_SEEK_OFFSET");
+ }
+
+ // Get the origin flags
+ if (SUCCEEDED(hr))
+ {
+ hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG, &dwOrigin);
+ CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_RESOURCES_SEEK_ORIGIN_FLAG");
+ }
+
+ // Get the context for this transfer
+ if (SUCCEEDED(hr))
+ {
+ hr = GetClientContext(pParams, pszContext, (IUnknown**) &pContext);
+ CHECK_HR(hr, "Failed to get resource context");
+ }
+
+ // Update the current seek pointer. For this driver, we update the NumBytesTransfered in the context so that subsequent reads/writes start
+ // at the appropriate place.
+ if (SUCCEEDED(hr))
+ {
+ ULONG ulSize = 0;
+ ULONG ulOriginalNumBytesTransfered = pContext->NumBytesTransfered;
+ CComPtr<IPortableDeviceValues> pResourceAttributes;
+
+ // Get the total size of the resource
+ hr = m_pFakeDevice->GetResourceAttributes(pContext->ObjectID, pContext->Key, &pResourceAttributes);
+ CHECK_HR(hr, "Failed to get attributes on [%ws]", pContext->ObjectID);
+ if (SUCCEEDED(hr))
+ {
+ hr = pResourceAttributes->GetUnsignedIntegerValue(WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE, &ulSize);
+ CHECK_HR(hr, "Failed to get WPD_RESOURCE_ATTRIBUTE_TOTAL_SIZE");
+ }
+
+ if(dwOrigin == STREAM_SEEK_CUR)
+ {
+ pContext->NumBytesTransfered += (LONG) lOffset;
+ }
+ else if(dwOrigin == STREAM_SEEK_SET)
+ {
+ pContext->NumBytesTransfered = (LONG) lOffset;
+ }
+ else
+ {
+ pContext->NumBytesTransfered = ulSize + (LONG) lOffset;
+ }
+
+ // Validate that this is in the correct range
+ if (SUCCEEDED(hr) && pContext->NumBytesTransfered > ulSize)
+ {
+ hr = E_INVALIDARG;
+ CHECK_HR(hr, "Attempting to seek beyond existing data");
+ // Restore the seek pointer to its original value
+ pContext->NumBytesTransfered = ulOriginalNumBytesTransfered;
+ }
+ }
+
+ // Set the return value
+ if (SUCCEEDED(hr))
+ {
+ hr = pResults->SetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START, pContext->NumBytesTransfered);
+ CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_RESOURCES_POSITION_FROM_START");
+ }
+
+ // Free the memory. CoTaskMemFree ignores NULLs so no need to check.
+ CoTaskMemFree(pszContext);
+
+ SAFE_RELEASE(pContext);
+
+ return hr;
+}
+
+HRESULT WpdObjectResources::CreateResourceContext(
+ _In_ ContextMap* pContextMap,
+ _In_ LPCWSTR pszObjectID,
+ _In_ REFPROPERTYKEY ResourceKey,
+ _In_ BOOL bCreateRequest,
+ _Outptr_ LPWSTR* ppszResourceContext)
+{
+ HRESULT hr = S_OK;
+ GUID guidContext = GUID_NULL;
+ ResourceContext* pContext = NULL;
+
+ if((pContextMap == NULL) ||
+ (pszObjectID == NULL) ||
+ (ppszResourceContext == NULL))
+ {
+ hr = E_POINTER;
+ CHECK_HR(hr, "Cannot have NULL parameter");
+ return hr;
+ }
+
+ *ppszResourceContext = NULL;
+
+ hr = CoCreateGuid(&guidContext);
+ CHECK_HR(hr, "Failed to CoCreateGuid used for identifying the resource context");
+
+ if (SUCCEEDED(hr))
+ {
+ pContext = new ResourceContext();
+ if(pContext == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ CHECK_HR(hr, "Failed to allocate new resource context");
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ pContext->ObjectID = pszObjectID;
+ pContext->Key = ResourceKey;
+ pContext->CreateRequest = bCreateRequest;
+
+ CAtlStringW strKey = CComBSTR(guidContext);
+ hr = pContextMap->Add(strKey, pContext);
+ CHECK_HR(hr, "Failed to insert bulk property operation context into our context Map");
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ hr = StringFromCLSID(guidContext, ppszResourceContext);
+ CHECK_HR(hr, "Failed to allocate string from GUID for resource context");
+ }
+
+ SAFE_RELEASE(pContext);
+
+ return hr;
+}
+
+HRESULT WpdObjectResources::DestroyResourceContext(
+ _In_ ContextMap* pContextMap,
+ _In_ LPCWSTR pszResourceContext)
+{
+ HRESULT hr = S_OK;
+
+ if(pszResourceContext == NULL)
+ {
+ hr = E_POINTER;
+ CHECK_HR(hr, "Cannot have NULL parameter");
+ return hr;
+ }
+
+ CAtlStringW strKey = pszResourceContext;
+ pContextMap->Remove(strKey);
+
+ return hr;
+}
+