Sample Code

Windows Driver Samples/ WPD WUDF sample driver/ C++/ FakeDevice.h/

#include "FakeDevice.h.tmh"

#define NUM_VERTICAL_OBJECTS    7
#define NUM_IMAGE_OBJECTS       31
#define NUM_MUSIC_OBJECTS       8
#define NUM_VIDEO_OBJECTS       4
#define NUM_MEMO_OBJECTS        2
#define NUM_CONTACT_OBJECTS     5
#define STORAGE1_OBJECT_ID                      L"Storage1"
#define STORAGE2_OBJECT_ID                      L"Storage2"
#define RENDERING_INFORMATION_OBJECT_ID         L"RenderingInformation"
#define NETWORK_CONFIG_OBJECT_ID                L"NetworkConfig"
#define UNTYPEDDATA_FOLDER_OBJECT_ID            L"UntypedData"
#define CONTACT_FOLDER_OBJECT_ID                L"Phonebook"
#define MEDIA_FOLDER_OBJECT_ID                  L"Media folder"
#define MEMO_FOLDER_OBJECT_ID                   L"Memo folder"
#define IMAGE_FOLDER_OBJECT_ID                  L"Picture folder"
#define MUSIC_FOLDER_OBJECT_ID                  L"Music folder"
#define VIDEO_FOLDER_OBJECT_ID                  L"Video folder"

/**
 * This class represents an abstraction of a real device.
 * Driver implementors should replace this with their own
 * device I/O classes/libraries.
 */
class FakeDevice
{
public:
    FakeDevice() :
        m_dwLastObjectID(0)
    {

    }

    ~FakeDevice()
    {
        for(size_t index = 0; index < m_Content.GetCount(); index++)
        {
            delete m_Content[index];
            m_Content[index] = NULL;
        }
    }

    HRESULT InitializeContent(
        _In_ IPortableDeviceClassExtension *pPortableDeviceClassExtension)
    {
        HRESULT hr = S_OK;

        if(pPortableDeviceClassExtension == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL parameter");
            return hr;
        }
        
        // Add device object
        CAutoPtr<DeviceObjectFakeContent> pDeviceObjectContent(new DeviceObjectFakeContent(pPortableDeviceClassExtension));
        if (pDeviceObjectContent)
        {
            pDeviceObjectContent->Name          = L"Fake Device";
            pDeviceObjectContent->ParentID      = L"";
            pDeviceObjectContent->ObjectID      = WPD_DEVICE_OBJECT_ID;
            pDeviceObjectContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pDeviceObjectContent->ObjectID.GetString());
            pDeviceObjectContent->CanDelete     = FALSE;
            pDeviceObjectContent->IsHidden      = TRUE;
            pDeviceObjectContent->ContentType   = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;

            _ATLTRY
            {
                m_Content.Add(pDeviceObjectContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding DeviceObjectFakeContent");
                return hr;
            }
            pDeviceObjectContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: Storage1
        CAutoPtr<StorageObjectFakeContent> pStorageContent(new StorageObjectFakeContent());
        if (pStorageContent)
        {
            pStorageContent->Name                  = STORAGE1_OBJECT_ID;
            pStorageContent->ParentID              = WPD_DEVICE_OBJECT_ID;
            pStorageContent->ObjectID              = pStorageContent->Name;
            pStorageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pStorageContent->ObjectID.GetString());
            pStorageContent->CanDelete             = FALSE;
            pStorageContent->ContentType           = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;
            pStorageContent->IsExternalStorage     = FALSE;
            pStorageContent->Capacity              = 120034890000;
            pStorageContent->FreeSpaceInBytes      = 60017445000;

            _ATLTRY
            {
                m_Content.Add(pStorageContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding StorageObjectFakeContent");
                return hr;
            }
            pStorageContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: Storage2
        pStorageContent.Attach(new StorageObjectFakeContent());
        if (pStorageContent)
        {
            pStorageContent->Name                  = STORAGE2_OBJECT_ID;
            pStorageContent->ParentID              = WPD_DEVICE_OBJECT_ID;
            pStorageContent->ObjectID              = pStorageContent->Name;
            pStorageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pStorageContent->ObjectID.GetString());
            pStorageContent->CanDelete             = FALSE;
            pStorageContent->ContentType           = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;
            pStorageContent->IsExternalStorage     = TRUE;
            pStorageContent->Capacity              = 80000000000;
            pStorageContent->FreeSpaceInBytes      = 75000000000;

            _ATLTRY
            {
                m_Content.Add(pStorageContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding StorageObjectFakeContent");
                return hr;
            }
            pStorageContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: RenderingInformation
        CAutoPtr<RenderingInformationFakeContent> pRenderingInformationContent(new RenderingInformationFakeContent());
        if (pRenderingInformationContent)
        {
            pRenderingInformationContent->Name                  = RENDERING_INFORMATION_OBJECT_ID;
            pRenderingInformationContent->ParentID              = WPD_DEVICE_OBJECT_ID;
            pRenderingInformationContent->ObjectID              = pRenderingInformationContent->Name;
            pRenderingInformationContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pRenderingInformationContent->ObjectID.GetString());
            pRenderingInformationContent->CanDelete             = FALSE;
            pRenderingInformationContent->IsHidden              = TRUE;
            pRenderingInformationContent->ContentType           = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;

            _ATLTRY
            {
                m_Content.Add(pRenderingInformationContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding RenderingInformationFakeContent");
                return hr;
            }
            pRenderingInformationContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: NetworkConfig
        CAutoPtr<NetworkConfigFakeContent> pNetworkConfigContent(new NetworkConfigFakeContent());
        if (pNetworkConfigContent)
        {
            pNetworkConfigContent->Name                  = NETWORK_CONFIG_OBJECT_ID;
            pNetworkConfigContent->ParentID              = WPD_DEVICE_OBJECT_ID;
            pNetworkConfigContent->ObjectID              = pNetworkConfigContent->Name;
            pNetworkConfigContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pNetworkConfigContent->ObjectID.GetString());
            pNetworkConfigContent->CanDelete             = FALSE;
            pNetworkConfigContent->IsHidden              = TRUE;
            pNetworkConfigContent->ContentType           = WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT;
            pNetworkConfigContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_NETWORK_ASSOCIATION);
            pNetworkConfigContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_WIRELESS_PROFILE);

            _ATLTRY
            {
                m_Content.Add(pNetworkConfigContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding NetworkConfigFakeContent");
                return hr;
            }
            pNetworkConfigContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: Untyped data folder
        CAutoPtr<FakeFolderContent> pFolderContent(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = UNTYPEDDATA_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = STORAGE2_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: Phonebook folder
        pFolderContent.Attach(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = CONTACT_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = STORAGE1_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_CONTACT);

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel folder object with custom icon: Memos
        CAutoPtr<FakeMemoFolderContent> pMemoFolderContent(new FakeMemoFolderContent());
        if (pMemoFolderContent)
        {
            pMemoFolderContent->Name                  = MEMO_FOLDER_OBJECT_ID;
            pMemoFolderContent->ParentID              = STORAGE1_OBJECT_ID;
            pMemoFolderContent->ObjectID              = pMemoFolderContent->Name;
            pMemoFolderContent->FileName              = pMemoFolderContent->Name;
            pMemoFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMemoFolderContent->ObjectID.GetString());
            pMemoFolderContent->CanDelete             = FALSE;
            pMemoFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pMemoFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_MEMO);

            _ATLTRY
            {
                m_Content.Add(pMemoFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeMemoFolderContent");
                return hr;
            }
            pMemoFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Toplevel object: Media folder
        pFolderContent.Attach(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = MEDIA_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = STORAGE1_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_FOLDER);

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Media folder: Picture folder
        pFolderContent.Attach(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = IMAGE_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = MEDIA_FOLDER_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_IMAGE);

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Media folder: Music folder
        pFolderContent.Attach(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = MUSIC_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = MEDIA_FOLDER_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_AUDIO);

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add Media folder: Video folder
        pFolderContent.Attach(new FakeFolderContent());
        if (pFolderContent)
        {
            pFolderContent->Name                  = VIDEO_FOLDER_OBJECT_ID;
            pFolderContent->ParentID              = MEDIA_FOLDER_OBJECT_ID;
            pFolderContent->ObjectID              = pFolderContent->Name;
            pFolderContent->FileName              = pFolderContent->Name;
            pFolderContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pFolderContent->ObjectID.GetString());
            pFolderContent->CanDelete             = FALSE;
            pFolderContent->ContentType           = WPD_CONTENT_TYPE_FOLDER;
            pFolderContent->RestrictToContentTypes.Add(WPD_CONTENT_TYPE_VIDEO);

            _ATLTRY
            {
                m_Content.Add(pFolderContent);
            }
            _ATLCATCH(e)
            {
                hr = e;
                CHECK_HR(hr, "ATL Exception when adding FakeFolderContent");
                return hr;
            }
            pFolderContent.Detach();
        }
        else
        {
            hr = E_OUTOFMEMORY;
            return hr;
        }

        // Add generic file content objects to storage 1
        for(DWORD dwIndex = 0; dwIndex < NUM_VERTICAL_OBJECTS; dwIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeGenericFileContent> pGenericFileContent(new FakeGenericFileContent());
            if (pGenericFileContent)
            {
                pGenericFileContent->Name.Format(L"Name%d", m_dwLastObjectID);
                pGenericFileContent->ParentID      = STORAGE1_OBJECT_ID;
                pGenericFileContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pGenericFileContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pGenericFileContent->ObjectID.GetString());
                pGenericFileContent->ContentType           = WPD_CONTENT_TYPE_UNSPECIFIED;
                pGenericFileContent->CanDelete             = FALSE;
                pGenericFileContent->IsHidden              = TRUE;
                pGenericFileContent->IsSystem              = TRUE;
                pGenericFileContent->FileName              = pGenericFileContent->Name;

                _ATLTRY
                {
                    m_Content.Add(pGenericFileContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeGenericFileContent");
                    return hr;
                }
                pGenericFileContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }

        // Add image objects to the image folder
        for(DWORD dwImageIndex = 0; dwImageIndex < NUM_IMAGE_OBJECTS; dwImageIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeImageContent> pImageContent(new FakeImageContent());
            if (pImageContent)
            {
                pImageContent->Name.Format(L"Image%d", m_dwLastObjectID);
                pImageContent->ParentID      = IMAGE_FOLDER_OBJECT_ID;
                pImageContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pImageContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pImageContent->ObjectID.GetString());
                pImageContent->ContentType   = WPD_CONTENT_TYPE_IMAGE;
                pImageContent->FileName.Format(L"ImageFile_%d.jpg", m_dwLastObjectID);

                _ATLTRY
                {
                    m_Content.Add(pImageContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeImageContent");
                    return hr;
                }
                pImageContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }

        // Add music objects to the music folder
        for(DWORD dwMusicIndex = 0; dwMusicIndex < NUM_MUSIC_OBJECTS; dwMusicIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeMusicContent> pMusicContent(new FakeMusicContent());
            if (pMusicContent)
            {
                pMusicContent->Name.Format(L"Music%d", m_dwLastObjectID);
                pMusicContent->ParentID      = MUSIC_FOLDER_OBJECT_ID;
                pMusicContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pMusicContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMusicContent->ObjectID.GetString());
                pMusicContent->ContentType   = WPD_CONTENT_TYPE_AUDIO;
                pMusicContent->FileName.Format(L"MusicFile_%d.wma", m_dwLastObjectID);

                _ATLTRY
                {
                    m_Content.Add(pMusicContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeMusicContent");
                    return hr;
                }
                pMusicContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }

        // Add video objects to the video folder
        for(DWORD dwVideoIndex = 0; dwVideoIndex < NUM_VIDEO_OBJECTS; dwVideoIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeVideoContent> pVideoContent(new FakeVideoContent());
            if (pVideoContent)
            {
                pVideoContent->Name.Format(L"Video%d", m_dwLastObjectID);
                pVideoContent->ParentID      = VIDEO_FOLDER_OBJECT_ID;
                pVideoContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pVideoContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pVideoContent->ObjectID.GetString());
                pVideoContent->ContentType   = WPD_CONTENT_TYPE_VIDEO;
                pVideoContent->FileName.Format(L"VideoFile_%d.wmv", m_dwLastObjectID);

                _ATLTRY
                {
                    m_Content.Add(pVideoContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeVideoContent");
                    return hr;
                }
                pVideoContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }

        // Add contact objects to the contact folder
        for(DWORD dwContactIndex = 0; dwContactIndex < NUM_CONTACT_OBJECTS; dwContactIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeContactContent> pContactContent(new FakeContactContent());
            if (pContactContent)
            {
                if ((m_dwLastObjectID % 2) == 0) // Mark every second contact as having a photo
                {
                    pContactContent->bHasContactPhoto = TRUE;
                }
                pContactContent->Name.Format(L"Contact%d", m_dwLastObjectID);
                pContactContent->ParentID       = CONTACT_FOLDER_OBJECT_ID;
                pContactContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContactContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContactContent->ObjectID.GetString());
                pContactContent->ContentType    = WPD_CONTENT_TYPE_CONTACT;
                pContactContent->DisplayName.Format(L"Surname%d, FirstName%d", dwContactIndex, dwContactIndex);
                pContactContent->PrimaryPhone   = L"(425) 555 0821";
                pContactContent->WorkPhone      = L"(425) 556 6010";
                pContactContent->CellPhone      = L"(206) 557 1441";

                _ATLTRY
                {
                    m_Content.Add(pContactContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeContactContent");
                    return hr;
                }
                pContactContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }

        // Add memo objects to the memo folder
        for(DWORD dwMemoIndex = 0; dwMemoIndex < NUM_MEMO_OBJECTS; dwMemoIndex++)
        {
            m_dwLastObjectID++;

            CAutoPtr<FakeMemoContent> pMemoContent(new FakeMemoContent());
            if (pMemoContent)
            {
                pMemoContent->Name.Format(L"Memo%d", m_dwLastObjectID);
                pMemoContent->ParentID      = MEMO_FOLDER_OBJECT_ID;
                pMemoContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pMemoContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pMemoContent->ObjectID.GetString());
                pMemoContent->ContentType   = WPD_CONTENT_TYPE_MEMO;
                pMemoContent->FileName.Format(L"MemoFile_%d.mem", m_dwLastObjectID);

                _ATLTRY
                {
                    m_Content.Add(pMemoContent);
                }
                _ATLCATCH(e)
                {
                    hr = e;
                    CHECK_HR(hr, "ATL Exception when adding FakeMemoContent");
                    return hr;
                }
                pMemoContent.Detach();
            }
            else
            {
                hr = E_OUTOFMEMORY;
                return hr;
            }
        }
        
        return hr;
    }

    bool FindNext(      const DWORD           dwStartIndex,
                  _In_  const CAtlStringW&    strParentID,
                  _Out_ CAtlStringW&          strObjectID,
                  _Out_ DWORD*                pdwNextStartIndex)
    {
        DWORD curIndex = 0;
        bool  bFound   = false;
        strObjectID = L"";
        *pdwNextStartIndex = 0;

        if(dwStartIndex >= m_Content.GetCount())
        {
            return false;
        }

        for(curIndex = dwStartIndex; curIndex < m_Content.GetCount(); curIndex++)
        {
            if(m_Content[curIndex]->ParentID == strParentID)
            {
                strObjectID = m_Content[curIndex]->ObjectID;
                bFound = true;
                break;
            }
        }

        if(bFound)
        {
            *pdwNextStartIndex = curIndex + 1;
        }
        else
        {
            *pdwNextStartIndex = (DWORD) m_Content.GetCount();
        }

        return bFound;
    }

    /**
     * Returns a pointer to the fake content in pElement corresponding to
     * the specified ObjectID.
     * Return value is true if found, otherwise false.
     */
    _Success_(return)
    bool GetContent(_In_ LPCWSTR pszObjectID, _Outptr_result_nullonfailure_ FakeContent** ppElement)
    {
        bool    bFound  = false;

        if((pszObjectID == NULL) ||
           (ppElement   == NULL))
        {
            return false;
        }

        *ppElement = NULL;

        for(DWORD Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if(m_Content[Index]->ObjectID == pszObjectID)
            {
                *ppElement = m_Content[Index];
                bFound = true;
                break;
            }
        }

        return bFound;
    }

    /**
     * Returns the index of the fake content corresponding to the
     * specified ObjectID.
     * Return value is true if found, otherwise false.
     */
    bool GetContentIndex(_In_ LPCWSTR pszObjectID, _Out_ DWORD* pIndex)
    {
        bool bFound  = false;

        if((pszObjectID == NULL) ||
           (pIndex      == NULL))
        {
            return false;
        }

        *pIndex = 0;

        for(DWORD Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if(m_Content[Index]->ObjectID == pszObjectID)
            {
                *pIndex = Index;
                bFound = true;
                break;
            }
        }

        return bFound;
    }

    HRESULT GetSupportedProperties(
        _In_         LPCWSTR                         pszObjectID,
        _COM_Outptr_ IPortableDeviceKeyCollection**  ppCollection)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        *ppCollection = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->GetSupportedProperties(ppCollection);
            CHECK_HR(hr, "Failed to get supported properties on element [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        return hr;
    }

    HRESULT GetAllValues(
        _In_         LPCWSTR                 pszObjectID,
        _COM_Outptr_ IPortableDeviceValues** ppValues)
    {
        HRESULT                 hr          = S_OK;
        FakeContent*            pElement    = NULL;

        if(ppValues == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL attributes parameter");
            return hr;
        }

        *ppValues = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            CComPtr<IPortableDeviceValues> pValues;

            hr = CoCreateInstance(CLSID_PortableDeviceValues,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDeviceValues,
                                  (VOID**) &pValues);
            CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");

            if (SUCCEEDED(hr))
            {
                hr = pElement->GetAllValues(pValues);
                CHECK_HR(hr, "Failed to fill property values");

                if (SUCCEEDED(hr))
                {
                    // Keep hr intact in case it is S_FALSE
                    HRESULT hrTemp = pValues->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppValues);

                    if (FAILED(hrTemp))
                    {
                        hr = hrTemp;
                        CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues");
                    }
                }
            }
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        return hr;
    }

    /**
     * This fake device is an unrealistic example from a performance standpoint, since
     * requesting some values is more costly than requesting all values.
     * The reason for this is that is is easier for the underlying objects to simply return all the
     * values, and for us to filter out only the ones we want here.
     * It is not expected that real devices function this way.
     */
    HRESULT GetValues(
        _In_            LPCWSTR                         pszObjectID,
        _In_            IPortableDeviceKeyCollection*   pKeys,
        _COM_Outptr_    IPortableDeviceValues**         ppValues)
    {
        HRESULT                 hr      = S_OK;
        BOOL                    bError  = FALSE;
        CComPtr<IPortableDeviceValues>  pAllValues;
        CComPtr<IPortableDeviceValues>  pValues;

        if((pszObjectID == NULL) ||
           (pKeys       == NULL) ||
           (ppValues    == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return hr;
        }

        *ppValues = NULL;

        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(CLSID_PortableDeviceValues,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDeviceValues,
                                  (VOID**) &pValues);
            CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDevicePropVariantCollection");
        }

        // Get ALL values (NOTE: This is inefficient)
        if (SUCCEEDED(hr))
        {
            hr = GetAllValues(pszObjectID, &pAllValues);
            CHECK_HR(hr, "Failed to get property values [%ws]", pszObjectID);
        }

        // Return only the requested values
        if (SUCCEEDED(hr))
        {
            PROPVARIANT pv      = {0};
            DWORD       dwIndex = 0;
            PROPERTYKEY Key     = WPD_PROPERTY_NULL;
            while(SUCCEEDED(hr) && pKeys->GetAt(dwIndex, &Key) == S_OK)
            {
                hr = pAllValues->GetValue(Key, &pv);
                if(FAILED(hr))
                {
                    bError = TRUE;
                    hr = pValues->SetErrorValue(Key, hr);
                    CHECK_HR(hr, "Failed to set error value for %ws.%d", (LPWSTR)CComBSTR(Key.fmtid),Key.pid);
                }
                else
                {
                    hr = pValues->SetValue(Key, &pv);
                    CHECK_HR(hr, "Failed to set property value %ws.%d", (LPWSTR)CComBSTR(Key.fmtid),Key.pid);
                }

                PropVariantClear(&pv);
                dwIndex++;
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = pValues->QueryInterface(IID_IPortableDeviceValues, (VOID**) ppValues);
            CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on Wpd IPortableDeviceValues");
        }

        if (SUCCEEDED(hr) &&
           (bError == TRUE))
        {
            hr = S_FALSE;
        }

        return hr;
    }

    HRESULT WriteProperty(
        _In_    LPCWSTR         pszObjectID,
        _In_    REFPROPERTYKEY  key,
        _In_    REFPROPVARIANT  Value)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->WriteValue(key, Value);
            CHECK_HR(hr, "Failed to write property value");
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        return hr;
    }

    HRESULT WritePropertiesOnObject(
        _In_            LPCWSTR                 pszObjectID,
        _In_            IPortableDeviceValues*  pValues,
        _COM_Outptr_    IPortableDeviceValues** ppWriteResults)
    {
        HRESULT                 hr              = S_OK;
        bool                    bErrorOnWrite   = false;
        DWORD                   cValues         = 0;
        CComPtr<IPortableDeviceValues> pWriteResults;

        *ppWriteResults = NULL;

        // Create property store  for values
        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(CLSID_PortableDeviceValues,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDeviceValues,
                                  (VOID**)&pWriteResults);
            CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
        }

        if (SUCCEEDED(hr))
        {
            hr = pValues->GetCount(&cValues);
            CHECK_HR(hr, "Failed to get property count");
        }

        if (SUCCEEDED(hr))
        {
            for(DWORD dwIndex = 0; dwIndex < cValues; dwIndex++)
            {
                HRESULT             hrWrite = S_OK;
                PROPERTYKEY         Key     = {0};
                PropVariantWrapper  pvValue;
                PropVariantWrapper  pvWriteResult;

                hrWrite = pValues->GetAt(dwIndex, &Key, &pvValue);
                CHECK_HR(hrWrite, "Failed to get property key/value at index %d", dwIndex);

                if (SUCCEEDED(hrWrite))
                {
                    FakeContent*    pElement    = NULL;
                    if(GetContent(pszObjectID, &pElement))
                    {
                        hr = pElement->WriteValue(Key, pvValue);
                        CHECK_HR(hr, "Failed to write property value");
                    }
                    else
                    {
                        hr = E_INVALIDARG;
                        CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
                    }
                }

                // Always set the write result for this property
                pvWriteResult.SetErrorValue(hrWrite);
                if(FAILED(hrWrite))
                {
                    bErrorOnWrite = true;
                }

                hrWrite  = pWriteResults->SetValue(Key, &pvWriteResult);
                CHECK_HR(hrWrite, "Failed to set write result for property %ws.%d", CComBSTR(Key.fmtid), Key.pid);
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = pWriteResults->QueryInterface(IID_IPortableDeviceValues, (VOID**)ppWriteResults);
            CHECK_HR(hr, "Failed to QI for IPortableDeviceValues on IPortableDeviceValues");
        }

        // Remember to set the hr to S_FALSE if there were any failures writing a property.
        if((hr            == S_OK) &&
           (bErrorOnWrite == true))
        {
            hr = S_FALSE;
        }

        return hr;
    }

    HRESULT GetAttributes(
        _In_            LPCWSTR                 pszObjectID,
        _In_            REFPROPERTYKEY          key,
        _COM_Outptr_    IPortableDeviceValues** ppAttributes)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        if(ppAttributes == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL attributes parameter");
            return hr;
        }

        *ppAttributes = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->GetAttributes(key, ppAttributes);
            CHECK_HR(hr, "Failed to get attributes for %ws.%d", CComBSTR(key.fmtid), key.pid);
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID);
        }
        return hr;
    }

    HRESULT GetSupportedResources(
        _In_            LPCWSTR                         pszObjectID,
        _COM_Outptr_    IPortableDeviceKeyCollection**  ppCollection)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        *ppCollection = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->GetSupportedResources(ppCollection);
            CHECK_HR(hr, "Failed to get supported resources on element [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        return hr;
    }

    HRESULT GetResourceAttributes(
        _In_            LPCWSTR                 pszObjectID,
        _In_            REFPROPERTYKEY          key,
        _COM_Outptr_    IPortableDeviceValues** ppAttributes)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        if(ppAttributes == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL attributes parameter");
            return hr;
        }

        *ppAttributes = NULL;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->GetResourceAttributes(key, ppAttributes);
            CHECK_HR(hr, "Failed to get resource attributes for %ws.%d", CComBSTR(key.fmtid), key.pid);
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID);
        }
        return hr;
    }

    HRESULT ReadData(
        _In_    LPCWSTR         pszObjectID,
        _In_    REFPROPERTYKEY  ResourceKey,
                DWORD           dwStartByte,
        _Out_writes_to_(dwNumBytesToRead, *pdwNumBytesRead) BYTE*   pBuffer,
                DWORD           dwNumBytesToRead,
        _Out_   DWORD*          pdwNumBytesRead)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        if((pBuffer         == NULL) ||
           (pdwNumBytesRead == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL parameter");
            return hr;
        }

        *pdwNumBytesRead = 0;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->ReadData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToRead, pdwNumBytesRead);
            CHECK_HR(hr, "Failed to read resource data for %ws.%d", CComBSTR(ResourceKey.fmtid), ResourceKey.pid);
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID);
        }
        return hr;
    }

    HRESULT WriteData(
        _In_    LPCWSTR         pszObjectID,
        _In_    REFPROPERTYKEY  ResourceKey,
                DWORD           dwStartByte,
        _In_reads_(dwNumBytesToWrite)    BYTE* pBuffer,
                DWORD           dwNumBytesToWrite,
        _Out_   DWORD*          pdwNumBytesWritten)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pElement    = NULL;

        if((pBuffer             == NULL) ||
           (pdwNumBytesWritten  == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL parameter");
            return hr;
        }

        *pdwNumBytesWritten = 0;

        if(GetContent(pszObjectID, &pElement))
        {
            hr = pElement->WriteData(ResourceKey, dwStartByte, pBuffer, dwNumBytesToWrite, pdwNumBytesWritten);
            CHECK_HR(hr, "Failed to write resource data for %ws.%d", CComBSTR(ResourceKey.fmtid), ResourceKey.pid);
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID);
        }
        return hr;
    }

    HRESULT MarkChildrenForDeletion(
        _In_    LPCWSTR pszParentObjectID)
    {
        HRESULT hr      = S_OK;

        CAtlStringW strParentObjectID = pszParentObjectID;
        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if (m_Content[Index]->ParentID == strParentObjectID)
            {
                // Mark this object
                m_Content[Index]->MarkedForDeletion = TRUE;
                hr = MarkChildrenForDeletion(m_Content[Index]->ObjectID);
                if (FAILED(hr))
                {
                    break;
                }
            }
        }
        return hr;
    }

    HRESULT RemoveObjectsMarkedForDeletion()
    {
        HRESULT hr      = S_OK;

        for (size_t Index = m_Content.GetCount(); Index > 0; Index--)
        {
            if (m_Content[Index - 1]->MarkedForDeletion == TRUE)
            {
                // Delete this object
                FakeContent* pContent = m_Content[Index - 1];
                m_Content.RemoveAt(Index - 1);
                delete pContent;
            }
        }

        // Removes empty elements
        m_Content.FreeExtra();

        return hr;
    }

    BOOL CanDeleteObject(
        _In_    LPCWSTR pszObjectID)
    {
        BOOL bCanDelete = FALSE;

        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if (m_Content[Index]->ObjectID == pszObjectID)
            {
                bCanDelete = m_Content[Index]->CanDelete;
                break;
            }
        }
        return bCanDelete;
    }

    HRESULT DeleteObject(
                DWORD   dwOptions,
        _In_    LPCWSTR pszObjectID)
    {
        HRESULT hr      = S_OK;
        DWORD   dwIndex = 0;

        if(pszObjectID == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL parameter");
            return hr;
        }

        // Do an access check to verify whether the object may be deleted
        if(CanDeleteObject(pszObjectID) == FALSE)
        {
            hr = E_ACCESSDENIED;
            CHECK_HR(hr, "Failed to delete read-only object %ws", pszObjectID);
        }

        // Objects can only be deleted with the no recursion flag
        // if they have no children.
        if ((dwOptions == PORTABLE_DEVICE_DELETE_NO_RECURSION) &&
            (HasChildren(pszObjectID) == TRUE))
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION);
            return hr;
        }

        // Loop through the objects and delete the children only if
        // the recursive option is specified.
        if (dwOptions == PORTABLE_DEVICE_DELETE_WITH_RECURSION)
        {
            hr = MarkChildrenForDeletion(pszObjectID);
            CHECK_HR(hr, "Error attempting to mark objects for (recursive) deletion");
        }

        // Mark this object for deletion
        if (hr == S_OK)
        {
            if(GetContentIndex(pszObjectID, &dwIndex))
            {
                m_Content[dwIndex]->MarkedForDeletion = TRUE;
            }
            else
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID);
            }
        }

        // Delete the objects
        if (hr == S_OK)
        {
            hr = RemoveObjectsMarkedForDeletion();
            CHECK_HR(hr, "Failed to remove objects marked for deletion");
        }

        return hr;
    }

    BOOL HasChildren(
        _In_    LPCWSTR pszObjectID)
    {
        BOOL bHasChild = FALSE;

        if (pszObjectID == NULL)
        {
            bHasChild = FALSE;
            return bHasChild;
        }

        CAtlStringW strObjectID = pszObjectID;
        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if (m_Content[Index]->ParentID == strObjectID)
            {
                bHasChild = TRUE;
                break;
            }
        }

        return bHasChild;
    }

    HRESULT SaveNewObject(
        _In_                            IPortableDeviceValues*  pObjectProperties,
        _Outptr_result_nullonfailure_   LPWSTR*                 ppszObjectID)
    {
        HRESULT     hr              = S_OK;
        DWORD       dwParentIndex   = 0;
        LPWSTR      pszObjectName   = NULL;
        LPWSTR      pszParentID     = NULL;
        GUID        guidContentType = WPD_CONTENT_TYPE_UNSPECIFIED;

        if(pObjectProperties == NULL || ppszObjectID == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return hr;
        }

        *ppszObjectID = NULL;

        // Get the content type
        hr = pObjectProperties->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, &guidContentType);
        CHECK_HR(hr, "Failed to get WPD_OBJECT_CONTENT_TYPE");

        // Get ParentID from pValues
        if (SUCCEEDED(hr))
        {
            hr = pObjectProperties->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID);
            CHECK_HR(hr, "Failed to get WPD_OBJECT_PARENT_ID");
        }

        // Get Object Name from pValues
        if (SUCCEEDED(hr))
        {
            hr = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &pszObjectName);
            CHECK_HR(hr, "Failed to get WPD_OBJECT_NAME");
        }

        if (SUCCEEDED(hr) && !GetContentIndex(pszParentID, &dwParentIndex))
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid Parent ObjectID [%ws]", pszParentID);
        }

        // Check whether the parent can hold objects of this content type
        if (SUCCEEDED(hr))
        {
            hr = IsValidContentType(guidContentType, m_Content[dwParentIndex]->RestrictToContentTypes);
            CHECK_HR(hr, "Object named [%ws] could not be created,  because parent [%ws] does not support this content type", pszObjectName, pszParentID);
        }

        if (SUCCEEDED(hr))
        {
            FakeContent* pContent;

            // Create the object
            hr = CreateContentObject(pszObjectName, pszParentID, guidContentType, pObjectProperties, &pContent);
            CHECK_HR(hr, "Failed to create object named %ws", pszObjectName);

            // Add it to the sample driver's internal list of content objects
            if (SUCCEEDED(hr))
            {
                m_Content.Add(pContent);
                *ppszObjectID = AtlAllocTaskWideString(pContent->ObjectID);
                if(*ppszObjectID == NULL)
                {
                    hr = E_OUTOFMEMORY;
                    CHECK_HR(hr, "Faield to allocate memory for newly created object's ID");
                }
            }
        }

        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
        CoTaskMemFree(pszParentID);
        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
        CoTaskMemFree(pszObjectName);

        return hr;
    }

    const CAtlStringW GetParentID(
        _In_    LPCWSTR pszObjectID)
    {
        CAtlStringW strParent = L"";

        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if(m_Content[Index]->ObjectID.CompareNoCase(pszObjectID) == 0)
            {
                strParent = m_Content[Index]->ParentID;
                break;
            }
        }

        return strParent;
    }

    BOOL GetDepthFromParent(
        _In_    LPCWSTR pszObjectID,
        _In_    LPCWSTR pszParentObjectID,
        _Out_   DWORD*  pdwDepth)
    {
        HRESULT hr          = S_OK;
        BOOL    bDescendant = FALSE;

        if((pszObjectID         == NULL) ||
           (pszParentObjectID   == NULL) ||
           (pdwDepth            == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return bDescendant;
        }

        *pdwDepth = 0;

        DWORD       dwDepth         = 0;
        CAtlStringW strCurObject    = pszObjectID;

        while((strCurObject.CompareNoCase(pszParentObjectID) != 0) &&
              (strCurObject.GetLength() > 0))
        {
            strCurObject = GetParentID(strCurObject);
            dwDepth++;
        }

        if(strCurObject.CompareNoCase(pszParentObjectID) == 0)
        {
            bDescendant = TRUE;
            *pdwDepth   = dwDepth;
        }

        return bDescendant;
    }

#pragma warning(suppress: 6388) // PREFast bug means here there is a false positive for the call to CComPtr<>::QueryInterface().
    HRESULT GetObjectIDsByFormat(
        _In_            REFGUID                                 guidObjectFormat,
        _In_            LPCWSTR                                 pszParentObjectID,
                        DWORD                                   dwDepth,
        _COM_Outptr_    IPortableDevicePropVariantCollection**  ppObjectIDs)
    {
        HRESULT hr = S_OK;
        CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;

        if(ppObjectIDs == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return hr;
        }

        *ppObjectIDs = NULL;

        // Create the ObjectID collection.  Since this sample driver does not
        // have any real hardware, it cannot take advantage of the devices
        // 'bulk' operations, and so we must simulate it here.
        // For simplicity, this sample driver simply walks the list of
        // objects, finds out whether they are of the specified format and depth, and
        // if the are then adds them to an object list.  This is
        // easy to do, but not the most efficient way to do it.
        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDevicePropVariantCollection,
                                  (VOID**) &pObjectIDs);
            CHECK_HR(hr, "Failed to CoCreate CLSID_IPortableDevicePropVariantCollection");
        }

        if (SUCCEEDED(hr))
        {
            // Walk the list of objects
            PROPVARIANT pv = {0};
            PropVariantInit(&pv);
            pv.vt = VT_LPWSTR;
            for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
            {
                // Check whether the object is of the correct format
                if((m_Content[Index]->GetObjectFormat() == guidObjectFormat) || (guidObjectFormat == WPD_OBJECT_FORMAT_ALL))
                {
                    DWORD dwObjectDepth = 0;
                    // find out the depth of this object from the parent
                    if(GetDepthFromParent(m_Content[Index]->ObjectID, pszParentObjectID, &dwObjectDepth))
                    {
                        // If it is within the appropriate depth, add it
                        if(dwObjectDepth <= dwDepth)
                        {
                            pv.pwszVal = (LPWSTR) m_Content[Index]->ObjectID.GetString();
                            hr = pObjectIDs->Add(&pv);
                            CHECK_HR(hr, "Failed to add next ObjectID to collection");
                            if(FAILED(hr))
                            {
                                break;
                            }
                        }
                    }
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            hr = pObjectIDs->QueryInterface(IID_PPV_ARGS(ppObjectIDs));
            CHECK_HR(hr, "Failed to QI IPortableDevicePropVariantCollection for IPortableDevicePropVariantCollection");
        }

        return hr;
    }

    HRESULT GetObjectIDFromPersistentID(
        _In_                          LPCWSTR     pszPersistentID,
        _Outptr_result_nullonfailure_ LPWSTR*     ppszObjectID)
    {
        HRESULT hr = S_OK;

        if((pszPersistentID == NULL) ||
           (ppszObjectID    == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return hr;
        }

        *ppszObjectID = NULL;

        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if(m_Content[Index]->PersistentUniqueID.CompareNoCase(pszPersistentID) == 0)
            {
                *ppszObjectID = AtlAllocTaskWideString(m_Content[Index]->ObjectID);
                if(*ppszObjectID == NULL)
                {
                    hr = E_OUTOFMEMORY;
                    CHECK_HR(hr, "Could not allocate memory for ObjectID");
                }
                break;
            }
        }

        if ((hr == S_OK) && (*ppszObjectID == NULL))
        {
            // We reached the end of the content but did not find the element
            hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
        }

        return hr;
    }

    /**
     * Since this is a fake device with no hardware or real contents, we simulate
     * a format by deleting the contents of a storage object.
     **/
    HRESULT FormatStorage(
        _In_    LPCWSTR                 pszObjectID,
        _In_    IPortableDeviceValues*  pCommandParams)
    {
        HRESULT     hr          = S_OK;
        CAtlStringW strObjectID = pszObjectID;

        // Validate that the object specified is one of our storage objects
        if((strObjectID.CompareNoCase(STORAGE1_OBJECT_ID) == 0) ||
           (strObjectID.CompareNoCase(STORAGE2_OBJECT_ID) == 0))
        {
            CComPtr<IPortableDeviceValues> pEventParams;

            hr = GetObjectPropertiesForEvent(strObjectID.GetString(), &pEventParams);
            CHECK_HR(hr, "Failed to get storage object properties for event");

            // Indicate that format has started
            hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_STARTED, 0);
            CHECK_HR(hr, "Failed to send format event progress: WPD_OPERATION_STATE_STARTED");

            if (hr == S_OK)
            {
                hr = MarkChildrenForDeletion(pszObjectID);
                CHECK_HR(hr, "Failed to mark children of storage for deletion");

                // Indicate that format is halfway there
                hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_RUNNING, 50);
                CHECK_HR(hr, "Failed to send format progress notification");

                // Delete the objects
                if (hr == S_OK)
                {
                    hr = RemoveObjectsMarkedForDeletion();
                    CHECK_HR(hr, "Failed to remove objects marked for deletion");
                }

            }

            // Indicate that format is done
            hr = PostWpdEventWithProgress(pCommandParams, pEventParams, WPD_EVENT_STORAGE_FORMAT, WPD_OPERATION_STATE_FINISHED, 100);
            CHECK_HR(hr, "Failed to send format event progress: WPD_OPERATION_STATE_FINISHED");
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Cannot format object [%ws] because it is not a storage", pszObjectID);
        }

        return hr;
    }

    HRESULT MoveObject(
        _In_    LPCWSTR     pszObjectID,
        _In_    LPCWSTR     pszDestinationID)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pSource     = NULL;
        FakeContent*    pDestFolder = NULL;

        if(GetContent(pszObjectID, &pSource))
        {
            // Verify that this is a content object.  Functional objects cannot be moved.
            if(pSource->ContentType != WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT)
            {
                // Verify that the source is not the same as the destination
                if(pSource->ObjectID.CompareNoCase(pszDestinationID) != 0)
                {
                    // Verify that the destination is a folder
                    if(GetContent(pszDestinationID, &pDestFolder))
                    {
                        if(pDestFolder->ContentType == WPD_CONTENT_TYPE_FOLDER)
                        {
                            pSource->ParentID = pszDestinationID;
                        }
                        else
                        {
                            hr = E_INVALIDARG;
                            CHECK_HR(hr, "Destination [%ws] for move object is not a folder", pszDestinationID);
                        }
                    }
                    else
                    {
                        hr = E_INVALIDARG;
                        CHECK_HR(hr, "Invalid ObjectID [%ws]", pszDestinationID ? pszDestinationID : L"NULL");
                    }
                }
                else
                {
                    hr = E_INVALIDARG;
                    CHECK_HR(hr, "Object [%ws] cannot be moved to itself", pszObjectID);
                }
            }
            else
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "Attempting to move functional object %ws", pszObjectID);
            }
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        return hr;
    }

    HRESULT CopyObject(
        _In_                          LPCWSTR     pszObjectID,
        _In_                          LPCWSTR     pszDestinationID,
        _Outptr_result_nullonfailure_ LPWSTR*     ppNewObjectID)
    {
        HRESULT         hr          = S_OK;
        FakeContent*    pSource     = NULL;
        FakeContent*    pDestFolder = NULL;
        CComPtr<IPortableDeviceValues> pNewContentProperties;
        LPWSTR          pwszNewObjectID = NULL;

        if(ppNewObjectID == NULL)
        {
            hr = E_POINTER;
            CHECK_HR(hr, ("Cannot have NULL parameter"));
            return hr;
        }

        *ppNewObjectID = NULL;

        if(GetContent(pszObjectID, &pSource))
        {
            // Verify that this is a content object.  Functional objects cannot be copied.
            if(pSource->ContentType != WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT)
            {
                // Verify that the source is not the same as the destination
                if(pSource->ObjectID.CompareNoCase(pszDestinationID) != 0)
                {
                    // Verify that the destination is a folder
                    if(GetContent(pszDestinationID, &pDestFolder))
                    {
                        if(pDestFolder->ContentType == WPD_CONTENT_TYPE_FOLDER)
                        {
                            hr = GetAllValues(pSource->ObjectID, &pNewContentProperties);
                            CHECK_HR(hr, "Failed to get ALL values for [%ws]", pSource->ObjectID);

                            if (SUCCEEDED(hr))
                            {
                                hr = pNewContentProperties->SetStringValue(WPD_OBJECT_PARENT_ID, pDestFolder->ObjectID);
                                CHECK_HR(hr, "Failed to update WPD_OBJECT_PARENT_ID value to content object[%ws] on new copied content", pDestFolder->ObjectID);
                            }

                            if (SUCCEEDED(hr))
                            {
                                hr = SaveNewObject(pNewContentProperties, &pwszNewObjectID);
                                CHECK_HR(hr, "Failed Create new content from object[%ws]", pSource->ObjectID);
                            }

                            // If the object we just copied was a folder and it contained child objects we need to copy the contents as well.
                            if (SUCCEEDED(hr) && (pSource->ContentType == WPD_CONTENT_TYPE_FOLDER) && (HasChildren(pSource->ObjectID) == TRUE))
                            {
                                hr = CopyChildrenToNewParent(pSource->ObjectID, pwszNewObjectID);
                            }
                        }
                        else
                        {
                            hr = E_INVALIDARG;
                            CHECK_HR(hr, "Destination [%ws] for move object is not a folder", pszDestinationID);
                        }
                    }
                    else
                    {
                        hr = E_INVALIDARG;
                        CHECK_HR(hr, "Invalid ObjectID [%ws]", pszDestinationID ? pszDestinationID : L"NULL");
                    }
                }
                else
                {
                    hr = E_INVALIDARG;
                    CHECK_HR(hr, "Object [%ws] cannot be copied to itself", pszObjectID);
                }
            }
            else
            {
                hr = E_INVALIDARG;
                CHECK_HR(hr, "Attempting to move functional object %ws", pszObjectID);
            }
        }
        else
        {
            hr = E_INVALIDARG;
            CHECK_HR(hr, "Invalid ObjectID [%ws]", pszObjectID ? pszObjectID : L"NULL");
        }

        // If we failed, free the allocated object ID.
        if (FAILED(hr))
        {
            if (pwszNewObjectID != NULL)
            {
                CoTaskMemFree(pwszNewObjectID);
                pwszNewObjectID = NULL;
            }
        }

        if (pwszNewObjectID != NULL)
        {
            // The newly created object ID is given to the caller to manage and free
            *ppNewObjectID = pwszNewObjectID;
        }

        return hr;
    }

    HRESULT GetObjectPropertiesForEvent(
        _In_            LPCWSTR                 pszObjectID,
        _COM_Outptr_    IPortableDeviceValues** ppValues)
    {
        HRESULT hr = S_OK;
        CComPtr<IPortableDeviceKeyCollection> pKeys;
        CComPtr<IPortableDeviceValues> pValues;

        *ppValues = NULL;

        if (SUCCEEDED(hr))
        {
            hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                                  NULL,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IPortableDeviceKeyCollection,
                                  (VOID**) &pKeys);
            CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceKeyCollection");
        }

        // Add the keys we need in order to fill out event parameters
        if (SUCCEEDED(hr))
        {
            hr = pKeys->Add(WPD_OBJECT_PERSISTENT_UNIQUE_ID);
            CHECK_HR(hr, "Failed to add WPD_OBJECT_PERSISTENT_UNIQUE_ID");

            if (SUCCEEDED(hr))
            {
                hr = pKeys->Add(WPD_OBJECT_NAME);
                CHECK_HR(hr, "Failed to add WPD_OBJECT_NAME");
            }
            if (SUCCEEDED(hr))
            {
                hr = pKeys->Add(WPD_OBJECT_CONTENT_TYPE);
                CHECK_HR(hr, "Failed to add WPD_OBJECT_CONTENT_TYPE");
            }

            //These properties may or may not exist on the object
            if (SUCCEEDED(hr))
            {
                hr = pKeys->Add(WPD_FUNCTIONAL_OBJECT_CATEGORY);
                CHECK_HR(hr, "Failed to add WPD_FUNCTIONAL_OBJECT_CATEGORY");
            }
            if (SUCCEEDED(hr))
            {
                hr = pKeys->Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
                CHECK_HR(hr, "Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME");
            }
            if (SUCCEEDED(hr))
            {
                hr = pKeys->Add(WPD_OBJECT_PARENT_ID);
                CHECK_HR(hr, "Failed to add WPD_OBJECT_PARENT_ID");
            }
        }

        // Get the values
        if (SUCCEEDED(hr))
        {
            hr = GetValues(pszObjectID, pKeys, &pValues);
            if (hr == S_FALSE)
            {
                hr = S_OK;
            }

            // Some objects supported by this driver don't support WPD_OBJECT_ORIGINAL_FILE_NAME
            // (e.g. functional objects) so remove it if there was an error reading it.
            if (SUCCEEDED(hr))
            {
                PROPVARIANT pv = {0};
                PropVariantInit(&pv);

                hr = pValues->GetValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pv);
                CHECK_HR(hr, "Failed to get WPD_OBJECT_ORIGINAL_FILE_NAME");

                if(SUCCEEDED(hr))
                {
                    if(pv.vt == VT_ERROR)
                    {
                        hr = pValues->RemoveValue(WPD_OBJECT_ORIGINAL_FILE_NAME);
                        CHECK_HR(hr, "Failed to remove WPD_OBJECT_ORIGINAL_FILE_NAME");
                    }
                }

                PropVariantClear(&pv);

                // None of the above is fatal
                hr = S_OK;
            }

            // Add the WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID
            if (SUCCEEDED(hr))
            {
                CAtlStringW strDevice = WPD_DEVICE_OBJECT_ID;
                // If this is the device object, set the parent's persistent unique ID to to the empty string since it has no parent.
                // Esle, get the persistent unique ID from the parent.
                if(strDevice.CompareNoCase(pszObjectID) == 0)
                {
                    pValues->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, L"");
                }
                else
                {
                    CComPtr<IPortableDeviceValues> pParentValues;
                    HRESULT hrTemp = S_OK;
                    LPWSTR  pszParentID = NULL;
                    LPWSTR  pszParentUID = NULL;

                    hrTemp = pValues->GetStringValue(WPD_OBJECT_PARENT_ID, &pszParentID);
                    if (SUCCEEDED(hrTemp))
                    {
                        hrTemp = GetValues(pszParentID, pKeys, &pParentValues);
                        if (SUCCEEDED(hrTemp))
                        {
                            hrTemp = pParentValues->GetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, &pszParentUID);
                            if (SUCCEEDED(hrTemp))
                            {
                                pValues->SetStringValue(WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID, pszParentUID);
                                CoTaskMemFree(pszParentUID);
                            }
                            CHECK_HR(hrTemp, "Failed to get parent unique id");
                        }
                        CoTaskMemFree(pszParentID);
                        CHECK_HR(hrTemp, "Failed to get values for parent id");
                    }
                    CHECK_HR(hrTemp, "Failed to get parent id");
                }
            }
            CHECK_HR(hr, "Failed to get properties to use in event information");
        }

        if (SUCCEEDED(hr))
        {
            *ppValues = pValues.Detach();
        }

        return hr;
    }


    HRESULT CreateContentObject(
        _In_    LPCWSTR                 pszObjectName,
        _In_    LPCWSTR                 pszParentID,
        _In_    REFGUID                 guidContentType,
        _In_    IPortableDeviceValues*  pObjectProperties,
        _Outptr_result_nullonfailure_   FakeContent**   ppContent)
    {
        HRESULT hr      = S_OK;
        HRESULT hrTemp  = S_OK;
        LPWSTR  wszOriginalFileName = NULL;

        *ppContent = NULL;

        if(guidContentType == WPD_CONTENT_TYPE_FOLDER)
        {
            FakeContent* pContent = NULL;

            pContent = new FakeContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->FileName      = pszObjectName;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                BOOL bTemp = FALSE;
                // Get next optional property WPD_OBJECT_CAN_DELETE
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_CAN_DELETE, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->CanDelete = bTemp;
                }
                // Get next optional property WPD_OBJECT_ISHIDDEN
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_ISHIDDEN, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->IsHidden = bTemp;
                }
                // Get next optional property WPD_OBJECT_NON_CONSUMABLE
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_NON_CONSUMABLE, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->NonConsumable = bTemp;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_CONTACT)
        {
            FakeContactContent* pContent = NULL;

            pContent = new FakeContactContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                // Get the other contact properties.
                LPWSTR  pszTempString   = NULL;

                hr = pObjectProperties->GetStringValue(WPD_CONTACT_DISPLAY_NAME, &pszTempString);
                CHECK_HR(hr, "Failed to get required contact property: WPD_CONTACT_DISPLAY_NAME");
                if (SUCCEEDED(hr))
                {
                    pContent->DisplayName = pszTempString;
                    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
                    CoTaskMemFree(pszTempString);

                    // Get next optional property WPD_CONTACT_PRIMARY_PHONE
                    hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_PRIMARY_PHONE, &pszTempString);
                    if(hrTemp == S_OK)
                    {
                        pContent->PrimaryPhone = pszTempString;
                        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
                        CoTaskMemFree(pszTempString);
                    }

                    // Get next optional property WPD_CONTACT_MOBILE_PHONE
                    hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_MOBILE_PHONE, &pszTempString);
                    if(hrTemp == S_OK)
                    {
                        pContent->CellPhone = pszTempString;
                        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
                        CoTaskMemFree(pszTempString);
                    }

                    // Get next optional property WPD_CONTACT_BUSINESS_PHONE
                    hrTemp = pObjectProperties->GetStringValue(WPD_CONTACT_BUSINESS_PHONE, &pszTempString);
                    if(hrTemp == S_OK)
                    {
                        pContent->WorkPhone = pszTempString;
                        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
                        CoTaskMemFree(pszTempString);
                    }

                    // Get next optional property WPD_OBJECT_ORIGINAL_FILE_NAME
                    hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &pszTempString);
                    if(hrTemp == S_OK)
                    {
                        pContent->FileName = pszTempString;
                        // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
                        CoTaskMemFree(pszTempString);
                    }

                    *ppContent = pContent;
                }
                else
                {
                    delete pContent;
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeContactContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_AUDIO)
        {
            FakeMusicContent* pContent = NULL;

            pContent = new FakeMusicContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
                if (hrTemp == S_OK)
                {
                    pContent->FileName = wszOriginalFileName;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeMusicContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_VIDEO)
        {
            FakeVideoContent* pContent = NULL;

            pContent = new FakeVideoContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
                if (hrTemp == S_OK)
                {
                    pContent->FileName = wszOriginalFileName;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeVideoContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_IMAGE)
        {
            FakeImageContent* pContent = NULL;

            pContent = new FakeImageContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
                if (hrTemp == S_OK)
                {
                    pContent->FileName = wszOriginalFileName;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeImageContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_NETWORK_ASSOCIATION)
        {
            FakeNetworkAssociationContent* pContent = NULL;

            pContent = new FakeNetworkAssociationContent();
            if (pContent)
            {
                BYTE *pValue = NULL;
                DWORD cbValue = 0;

                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                // Get required property WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS
                hr = pObjectProperties->GetBufferValue(WPD_NETWORK_ASSOCIATION_HOST_NETWORK_IDENTIFIERS, &pValue, &cbValue);
                if (SUCCEEDED(hr))
                {
                    // Make sure the HostEUI64Array is the correct length
                    if ((cbValue % 8) == 0)
                    {
                        // Avoid copy by manually updating the PROPVARIANT
                        pContent->HostEUI64Array.vt = VT_VECTOR | VT_UI1;
                        pContent->HostEUI64Array.caub.cElems  = cbValue;
                        pContent->HostEUI64Array.caub.pElems  = pValue;
                    }
                    else
                    {
                        CoTaskMemFree(pValue);
                        pValue = NULL;

                        hr = E_INVALIDARG;
                        CHECK_HR(hr, "Invalid value for HostEUI64Array");
                    }
                }

                if (SUCCEEDED(hr))
                {
                    *ppContent = pContent;
                }
                else
                {
                    delete pContent;
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeNetworkAssociationContent object");
            }
        }
        else if(guidContentType == WPD_CONTENT_TYPE_WIRELESS_PROFILE)
        {
            FakeWirelessProfileContent* pContent = NULL;

            pContent = new FakeWirelessProfileContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
                if (hrTemp == S_OK)
                {
                    pContent->FileName = wszOriginalFileName;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeWirelessProfileContent object");
            }
        }
        else
        {
            FakeContent* pContent;

            // Add generic file object
            pContent = new FakeGenericFileContent();
            if (pContent)
            {
                pContent->Name          = pszObjectName;
                pContent->ParentID      = pszParentID;
                m_dwLastObjectID++;
                pContent->ObjectID.Format(L"%d", m_dwLastObjectID);
                pContent->ContentType   = guidContentType;
                pContent->PersistentUniqueID.Format(L"PersistentUniqueID_%ws", pContent->ObjectID.GetString());

                BOOL bTemp = FALSE;
                // Get next optional property WPD_OBJECT_CAN_DELETE
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_CAN_DELETE, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->CanDelete = bTemp;
                }
                // Get next optional property WPD_OBJECT_ISHIDDEN
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_ISHIDDEN, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->IsHidden = bTemp;
                }
                // Get next optional property WPD_OBJECT_NON_CONSUMABLE
                hrTemp = pObjectProperties->GetBoolValue(WPD_OBJECT_NON_CONSUMABLE, &bTemp);
                if(hrTemp == S_OK)
                {
                    pContent->NonConsumable = bTemp;
                }

                hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
                if (hrTemp == S_OK)
                {
                    pContent->FileName = wszOriginalFileName;
                }

                *ppContent = pContent;
            }
            else
            {
                hr = E_OUTOFMEMORY;
                CHECK_HR(hr, "Failed to allocate new FakeContent object");
            }
        }

        if (wszOriginalFileName != NULL)
        {
            CoTaskMemFree(wszOriginalFileName);
            wszOriginalFileName = NULL;
        }

        return hr;
    }

    HRESULT GetContentFormat(
        _In_  LPCWSTR pszObjectID,
        _Out_ GUID&   guidObjectFormat)
    {
        HRESULT hr = S_OK;
        FakeContent* pContent = NULL;
        guidObjectFormat = GUID_NULL;

        if (GetContent(pszObjectID, &pContent) == true)
        {
            guidObjectFormat = pContent->GetObjectFormat();
        }
        else
        {
            hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
        }

        return hr;
    }

    HRESULT EnableResource(
        _In_    LPCWSTR pszObjectID,
        _In_    REFPROPERTYKEY ResourceKey)
    {
        HRESULT hr = S_OK;
        FakeContent* pContent = NULL;

        if (GetContent(pszObjectID, &pContent) == true)
        {
            hr = pContent->EnableResource(ResourceKey);
        }
        else
        {
            hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
        }

        return hr;
    }

    HRESULT CopyChildrenToNewParent(
        _In_    LPCWSTR pszParentObjectID,
        _In_    LPCWSTR pszNewParentObjectID)
    {
        HRESULT hr = S_OK;

        CAtlStringW strParentObjectID = pszParentObjectID;
        for (size_t Index = 0; Index < m_Content.GetCount(); Index++)
        {
            if (m_Content[Index]->ParentID == strParentObjectID)
            {
                LPWSTR pwszNewObjectID = NULL;
                // Copy this object
                hr = CopyObject(m_Content[Index]->ObjectID, pszNewParentObjectID, &pwszNewObjectID);

                // Free allocate new object ID
                if (pwszNewObjectID != NULL)
                {
                    CoTaskMemFree(pwszNewObjectID);
                    pwszNewObjectID = NULL;
                }

                if (FAILED(hr))
                {
                    break;
                }
            }
        }
        return hr;
    }

    HRESULT SupportsResource(
        _In_    LPCWSTR         pszObjectID,
        _In_    REFPROPERTYKEY  ResourceKey,
        _Out_   BOOL*           pbSupportsResource)
    {
        HRESULT hr = S_OK;
        CComPtr<IPortableDeviceKeyCollection> pKeys;

        if((pszObjectID == NULL) || (pbSupportsResource == NULL))
        {
            hr = E_POINTER;
            CHECK_HR(hr, "Cannot have NULL parameter");
            return hr;
        }

        *pbSupportsResource = FALSE;
        hr = GetSupportedResources(pszObjectID, &pKeys);
        CHECK_HR(hr, "Failed to get supported resources for validation");

        if (hr == S_OK)
        {
            DWORD        dwIndex = 0;
            PROPERTYKEY  TempKey = WPD_PROPERTY_NULL;
            while(pKeys->GetAt(dwIndex++, &TempKey) == S_OK)
            {
                if(IsEqualPropertyKey(ResourceKey, TempKey))
                {
                    *pbSupportsResource = TRUE;
                }
            }
        }

        return hr;
    }

    HRESULT UpdateContentObject(
        _In_    LPCWSTR                 pszObjectID,
        _In_    IPortableDeviceValues*  pObjectProperties)
    {
        HRESULT         hr       = S_OK;
        FakeContent*    pElement = NULL;

        if (GetContent(pszObjectID, &pElement))
        {
            HRESULT hrTemp              = S_OK;
            LPWSTR  wszOriginalFileName = NULL;
            LPWSTR  wszObjectName       = NULL;

            // Update selected properties; other properties will be discarded.
            hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_ORIGINAL_FILE_NAME, &wszOriginalFileName);
            if(hrTemp == S_OK)
            {
                pElement->FileName = wszOriginalFileName;
                CoTaskMemFree(wszOriginalFileName);
            }

            hrTemp = pObjectProperties->GetStringValue(WPD_OBJECT_NAME, &wszObjectName);
            if(hrTemp == S_OK)
            {
                pElement->Name = wszObjectName;
                CoTaskMemFree(wszObjectName);
            }

            // Note: This fake driver does nothing with the data.  The WriteData method is simply
            // a dummy one, and the data size is a constant.
            // Normally, a driver would also update the WPD_OBJECT_SIZE to the new data size.
        }
        else
        {
            hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
            CHECK_HR(hr, "Failed to find the object '%ws'", pszObjectID);
        }

        return hr;
    }

private:
    CAtlArray<FakeContent*> m_Content;
    DWORD                   m_dwLastObjectID;
};


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS