/** * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES: Contains an IPortableDeviceValues, describing * properties of the new object. At the very least, it will contain: * - WPD_OBJECT_NAME: The object name. * - WPD_PARENT_ID: Identifies the parent object. The object should be inserted as a child of * this parent (e.g. this would be the target directory in a file system based device). * * The driver should: * - Create the object, and return its ID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID. */ HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesOnly( IPortableDeviceValues* pParams, IPortableDeviceValues* pResults) { HRESULT hr = S_OK; LPWSTR pszContext = NULL; CComPtr<IPortableDeviceValues> pValues; CComPtr<IPortableDeviceValues> pEventParams; // Get the Object Properties if (hr == S_OK) { hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pValues); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); } // Save the object to the device here. if (hr == S_OK) { hr = m_pFakeDevice->SaveNewObject(pValues, &pszContext); CHECK_HR(hr, "Failed to save new (properties only) object to device"); } if (hr == S_OK) { hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszContext); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); } // Send the event HRESULT hrTemp = S_OK; // Get the object properties used for sending the event hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszContext, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszContext); if (hrTemp == S_OK) { // Set the event-specific parameters hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); // Send the Event if (hrTemp == S_OK) { PostWpdEvent(pParams, pEventParams); } } // Free the memory. CoTaskMemFree ignores NULLs so no need to check. CoTaskMemFree(pszContext); return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_CREATE_OBJECT_WITH_PROPERTIES_ONLY * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES: Contains an IPortableDeviceValues, describing * properties of the new object. At the very least, it will contain: * - WPD_OBJECT_NAME: The object name. * - WPD_PARENT_ID: Identifies the parent object. The object should be inserted as a child of * this parent (e.g. this would be the target directory in a file system based device). * * The driver should: * - Create the object, and return its ID in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID. */ HRESULT WpdObjectManagement::OnCreateObjectWithPropertiesOnly( _In_ IPortableDeviceValues* pParams, _In_ IPortableDeviceValues* pResults) { HRESULT hr = S_OK; LPWSTR pszObjectID = NULL; CComPtr<IPortableDeviceValues> pObjectProperties; CComPtr<IPortableDeviceValues> pEventParams; hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**) &pEventParams); CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); // Get the Object Properties if (SUCCEEDED(hr)) { hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES, &pObjectProperties); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_CREATION_PROPERTIES"); } // Save the object to the device here. if (SUCCEEDED(hr)) { ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); hr = m_pDevice->CreatePropertiesOnlyObject(Scope, pObjectProperties, pEventParams, &pszObjectID); CHECK_HR(hr, "Failed to save new (properties only) object to device"); } if (SUCCEEDED(hr)) { // Create is successful, so we post an event. // This is best effort, so errors are ignored HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); CHECK_HR(hrEvent, "Failed post event for new object [%ws] (errors ignored)", pszObjectID); } if (SUCCEEDED(hr)) { hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectID); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); } // Free the memory. CoTaskMemFree ignores NULLs so no need to check. CoTaskMemFree(pszObjectID); return hr; }
HRESULT WpdObjectManagement::CommitNewObject( __in ObjectManagementContext* pContext, __in LPCWSTR pszContext, __in IPortableDeviceValues* pParams, __out IPortableDeviceValues* pResults) { LPWSTR pszObjectID = NULL; // Save the new object HRESULT hr = m_pFakeDevice->SaveNewObject(pContext->ObjectProperties, &pszObjectID); CHECK_HR(hr, "Failed to save new object [%ws] to device", pContext->Name); // Let the caller know the new object's ID. if (hr == S_OK) { hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectID); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); } if (hr == S_OK) { // Send the event HRESULT hrTemp = S_OK; CComPtr<IPortableDeviceValues> pEventParams; // Get the object properties used for sending the event hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID); if (hrTemp == S_OK) { // Set the event-specific parameters hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); hrTemp = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE, pszContext); CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE"); // Send the Event if (hrTemp == S_OK) { PostWpdEvent(pParams, pEventParams); } } } CoTaskMemFree(pszObjectID); return hr; }
HRESULT WpdObjectManagement::UpdateObject( __in ObjectManagementContext* pContext, __in IPortableDeviceValues* pParams, __out IPortableDeviceValues* pResults) { // For convenience, the ObjectID was stored in Name for object updates LPCWSTR pszObjectId = pContext->Name; // Update the object HRESULT hr = m_pFakeDevice->UpdateContentObject(pszObjectId, pContext->ObjectProperties); CHECK_HR(hr, "Failed to update object [%ws] to device", pszObjectId); // Set the object ID. if (hr == S_OK) { hr = pResults->SetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID, pszObjectId); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_ID"); } if (hr == S_OK) { // Send the event HRESULT hrTemp = S_OK; CComPtr<IPortableDeviceValues> pEventParams; // Get the object properties used for sending the event hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pContext->Name, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectId); if (hrTemp == S_OK) { // Set the event-specific parameters hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED); CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); // Send the Event if (hrTemp == S_OK) { PostWpdEvent(pParams, pEventParams); } } } return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_COPY_OBJECTS * command. * * This command will copy the specified objects to the destination folder. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS : the ObjectIDs, indicating which objects to copy. These may * be folder objects. * - WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID: Indicates the destination folder for the copy operation. * * The driver should: * - Attempt to copy the object specified in WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS to the folder specified in * WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID. * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS. It contains an IPortableDevicePropVariantCollection of * VT_ERROR values indicating the success or failure of the operation for that element. * Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on. * - The driver should return: * - S_OK if all objects were copied successfully. * - S_FALSE if any object copy failed. * - An error return indicates that the driver did not copy any objects, and * WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS is ignored. */ HRESULT WpdObjectManagement::OnCopy( IPortableDeviceValues* pParams, IPortableDeviceValues* pResults) { HRESULT hr = S_OK; LPWSTR pszDestFolderObjectID = NULL; CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; CComPtr<IPortableDevicePropVariantCollection> pCopyResults; BOOL bCopyFailed = FALSE; VARTYPE vt = VT_EMPTY; if (hr == S_OK) { hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); } // Ensure that this is a collection of VT_LPWSTR if (hr == S_OK) { hr = pObjectIDs->GetType(&vt); CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); if (hr == S_OK) { if (vt != VT_LPWSTR) { hr = E_INVALIDARG; CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR"); } } } if (hr == S_OK) { hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID, &pszDestFolderObjectID); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DESTINATION_FOLDER_OBJECT_ID"); } if (hr == S_OK) { hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDevicePropVariantCollection, (VOID**) &pCopyResults); CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); } if (hr == S_OK) { DWORD cObjects = 0; // Loop through the object list and attempt to copy hr = pObjectIDs->GetCount(&cObjects); CHECK_HR(hr, "Failed to get number of objects to copy"); if (hr == S_OK) { for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) { CComPtr<IPortableDeviceValues> pEventParams; LPWSTR pszNewObjectID = NULL; HRESULT hrTemp = S_OK; PROPVARIANT pv = {0}; PropVariantInit(&pv); // Get the next Object to copy hrTemp = pObjectIDs->GetAt(dwIndex, &pv); CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); if (hrTemp == S_OK) { // Copy this object hrTemp = m_pFakeDevice->CopyObject(pv.pwszVal, pszDestFolderObjectID, &pszNewObjectID); CHECK_HR(hrTemp, "Failed to copy object [%ws] to folder [%ws]", pv.pwszVal, pszDestFolderObjectID); if(FAILED(hrTemp)) { bCopyFailed = TRUE; } PROPVARIANT pvResult = {0}; PropVariantInit(&pvResult); // Save this result pvResult.vt = VT_ERROR; pvResult.scode = hrTemp; hrTemp = pCopyResults->Add(&pvResult); CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); PropVariantClear(&pvResult); if (hrTemp == S_OK) { // Send the event // Get the object properties used for sending the event hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszNewObjectID, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszNewObjectID); if (hrTemp == S_OK) { // Set the event-specific parameters hrTemp = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_ADDED); CHECK_HR(hrTemp, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); // Send the Event if (hrTemp == S_OK) { PostWpdEvent(pParams, pEventParams); } } } } if (pszNewObjectID != NULL) { CoTaskMemFree(pszNewObjectID); pszNewObjectID = NULL; } PropVariantClear(&pv); } } } // Set the results if (hr == S_OK) { hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS, pCopyResults); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_COPY_RESULTS"); } CoTaskMemFree(pszDestFolderObjectID); // If an object failed to copy, make sure we return S_FALSE if ((hr == S_OK) && (bCopyFailed)) { hr = S_FALSE; } return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS: the ObjectIDs, indicating which objects to delete. These may * contain children. * - WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS: Flag parameter indicating delete options. Must be one * of the following: * - PORTABLE_DEVICE_DELETE_NO_RECURSION - Deletes the * object only. This should fail if children exist. * - PORTABLE_DEVICE_DELETE_WITH_RECURSION - Deletes this * object and all children. * * The driver should: * - If the flag is PORTABLE_DEVICE_DELETE_NO_RECURSION the driver should delete the * specified object only. If the object still has children the driver should not delete * the object and instead return HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION). * - If the flag is PORTABLE_DEVICE_DELETE_WITH_RECURSION the driver should delete the * specified object and all of its children. * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS. It contains an IPortableDevicePropVariantCollection of * VT_ERROR values indicating the success or failure of the operation for that element. * Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on. * - The driver should return: * - S_OK if all objects were deleted successfully. * - S_FALSE if any object delete failed. * - An error return indicates that the driver did not delete any objects, and * WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS is ignored. */ HRESULT WpdObjectManagement::OnDelete( IPortableDeviceValues* pParams, IPortableDeviceValues* pResults) { HRESULT hr = S_OK; DWORD dwOptions = PORTABLE_DEVICE_DELETE_NO_RECURSION; BOOL bDeleteFailed = FALSE; CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; CComPtr<IPortableDevicePropVariantCollection> pDeleteResults; CComPtr<IPortableDeviceValues> pEventParams; VARTYPE vt = VT_EMPTY; if (hr == S_OK) { hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); } // Ensure that this is a collection of VT_LPWSTR if (hr == S_OK) { hr = pObjectIDs->GetType(&vt); CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); if (hr == S_OK) { if (vt != VT_LPWSTR) { hr = E_INVALIDARG; CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR"); } } } if (hr == S_OK) { hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS, &dwOptions); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS"); } if (hr == S_OK) { hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDevicePropVariantCollection, (VOID**) &pDeleteResults); CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); } if (hr == S_OK) { DWORD cObjects = 0; // Loop through the object list and attempt to delete hr = pObjectIDs->GetCount(&cObjects); CHECK_HR(hr, "Failed to get number of objects to delete"); if (hr == S_OK) { for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) { HRESULT hrTemp = S_OK; PROPVARIANT pv = {0}; PropVariantInit(&pv); // Get the next Object to delete hr = pObjectIDs->GetAt(dwIndex, &pv); CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); if (hr == S_OK) { pEventParams = NULL; // Get the object properties used for sending the event. Ignore errors (these are expected in some cases // e.g. app is sending object ID that doesn't exist), since this relates to the event, not the delete operation, // and we return results for the delete (an error posting the event is non-fatal). hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pv.pwszVal, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pv.pwszVal); HRESULT hrDelete = S_OK; PROPVARIANT pvResult = {0}; PropVariantInit(&pvResult); hrDelete = m_pFakeDevice->DeleteObject(dwOptions, pv.pwszVal); CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal); if(FAILED(hrDelete)) { bDeleteFailed = TRUE; } // Save this result pvResult.vt = VT_ERROR; pvResult.scode = hrDelete; hrTemp = pDeleteResults->Add(&pvResult); PropVariantClear(&pvResult); CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); if ((hrDelete == S_OK) && (hrTemp == S_OK)) { HRESULT hrEvent = S_OK; // Set the event-specific parameters hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_REMOVED); CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); // Send the Event if (hrEvent == S_OK) { PostWpdEvent(pParams, pEventParams); } } PropVariantClear(&pv); } else { break; } } } } // Set the results if (hr == S_OK) { hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS, pDeleteResults); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS"); } // If an object failed to delete, make sure we return S_FALSE if ((hr == S_OK) && (bDeleteFailed)) { hr = S_FALSE; } return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_NEXT * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT: the context the driver returned to * the client in OnGetValuesByObjectListStart. * * The driver should: * - Write the next set of property values, and return the write results in WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS. * If there are no more properties to be written, an empty collection should be returned. * - It is up to the driver to write as many object property values as it wants. If zero write results are returned * it is assumed the bulk operation is complete and the WPD_COMMAND_OBJECT_PROPERTIES_BULK_SET_VALUES_BY_OBJECT_LIST_END * will be called next. * * - S_OK should be returned if the collection can be returned successfully. * - Any error return indicates that the driver did not fill in any results, and the caller will * not attempt to unpack any property values. */ HRESULT WpdObjectPropertiesBulk::OnSetValuesByObjectListNext( _In_ IPortableDeviceValues* pParams, _In_ IPortableDeviceValues* pResults) { HRESULT hr = S_OK; LPWSTR pwszContext = NULL; BulkPropertiesContext* pContext = NULL; DWORD cObjects = 0; CComPtr<IPortableDeviceValues> pEventParams; CComPtr<IPortableDeviceValuesCollection> pWriteResults; CComPtr<IPortableDeviceValuesCollection> pValuesCollection; hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT, &pwszContext); CHECK_HR(hr, "Failed to get WPD_PROPERTY_OBJECT_PROPERTIES_BULK_CONTEXT from IPortableDeviceValues"); // Get the bulk property operation context if (SUCCEEDED(hr)) { hr = GetClientContext(pParams, pwszContext, (IUnknown**) &pContext); CHECK_HR(hr, "Failed to get bulk property context"); } // Make sure the the collection holds a ValuesCollection, then get the number of elements. if (SUCCEEDED(hr)) { if(pContext->ValuesCollection != NULL) { hr = pContext->ValuesCollection->GetCount(&cObjects); CHECK_HR(hr, "Failed to get number of objectIDs from bulk properties context"); } else { hr = E_INVALIDARG; CHECK_HR(hr, "Incorrect context specified - this context does not contain a values collection"); } } // Create the collection to hold the write results if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValuesCollection, (VOID**) &pWriteResults); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValuesCollection"); } // Create the collection to hold the event parameters if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**) &pEventParams); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); } if (SUCCEEDED(hr)) { for (DWORD dwIndex = pContext->NextObject; dwIndex < cObjects; dwIndex++) { CComPtr<IPortableDeviceValues> pValues; CComPtr<IPortableDeviceValues> pSetResults; bool bObjectChanged = false; hr = pContext->ValuesCollection->GetAt(dwIndex, &pValues); CHECK_HR(hr, "Failed to get next values from bulk properties context"); // CoCreate a collection to store the per object results. if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**) &pSetResults); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); } if (SUCCEEDED(hr)) { LPWSTR pszObjectID = NULL; // Get which object this is for hr = pValues->GetStringValue(WPD_OBJECT_ID, &pszObjectID); if (SUCCEEDED(hr)) { hr = m_pDevice->SetPropertyValues(pContext->Scope, pszObjectID, pValues, pSetResults, pEventParams, &bObjectChanged); CHECK_HR(hr, "Failed to get count of values"); } if (SUCCEEDED(hr)) { // Ensure the write results contain which ObjectID this was for hr = pSetResults->SetStringValue(WPD_OBJECT_ID, pszObjectID); CHECK_HR(hr, "Failed to set WPD_OBJECT_ID in write results"); if (SUCCEEDED(hr) && bObjectChanged) { // set property values is successful and object has changed, so we post an event. // This is best effort, so errors are ignored HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); CHECK_HR(hrEvent, "Failed post event for updated object [%ws] (errors ignored)", pszObjectID); } pEventParams->Clear(); } CoTaskMemFree(pszObjectID); } if (SUCCEEDED(hr)) { hr = pWriteResults->Add(pSetResults); CHECK_HR(hr, "Failed to add IPortableDeviceValues to IPortableDeviceValuesCollection"); } pContext->NextObject++; } } if (SUCCEEDED(hr)) { hr = pResults->SetIUnknownValue(WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS, pWriteResults); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_BULK_WRITE_RESULTS"); } // Free the memory. CoTaskMemFree ignores NULLs so no need to check. CoTaskMemFree(pwszContext); SAFE_RELEASE(pContext); return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_MANAGEMENT_DELETE_OBJECTS * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS: the ObjectIDs, indicating which objects to delete. These may * contain children. * - WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS: Flag parameter indicating delete options. Must be one * of the following: * - PORTABLE_DEVICE_DELETE_NO_RECURSION - Deletes the * object only. This should fail if children exist. * - PORTABLE_DEVICE_DELETE_WITH_RECURSION - Deletes this * object and all children. * * The driver should: * - If the flag is PORTABLE_DEVICE_DELETE_NO_RECURSION the driver should delete the * specified object only. If the object still has children the driver should not delete * the object and instead return HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION). * - If the flag is PORTABLE_DEVICE_DELETE_WITH_RECURSION the driver should delete the * specified object and all of its children. * - Fill out the operation results in WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS. It contains an IPortableDevicePropVariantCollection of * VT_ERROR values indicating the success or failure of the operation for that element. * Order is implicit, i.e. the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS corresponds to the first element of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS and so on. * - The driver should return: * - S_OK if all objects were deleted successfully. * - S_FALSE if any object delete failed. * - An error return indicates that the driver did not delete any objects, and * WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS is ignored. */ HRESULT WpdObjectManagement::OnDelete( _In_ IPortableDeviceValues* pParams, _In_ IPortableDeviceValues* pResults) { HRESULT hr = S_OK; DWORD dwOptions = PORTABLE_DEVICE_DELETE_NO_RECURSION; BOOL bDeleteFailed = FALSE; VARTYPE vt = VT_EMPTY; CComPtr<IPortableDevicePropVariantCollection> pObjectIDs; CComPtr<IPortableDevicePropVariantCollection> pDeleteResults; CComPtr<IPortableDeviceValues> pEventParams; if (hr == S_OK) { hr = pParams->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS, &pObjectIDs); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); } // Ensure that this is a collection of VT_LPWSTR if (hr == S_OK) { hr = pObjectIDs->GetType(&vt); CHECK_HR(hr, "Failed to get the VARTYP of WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS"); if (hr == S_OK) { if (vt != VT_LPWSTR) { hr = E_INVALIDARG; CHECK_HR(hr, "WPD_PROPERTY_OBJECT_MANAGEMENT_OBJECT_IDS is not a collection of VT_LPWSTR"); } } } if (hr == S_OK) { hr = pParams->GetUnsignedIntegerValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS, &dwOptions); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_OPTIONS"); } if (hr == S_OK) { hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDevicePropVariantCollection, (VOID**) &pDeleteResults); CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDevicePropVariantCollection"); } if (hr == S_OK) { hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**) &pEventParams); CHECK_HR(hr, "Failed to CoCreateInstance CLSID_PortableDeviceValues"); } if (hr == S_OK) { DWORD cObjects = 0; // Loop through the object list and attempt to delete hr = pObjectIDs->GetCount(&cObjects); CHECK_HR(hr, "Failed to get number of objects to delete"); if (hr == S_OK) { ACCESS_SCOPE Scope = m_pDevice->GetAccessScope(pParams); for(DWORD dwIndex = 0; dwIndex < cObjects; dwIndex++) { HRESULT hrTemp = S_OK; PROPVARIANT pv = {0}; PropVariantInit(&pv); // Get the next Object to delete hr = pObjectIDs->GetAt(dwIndex, &pv); CHECK_HR(hr, "Failed to get next object id at index %d", dwIndex); if (hr == S_OK) { HRESULT hrDelete = S_OK; PROPVARIANT pvResult = {0}; PropVariantInit(&pvResult); hrDelete = m_pDevice->DeleteObject(Scope, dwOptions, pv.pwszVal, pEventParams); CHECK_HR(hrDelete, "Failed to delete object [%ws]", pv.pwszVal); if(FAILED(hrDelete)) { bDeleteFailed = TRUE; } else { // Delete is successful, so we post an event. // This is best effort, so errors are ignored HRESULT hrEvent = PostWpdEvent(pParams, pEventParams); CHECK_HR(hrEvent, "Failed post event for deleted object [%ws] (errors ignored)", pv.pwszVal); } // Clear event parameters for reuse pEventParams->Clear(); // Save this result pvResult.vt = VT_ERROR; pvResult.scode = hrDelete; hrTemp = pDeleteResults->Add(&pvResult); PropVariantClear(&pvResult); CHECK_HR(hrTemp, "Failed to add result for [%ws] to list of results", pv.pwszVal); PropVariantClear(&pv); } else { break; } } } } // Set the results if (hr == S_OK) { hr = pResults->SetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS, pDeleteResults); CHECK_HR(hr, "Failed to set WPD_PROPERTY_OBJECT_MANAGEMENT_DELETE_RESULTS"); } // If an object failed to delete, make sure we return S_FALSE if ((hr == S_OK) && (bDeleteFailed)) { hr = S_FALSE; } return hr; }
HRESULT WpdServiceMethods::DispatchMethod( _In_ LPCWSTR pwszContext, _In_ IPortableDeviceValues* pStartParams, _COM_Outptr_ IPortableDeviceValues** ppResults) { HRESULT hr = S_OK; HRESULT hrStatus = S_OK; GUID Method = GUID_NULL; CComPtr<IPortableDeviceValues> pMethodParams; CComPtr<IPortableDeviceValues> pMethodResults; *ppResults = NULL; // Get the method GUID hr = pStartParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method); CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD"); // Get the method parameters. These can be optional if the methods don't require parameters if (SUCCEEDED(hr)) { HRESULT hrTemp = pStartParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES, &pMethodParams); CHECK_HR(hrTemp, "Failed to get WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES (ok if method does not require parameters)"); } // Prepare the results collection if (SUCCEEDED(hr)) { hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**)&pMethodResults); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); } if (SUCCEEDED(hr)) { // Invoke the method if (IsEqualGUID(METHOD_FullEnumSyncSvc_BeginSync, Method)) { hrStatus = m_pContactsService->OnBeginSync(pMethodParams, *ppResults); CHECK_HR(hrStatus, "BeginSync method failed"); } else if (IsEqualGUID(METHOD_FullEnumSyncSvc_EndSync, Method)) { hrStatus = m_pContactsService->OnEndSync(pMethodParams, *ppResults); CHECK_HR(hrStatus, "EndSync method failed"); } else if (IsEqualGUID(MyCustomMethod, Method)) { CComPtr<IPortableDeviceValues> pCustomEventParams; hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**)&pCustomEventParams); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); if (SUCCEEDED(hr)) { hrStatus = m_pContactsService->OnMyCustomMethod(pMethodParams, pMethodResults, pCustomEventParams); CHECK_HR(hrStatus, "MyCustomMethod method failed"); } if (SUCCEEDED(hr)) { // In addition to a method complete event, we can also send a custom event, // for example, to indicate progress of the method hr = PostWpdEvent(pStartParams, pCustomEventParams); CHECK_HR(hr, "Failed to post custom event"); } } else { hrStatus = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); CHECK_HR(hr, "Unknown method %ws received",CComBSTR(Method)); } } // We always want to post a method completion event // Even if the method has failed { CComPtr<IPortableDeviceValues> pEventParams; hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**)&pEventParams); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); if (SUCCEEDED(hr)) { hr = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_SERVICE_METHOD_COMPLETE); CHECK_HR(hr, "Failed to set the event id to WPD_EVENT_SERVICE_METHOD_COMPLETE"); } if (SUCCEEDED(hr)) { hr = pEventParams->SetStringValue(WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT, pwszContext); CHECK_HR(hr, "Failed to set the method context for WPD_EVENT_SERVICE_METHOD_COMPLETE"); } if (SUCCEEDED(hr)) { hr = PostWpdEvent(pStartParams, pEventParams); CHECK_HR(hr, "Failed to post WPD_EVENT_SERVICE_METHOD_COMPLETE"); } } if (SUCCEEDED(hr)) { hr = hrStatus; } if (SUCCEEDED(hr)) { *ppResults = pMethodResults.Detach(); } return hr; }
HRESULT WpdServiceMethods::DispatchMethod( _In_ LPCWSTR pwszContext, _In_ IPortableDeviceValues* pStartParams, _Outptr_ IPortableDeviceValues** ppResults) { HRESULT hr = S_OK; HRESULT hrStatus = S_OK; GUID Method = GUID_NULL; CComPtr<IPortableDeviceValues> pMethodParams; CComPtr<IPortableDeviceValues> pMethodResults; *ppResults = NULL; // Get the method GUID hr = pStartParams->GetGuidValue(WPD_PROPERTY_SERVICE_METHOD, &Method); CHECK_HR(hr, "Failed to get WPD_PROPERTY_SERVICE_METHOD"); // Get the method parameters. These can be optional if the methods don't require parameters if (hr == S_OK) { HRESULT hrTemp = pStartParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES, &pMethodParams); CHECK_HR(hrTemp, "Failed to get WPD_PROPERTY_SERVICE_METHOD_PARAMETER_VALUES (ok if method does not require parameters)"); } // Prepare the results collection if (hr == S_OK) { hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER, IID_IPortableDeviceValues, (VOID**)ppResults); CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues"); } if (hr == S_OK) { // Invoke the method to the service m_pGattService->OnMethodInvoke(Method, pMethodParams, *ppResults); } // Post the completion event if the method has failed. { if (hr == S_OK) { hr = (*ppResults)->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_SERVICE_METHOD_COMPLETE); CHECK_HR(hr, "Failed to set the event id to WPD_EVENT_SERVICE_METHOD_COMPLETE"); } if (hr == S_OK) { hr = (*ppResults)->SetStringValue(WPD_EVENT_PARAMETER_SERVICE_METHOD_CONTEXT, pwszContext); CHECK_HR(hr, "Failed to set the method context for WPD_EVENT_SERVICE_METHOD_COMPLETE"); } if (hr == S_OK) { hr = PostWpdEvent(pStartParams, *ppResults); CHECK_HR(hr, "Failed to post WPD_EVENT_SERVICE_METHOD_COMPLETE"); } } if (hr == S_OK) { hr = hrStatus; } return hr; }
/** * This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET * command. * * The parameters sent to us are: * - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return. * - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which * specific property values we are requested to write. * * The driver should: * - Write all requested property values. For each property, a write result should be returned in the * write result property store. * - If any property write failed, the corresponding write result value should be * set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure. * - S_OK should be returned if all properties were written successfully. * - S_FALSE should be returned if any property write failed. * - Any error return indicates that the driver did not write any results, and the caller will * not attempt to unpack any property write results. */ HRESULT WpdObjectProperties::OnWriteProperties( IPortableDeviceValues* pParams, IPortableDeviceValues* pResults) { HRESULT hr = S_OK; LPWSTR pszObjectID = NULL; CComPtr<IPortableDeviceValues> pValues; CComPtr<IPortableDeviceValues> pWriteResults; CComPtr<IPortableDeviceValues> pEventParams; if (hr == S_OK) { hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &pszObjectID); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID"); } if (hr == S_OK) { hr = pParams->GetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, &pValues); CHECK_HR(hr, "Missing value for WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES"); } if (hr == S_OK) { hr = m_pFakeDevice->WritePropertiesOnObject(pszObjectID, pValues, &pWriteResults); if(FAILED(hr)) { CHECK_HR(hr, "Failed to write properties on [%ws]", pszObjectID); } } // Be sure to preserve possible S_FALSE returns if (SUCCEEDED(hr)) { HRESULT hrTemp = S_OK; hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS, pWriteResults); CHECK_HR(hrTemp, ("Failed to set WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_WRITE_RESULTS")); if(FAILED(hrTemp)) { hr = hrTemp; } pEventParams = NULL; hrTemp = m_pFakeDevice->GetObjectPropertiesForEvent(pszObjectID, &pEventParams); CHECK_HR(hrTemp, "Failed to get properties for event on object %ws", pszObjectID); if (hrTemp == S_OK) { HRESULT hrEvent = S_OK; // Set the event-specific parameters hrEvent = pEventParams->SetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, WPD_EVENT_OBJECT_UPDATED); CHECK_HR(hrEvent, "Failed to add WPD_EVENT_PARAMETER_EVENT_ID"); // Send the Event if (hrEvent == S_OK) { PostWpdEvent(pParams, pEventParams); } } } // Free the memory. CoTaskMemFree ignores NULLs so no need to check. CoTaskMemFree(pszObjectID); return hr; }