Step 4: Copy the Sample Implementation Source Code to Files on Your Computer (Compact 7)

3/12/2014

In Windows Explorer on your computer, browse to the subproject directory (which, for the purposes of this example, is C:\WINCE700\OSDesigns\OSDesignMediaPlugin\MediaParserPlugin), and then create the file SampleMediaParser.h with the following code.

Important

The following code example may not contain sufficient error handling for your device. Do not include the following code in a shipping device without extensive scenario testing.

//// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//==========================================================================;
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//
//--------------------------------------------------------------------------;
//
//                          Sample Media Parser Plug-In 
//
// SampleMediaParser.h - Declaration of ATL CoCreate'able class CSampleMediaParser.
//
//------------------------------------------------------------------------------

DEFINE_GUID(CLSID_SampleMediaParser, 0x65C0781B, 0xDA05, 0x465B, 0xB5, 0xC4, 0xE9, 0x0B, 0x43, 0xAF, 0xDD, 0x35);

/////////////////////////////////////////////////////////////////////////////
// CSampleMediaParser
class ATL_NO_VTABLE CSampleMediaParser :
    public CComObjectRoot,
    public CComCoClass<CSampleMediaParser, &CLSID_SampleMediaParser>,
    public IMLMediaParser
{
public:
    CSampleMediaParser(void);
    virtual ~CSampleMediaParser(void);

DECLARE_PROTECT_FINAL_CONSTRUCT()

DECLARE_NOT_AGGREGATABLE(CSampleMediaParser)

DECLARE_NO_REGISTRY()

BEGIN_COM_MAP(CSampleMediaParser)
    COM_INTERFACE_ENTRY(IMLMediaParser)
END_COM_MAP()

    // IMLMediaParser interface
    STDMETHOD(Init)();
    STDMETHOD(GetKnownFileExtensions)(ULONG *pcFileExtensions, BSTR **prgFileExtensions);
    STDMETHOD(GetEntityType)(MLDSItem file, ULONG *pEntityType);
    STDMETHOD(ExtractMetadata)(MLDSItem file, IMLPropertySet *pPropertySetWanted, IMLPropertySet *pFileMetadata);

private:
    HRESULT ConvertLabelToEntityId(LPCWSTR szLabel, DWORD dwLabelCC, EntityId *pEntityId);
    HRESULT GetFileExtension(BSTR bstrURL, LPCWSTR wcsExtension, size_t cchExtension);
    HRESULT GetURLExtension(BSTR bFilename, LPWSTR wcsExtension, const USHORT uExtensionMaxChars);
    HRESULT GetKnownEntity(LPCWSTR wcsExtension, EntityId *pKnownEntity);
    HRESULT LoadEntityExtensions(void);
    HRESULT SetFolderFileNames(BSTR fileURL, PropertyId propidFileURL, PropertyId propidFileName, PropertyId propidFolderName, IMLPropertySet *pPropertySetWanted, IMLPropertySet *pFileMetadata);
    HRESULT SetFileSizeAndTime(ULONGLONG llFileSize, const FILETIME &fileTime, PropertyId mlidFileSize, PropertyId mlidFileTime, IMLPropertySet *pPropertySetWanted, IMLPropertySet *pFileMetadata);
    HRESULT ConvertFileTimeToMLFileTime(FILETIME const *pftValue, MLDateTime *pmlftDest);

    // Member data
    BSTR *m_rgEntityExtensions;
    DWORD m_dwEntityExtensionCount;
};

OBJECT_ENTRY_AUTO(CLSID_SampleMediaParser, CSampleMediaParser)

In the same directory location (C:\WINCE700\OSDesigns\OSDesignMediaPlugin\ MediaParserPlugin), create the file SampleMediaParser.cpp with the following content.

Important

The following code example may not contain sufficient error handling for your device. Do not include the following code in a shipping device without extensive scenario testing.

//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft
// premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license
// agreement, you are not authorized to use this source code.
// For the terms of the license, please see the license agreement
// signed by you and Microsoft.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//------------------------------------------------------------------------------
//
//                          Sample Media Parser Plug-In 
//
// SampleMediaParser.cpp  - Implementation of ATL CoCreate'able class CSampleMediaParser.
//                 Complies with IMLMediaParser.
//
//------------------------------------------------------------------------------

#include "stdafx.h"
#include "SampleMediaParser.h"

//============================================================================
//                                CSampleMediaParser
//============================================================================

//============================================================================
//  Initialize static constant members
//============================================================================
static LPCWSTR      wcsEntityExtensionsRegKey = L"Software\\Microsoft\\MLib\\EntityExtensions";
static HKEY         hkEntityExtensionsRootKey = HKEY_LOCAL_MACHINE;
static const USHORT cchMaxExtension = MAX_PATH;  // Support long filenames.
    
//============================================================================
//
// CSampleMediaParser constructor
//
//============================================================================
CSampleMediaParser::CSampleMediaParser()
: m_rgEntityExtensions(NULL),
  m_dwEntityExtensionCount(0)
{
}

//============================================================================
//
// CSampleMediaParser destructor
//
// Description:
//     Clean up dynamically allocated memory.
//
//============================================================================
CSampleMediaParser::~CSampleMediaParser(void)
{
    if (NULL != m_rgEntityExtensions)
    {
        for (ULONG iExtension = 0; iExtension < m_dwEntityExtensionCount; iExtension++)
        {
            if (m_rgEntityExtensions[iExtension] != NULL)
            {
                SysFreeString(m_rgEntityExtensions[iExtension]);
            }
        }
        delete [] m_rgEntityExtensions;
    }
}

//============================================================================
//
// Init
//
// Expected Result Code:
//     S_OK : success
//
// Description:
//     Media Library service calls this method upon start up.
//
//============================================================================
HRESULT
CSampleMediaParser::Init(void)
{
    return LoadEntityExtensions();
}

//============================================================================
//
// LoadEntityExtensions
//
// Possible Result Codes:
//     S_OK      : call succeeded. 
//
// Description:
//     Gathers an array of extensions from the Media Library application
//     registry key.  Only the labels successfully interpreted as belonging
//     to a Media Library entity id will be supported.
//
//============================================================================
HRESULT
CSampleMediaParser::LoadEntityExtensions(void)
{
    HRESULT hr = S_OK;
    CRegKey reg;
    LONG    lRet = 0L;
    HKEY    hKey = NULL;
    DWORD   dwIndex = 0;
    DWORD   cValues = 0;
    DWORD   dwLocalExtCount = 0;
    BSTR   *prgLocalExtensions = NULL;
    ASSERT(m_rgEntityExtensions == NULL);

    lRet = reg.Open(hkEntityExtensionsRootKey, wcsEntityExtensionsRegKey, KEY_READ);
    ChkBool((lRet == ERROR_SUCCESS), S_OK);  // TODO: Explain

    hKey = (HKEY)reg;  // Use a copy of the HANDLE for Reg functions not implemented in CRegKey.
    ChkBool(hKey != NULL, E_FAIL);
    
    // Get a count of values.
    lRet = RegQueryInfoKey(
               hKey,        // key handle 
               NULL,
               NULL,
               NULL,
               NULL,
               NULL,
               NULL,
               &cValues,    // number of values for this key 
               NULL,
               NULL,
               NULL,
               NULL);

    ChkBool((lRet == ERROR_SUCCESS), E_FAIL);
    ChkBool((cValues > 0), S_OK);   // Finding no supported extensions is OK, but don't continue.

    prgLocalExtensions = new BSTR[cValues];
    memset((void *)prgLocalExtensions, 0, sizeof(BSTR)*cValues);

    for (dwIndex = 0; dwIndex < cValues && lRet != ERROR_NO_MORE_ITEMS; dwIndex++)
    {
        DWORD dwType = 0;
        WCHAR szValueName[cchMaxExtension];
        DWORD cchValueName = 0;

        szValueName[0] = L'\0';
        cchValueName = cchMaxExtension;

        //Capacity going in must include NULL termination character,
        // but count coming out excludes NULL termination character. 
        lRet = RegEnumValue(hKey, dwIndex, szValueName, &cchValueName, NULL, &dwType, NULL, NULL);
        switch (lRet)
        {
        case ERROR_SUCCESS:
            if (dwType == REG_SZ && cchValueName <= cchMaxExtension)
            {
                DWORD dwEntity = 0;
                WCHAR szValueData[cchMaxExtension];
                ULONG nCch = _countof(szValueData);
                EntityId entityId = ml_Entity_NONE;

                if (ERROR_SUCCESS == reg.QueryStringValue(szValueName, szValueData, &nCch))
                {
                    // Ignore bad labels.  They will simply be unsupported.
                    if (S_OK == ConvertLabelToEntityId(szValueData, nCch, &entityId))
                    {
                        prgLocalExtensions[dwLocalExtCount] = SysAllocString((LPWSTR)szValueName);
                        dwLocalExtCount++;
                    }
                }
            }
            break;
        case ERROR_NO_MORE_ITEMS:
            // Done.  Let the "for" condition stop the loop.
            break;
        case ERROR_MORE_DATA:
            // The buffer receiving the value name is too small.
            // Overly long extensions are disallowed but are not a blocking problem.
            break;
        default:
            ChkBool(FALSE, HRESULT_FROM_WIN32(lRet));
            break;
        }
    }

    hKey = NULL;  // Remove temptation to close this copy of the key handle.

    // Transfer valid extensions from local to member storage.
    if (dwLocalExtCount == cValues)
    {
        // All elements are valid.  Just use the array as-is.
        m_rgEntityExtensions = prgLocalExtensions;
    }
    else
    {
        // Allocate member array for only valid elements.
        m_rgEntityExtensions = new BSTR[dwLocalExtCount];
        for (dwIndex = 0; dwIndex < dwLocalExtCount; dwIndex++)
        {
            m_rgEntityExtensions[dwIndex] = prgLocalExtensions[dwIndex];
        }

        // Free local container array.
        delete [] prgLocalExtensions;
    }

    m_dwEntityExtensionCount = dwLocalExtCount;

Cleanup:
    return hr;
}

//============================================================================
//
// GetKnownFileExtensions
//
// Parameters:
// out ULONG *pcFileExtensions   : Number of file extensions
// out BSTR **pprgFileExtensions : Supported file extensions (caller must free)
//
// Possible Result Codes:
//     S_OK          : Success
//     E_POINTER     : One or more parameters are NULL.
//     E_OUTOFMEMORY : An out of memory error occurred.
//     E_FAIL        : The method was not successful
//
// Description:
//     The media parser manager IMLMediaParserManager::GetKnownFileExtensions
//     method determines which media parser object to call for each request
//     and internally calls this method to retrieve the file extensions
//     supported by this media parser.
//
//     The caller frees elements of this array using SysFreeString and frees
//     the array itself with CoTaskMemFree.
//
//============================================================================
HRESULT
CSampleMediaParser::GetKnownFileExtensions(ULONG *pcFileExtensions, BSTR **pprgFileExtensions)
{
    HRESULT hr = S_OK;
    BSTR   *prgLocalExt = NULL;  // Dynamic memory held local until ready to be returned with S_OK.
    DWORD   dwIndex = 0;
    
    ChkBool((pcFileExtensions != NULL), E_POINTER);
    ChkBool((pprgFileExtensions != NULL), E_POINTER);

    // Allocate containing array using memory shared with this COM object's client.
    prgLocalExt = (BSTR*)CoTaskMemAlloc(sizeof(BSTR) * m_dwEntityExtensionCount);
    ChkBool((prgLocalExt != NULL), E_OUTOFMEMORY);

    // Initialize allocated memory.  Use NULL to detect allocated elements during emergency cleanup.
    memset((void *)prgLocalExt, 0, sizeof(BSTR) * m_dwEntityExtensionCount);

    // Fill allocated array with copies of supported extensions.
    for (dwIndex = 0; dwIndex < m_dwEntityExtensionCount; dwIndex++)
    {
        ASSERT(m_rgEntityExtensions[dwIndex] != NULL);

        // Give caller a copy of each element.
        prgLocalExt[dwIndex] = SysAllocString(m_rgEntityExtensions[dwIndex]);
        ChkBool((prgLocalExt[dwIndex] != NULL), E_OUTOFMEMORY);
    }

    // Safe to make assignments to output arguments.
    *pcFileExtensions = m_dwEntityExtensionCount;
    *pprgFileExtensions = prgLocalExt;

    // Don't clean up memory passed to caller.
    prgLocalExt = NULL;

Cleanup:
    if (prgLocalExt)
    {
        ASSERT(FAILED(hr)); // If allocated memory is still local, there will be an error.

        // Free local allocated memory.
        for (dwIndex = 0; dwIndex < m_dwEntityExtensionCount; dwIndex++)
        {
            if (prgLocalExt[dwIndex] != NULL)
            {
                SysFreeString(prgLocalExt[dwIndex]);
            }
        }
        CoTaskMemFree(prgLocalExt);
    }
    return hr;
}

//============================================================================
//
// GetEntityType
//
// Parameters:
//     MLDSItem file    : File from which to determine the entity type.
// out ULONG *pEntityId : EntityId belonging to the file.
//
// Possible Result Codes:
//     S_OK      : success
//     S_FALSE   : unknown file extension
//     E_POINTER : parameters invalid
//
// Description:
//     Searches for the extension found in file.URL in the known entity
//     list for a match. Does not read the file, just checks the extension.
//
//============================================================================

HRESULT
CSampleMediaParser::GetEntityType(MLDSItem file, ULONG *pEntityType)
{
    HRESULT hr = S_OK;
    WCHAR   wsExtension[cchMaxExtension];

    ChkBool((pEntityType != NULL), E_POINTER);

    Chk(GetURLExtension(file.URL, wsExtension, cchMaxExtension));
    if (hr == S_OK)
    {
        Chk(GetKnownEntity(wsExtension, (EntityId *)pEntityType));
    }

Cleanup:
    return hr;
}

//============================================================================
//
// ExtractMetadata
//
// Parameters:
//     MLDSItem file                      : File to parse.
//     IMLPropertySet *pPropertySetWanted : Metadata requested by caller.
//     IMLPropertySet *pFileMetadata      : Metadata collection to return to caller.
//
// Possible Result Codes:
//     S_OK      : call succeeded. 
//     E_POINTER : a property collection is invalid.
//     E_FAIL    : file system or other unknown failure
//
// Description:
//     Satisfies IMLMediaParser interface requirements.
//     Collects basic file system metadata from the file if it is of a known type.
//     Passes metadata on to StoreMetadata consumer, if provided, before returning.
//
//============================================================================
HRESULT
CSampleMediaParser::ExtractMetadata(MLDSItem file, IMLPropertySet *pPropertySetWanted, IMLPropertySet *pFileMetadata)
{
    HRESULT hr = S_OK;
    PropertyId propidFileURL = mlid_NONE;
    PropertyId propidFileName = mlid_NONE;
    PropertyId propidFolderName = mlid_NONE;
    PropertyId propidFileSize = mlid_NONE;
    PropertyId propidFileTime = mlid_NONE;
    ULONG      ulEntityType = (ULONG)ml_Entity_NONE;

    ChkBool((pFileMetadata != NULL), E_POINTER);

    Chk(GetEntityType(file, &ulEntityType));

    switch (ulEntityType)
    {
        case ml_Entity_video:
            propidFileURL = mlid_video_fileURL;
            propidFileName = mlid_video_fileName;
            propidFolderName = mlid_video_folderName;
            propidFileSize = mlid_video_fileSize;
            propidFileTime = mlid_video_fileTime;
            break;

        case ml_Entity_music:
            propidFileURL = mlid_music_fileURL;
            propidFileName = mlid_music_fileName;
            propidFolderName = mlid_music_folderName;
            propidFileSize = mlid_music_fileSize;
            propidFileTime = mlid_music_fileTime;
            break;

        case ml_Entity_photo:
            propidFileURL = mlid_photo_fileURL;
            propidFileName = mlid_photo_fileName;
            propidFolderName = mlid_photo_folderName;
            propidFileSize = mlid_photo_fileSize;
            propidFileTime = mlid_photo_fileTime;
            break;

        case ml_Entity_playlist:
            propidFileURL = mlid_playlist_fileURL;
            propidFileName = mlid_playlist_fileName;
            propidFolderName = mlid_playlist_folderName;
            propidFileSize = mlid_playlist_fileSize;
            propidFileTime = mlid_playlist_fileTime;
            break;

        case ml_Entity_generic:
            propidFileURL = mlid_generic_fileURL;
            propidFileName = mlid_generic_fileName;
            propidFolderName = mlid_generic_folderName;
            propidFileSize = mlid_generic_fileSize;
            propidFileTime = mlid_generic_fileTime;
            break;

        default:
            ChkBool(FALSE, E_FAIL);
            break;
    }

    Chk(SetFolderFileNames(file.URL, propidFileURL, propidFileName, propidFolderName, pPropertySetWanted, pFileMetadata));
    Chk(SetFileSizeAndTime(file.cbFileSize, file.ftTimeStamp, propidFileSize, propidFileTime, pPropertySetWanted, pFileMetadata));

Cleanup:
    return hr;
}

//============================================================================
//
// GetKnownEntity
//
// Parameters:
//     LPCWSTR wcsExtension   : The file name extension to query.
// out EntityId *pKnownEntity : The entity of the extension.
//
// Possible Result Codes:
//     S_OK   : call succeeded. 
//     E_FAIL : file system or other unknown failure
//
// Description:
//     References the registry for the entity id of a known extension.
//
//============================================================================
HRESULT
CSampleMediaParser::GetKnownEntity(LPCWSTR wcsExtension, EntityId *pKnownEntity)
{
    HRESULT hr = S_OK;
    CRegKey reg;
    LONG    lRet = 0L;
    WCHAR   szValueData[MAX_PATH];
    DWORD   dwValueDataCC = _countof(szValueData);

    lRet = reg.Open(HKEY_LOCAL_MACHINE, wcsEntityExtensionsRegKey, KEY_READ);
    ChkBool((lRet == ERROR_SUCCESS), E_FAIL);
    
    lRet = reg.QueryStringValue(wcsExtension, szValueData, &dwValueDataCC);
    ChkBool((lRet != ERROR_FILE_NOT_FOUND), S_FALSE);
    ChkBool((lRet == ERROR_SUCCESS), E_FAIL);

    // Interpret the entity label.  The character count includes z-term.  Subtract it for string comparison.
    hr = ConvertLabelToEntityId(szValueData, dwValueDataCC, pKnownEntity);

Cleanup:
    return hr;
}

//============================================================================
//
// GetURLExtension
//
// Parameters:
//       BSTR bFilename            : media library detected file name.
//   out LPWSTR wcsExtension       : In: pointer to string buffer. Out: extension of bFilename
// const USHORT uExtensionMaxChars : Capacity of wcsExtension in characters.
//
// Possible Result Codes:
//     S_OK         : call succeeded. 
//     E_POINTER    : bFilename or wcsExtension is NULL
//     E_UNEXPECTED : Output Buffer has no capacity.
//     E_FAIL       : file system or other unknown failure
//
// Description:
//     Search the file name for the extension part.
//     Presumes name is not that of a directory because Media Library does
//     not pass directories.
//
//============================================================================
HRESULT
CSampleMediaParser::GetURLExtension(BSTR bFilename, LPWSTR wcsExtension, const USHORT uExtensionMaxChars)
{
    HRESULT hr = S_OK;
    UINT    cchb = 0;
    LPCWSTR pFilenamePart = NULL;
    LPCWSTR pExtensionPart = NULL;

    ChkBool((bFilename != NULL), E_POINTER);
    ChkBool((wcsExtension != NULL), E_POINTER);
    ChkBool((uExtensionMaxChars > 0), E_UNEXPECTED);

    // Find filename part after path.
    pFilenamePart = wcsrchr(bFilename, L'\\');
    if (pFilenamePart == NULL)
    {
        // No path delimiter found; file name has no path part.
        pFilenamePart = bFilename;
    }

    // Find extension part.
    pExtensionPart = wcsrchr(pFilenamePart, L'.');
    if (pExtensionPart == NULL)
    {
        // No filename extension was found, which is OK.  Return empty string.
        ChkBool((uExtensionMaxChars > 0), E_FAIL);
        wcsExtension[0] = L'\0';
    }
    else
    {
        // Pointer now indicates L'.' and will need to indicate extension.
        pExtensionPart++;
        Chk(StringCchCopy(wcsExtension, uExtensionMaxChars, pExtensionPart));
    }

Cleanup:
    return hr;
}

//============================================================================
//
// ConvertLabelToEntityId
//
// Parameters:
//     LPCWSTR   szLabel   : Entity label which begins with ml_Entity_
//     DWORD     dwLabelCC : Character count of label (including z-term).
// out EntityId *pEntityId : Schema defined entity id.
//
// Possible Result Codes:
//     S_OK      : call succeeded. 
//     E_POINTER : Bad argument
//     E_FAIL    : Couldn't interpret the label.
//
// Description:
//     Interprets label as entity id.
//     Since string lengths have already been gathered, this method takes
//     advantage of these lengths to optimize the label search.
//
//============================================================================
HRESULT
CSampleMediaParser::ConvertLabelToEntityId(LPCWSTR szLabel, DWORD dwLabelCC, EntityId *pEntityId)
{
    HRESULT hr = S_OK;

    ChkBool((szLabel != NULL), E_POINTER);
    ChkBool((pEntityId != NULL), E_POINTER);

    if (0 == wcsncmp(szLabel, ENTITYLABELMUSIC, dwLabelCC))
    {
        *pEntityId = ml_Entity_music;
    } else if (0 == wcsncmp(szLabel, ENTITYLABELVIDEO, dwLabelCC))
    {
        *pEntityId = ml_Entity_video;
    } else if (0 == wcsncmp(szLabel, ENTITYLABELPHOTO, dwLabelCC))
    {
        *pEntityId = ml_Entity_photo;
    } else if (0 == wcsncmp(szLabel, ENTITYLABELGENERIC, dwLabelCC))
    {
        *pEntityId = ml_Entity_generic;
    } else if (0 == wcsncmp(szLabel, ENTITYLABELPLAYLIST, dwLabelCC))
    {
        *pEntityId = ml_Entity_playlist;
    } else
    {
        hr = S_FALSE;
    }

Cleanup:
    return hr;
}

//============================================================================
//
// SetFolderFileNames
//
// Parameters:
//     BSTR            fileURL            : file URL with the parts to extract.
//     PropertyId      propidFileURL      : File URL property id specific to the entity.
//     PropertyId      propidFileName     : File name property id specific to the entity.
//     PropertyId      propidFolderName   : Folder name property id specific to the entity.
//     IMLPropertySet *pPropertySetWanted : Requested properties.  If NULL, store all.
// out IMLPropertySet *pFileMetadata      : PropertySet to receive properties.
//
// Possible Result Codes:
//     S_OK          : call succeeded. 
//     E_FAIL        : Couldn't interpret the URL.
//     E_INVALIDARG  : An invalid propid value.
//     E_OUTOFMEMORY : Unable to dynamically allocate memory.
//
// Description:
//     Extracts the parts of the fileURL and stores them in the property set as requested.
//
//============================================================================
HRESULT
CSampleMediaParser::SetFolderFileNames(
    BSTR fileURL,
    PropertyId propidFileURL,
    PropertyId propidFileName,
    PropertyId propidFolderName,
    IMLPropertySet *pPropertySetWanted,
    IMLPropertySet *pFileMetadata)
{
    HRESULT hr = S_OK;

    BSTR   bstrFilename(NULL);
    BSTR   bstrFoldername(NULL);
    WCHAR  wcsParts[MAX_PATH];
    LPWSTR lpFilePart = NULL;
    LPWSTR lpFolderPart = NULL;
    DWORD  cchParts = 0L;
    DWORD  cchFilename = 0L;
    DWORD  cchPath = 0L;
    DWORD  dwAttributes = GetFileAttributes(fileURL);

    Chk(((propidFileURL > mlid_NONE && propidFileURL < mlid_MAX) ? S_OK : E_INVALIDARG));
    Chk(((propidFileName > mlid_NONE && propidFileName < mlid_MAX) ? S_OK : E_INVALIDARG));
    Chk(((propidFolderName > mlid_NONE && propidFolderName < mlid_MAX) ? S_OK : E_INVALIDARG));

    ChkBool((dwAttributes != INVALID_FILE_ATTRIBUTES), E_FAIL);
    
    // Can't accept hidden files or directories.
    ChkBool((dwAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY)) == 0, E_FAIL);

    cchParts = GetFullPathName(fileURL, MAX_PATH, wcsParts, &lpFilePart);
    ChkBool((cchParts != 0), E_FAIL);
    cchFilename = cchParts - (lpFilePart - wcsParts);
    cchPath = lpFilePart - wcsParts;

    bstrFilename = SysAllocStringLen(lpFilePart, cchFilename);
    ChkBool((bstrFilename != NULL), E_OUTOFMEMORY);

    lpFolderPart = &(wcsParts[cchPath - 1]);

    if (*lpFolderPart == L'\\' || *lpFolderPart == L'/')
    {
        UINT cchFolderName = 0;

        *lpFolderPart = L'\0';
        lpFolderPart--;

        while(lpFolderPart > wcsParts)
        {
            if (*lpFolderPart == L'\\' || *lpFolderPart == L'/')
            {
                lpFolderPart++;
                break;
            }
            else
            {
                lpFolderPart--;
            }
        }

        cchFolderName = cchPath - (lpFolderPart - wcsParts) - 1;
        bstrFoldername = SysAllocStringLen(lpFolderPart, cchFolderName);
    }

    if (pPropertySetWanted == NULL)
    {
        Chk(pFileMetadata->SetBSTR(propidFileURL, fileURL));
        Chk(pFileMetadata->SetBSTR(propidFileName, bstrFilename));
        Chk(pFileMetadata->SetBSTR(propidFolderName, bstrFoldername));
    }
    else
    {
        ULONG cProperties = 0;
        ULONG iProperty = 0;

        Chk(pPropertySetWanted->Count(&cProperties));

        for (iProperty = 0; iProperty < cProperties; iProperty++)
        {
            ULONG propertyId;
            BSTR bstr;
            
            Chk(pPropertySetWanted->GetIDByIndex(iProperty, &propertyId));
            
            if (propertyId == propidFileURL)
            {
                bstr = fileURL;
            }
            else if (propertyId == propidFileName)
            {
                bstr = bstrFilename;
            }
            else if (propertyId == propidFolderName)
            {
                bstr = bstrFoldername;
            }
            else
            {
                continue;
            }

            Chk(pFileMetadata->SetBSTR(propertyId, bstr));
        }
    }
Cleanup:

    // Free remaining dynamic memory.
    if (bstrFilename != NULL)
    {
        SysFreeString(bstrFilename);
    }
    if (bstrFoldername != NULL)
    {
        SysFreeString(bstrFoldername);
    }

    return hr;
}

//============================================================================
//
// SetFileSizeAndTime
//
// Parameters:
//     ULONGLONG llFileSize               : FileSize property value to store if requested.
//     FILETIME &fileTime                 : FileTime property value to store if requested.
//     PropertyId propidFileSize          : ML id representing file size.
//     PropertyId propidFileTime          : ML id representing file time.
//     IMLPropertySet *pPropertySetWanted : Requested properties.  If NULL, store all.
// out IMLPropertySet *pFileMetadata      : PropertySet receiving fileSize and/or fileTime
//
// Possible Result Codes:
//     S_OK         : Successfully stored one or both properties.
//     E_FAIL       : File time conversion failure.
//     E_INVALIDARG : An mlid arg is invalid
//     E_POINTER    : pFileMetadata argument is NULL
//
// Description:
//     Stores the file size and time properties, as requested, in the receiving property set.
//
//============================================================================

HRESULT
CSampleMediaParser::SetFileSizeAndTime(
    ULONGLONG llFileSize,
    const FILETIME &fileTime,
    PropertyId propidFileSize,
    PropertyId propidFileTime,
    IMLPropertySet *pPropertySetWanted,
    IMLPropertySet *pFileMetadata)
{
    HRESULT hr = S_OK;
    BOOL  fCollectAllProperties = (BOOL)(pPropertySetWanted == NULL);
    Chk(((propidFileSize > mlid_NONE && propidFileSize < mlid_MAX) ? S_OK : E_INVALIDARG));
    Chk(((propidFileTime > mlid_NONE && propidFileTime < mlid_MAX) ? S_OK : E_INVALIDARG));
    Chk((pFileMetadata != NULL ? S_OK : E_POINTER));

    if (fCollectAllProperties || S_OK == pPropertySetWanted->HasID(propidFileTime))
    {
        MLDateTime mldt;
        Chk(ConvertFileTimeToMLFileTime(&fileTime, &mldt));
        Chk(pFileMetadata->SetDATE(propidFileTime, &mldt));
    }

    if (fCollectAllProperties || S_OK == pPropertySetWanted->HasID(propidFileSize))
    {
        Chk(pFileMetadata->SetLONGLONG(propidFileSize, llFileSize));
    }

Cleanup:
    return hr;
}

//============================================================================
//
// ConvertFileTimeToMLFileTime
//
// Parameters:
//     FILETIME   *pftValue  : FILETIME value
// out MLDateTime *pmldtDest : MLDateTime value
//
// Possible Result Codes:
//     S_OK : success
//     E_FAIL : could not convert
//     E_POINTER : parameter validation
//
// Description:
//     Converts from the CE FILETIME structure to a MLDateTime.
//     No range checking is done on the output values.
//
//============================================================================

HRESULT
CSampleMediaParser::ConvertFileTimeToMLFileTime(FILETIME const *pftValue, MLDateTime *pmldtDest)
{
    HRESULT hr = S_OK;
    SYSTEMTIME stValue;
    
    ChkBool((pftValue != NULL), E_POINTER);
    ChkBool((pmldtDest != NULL), E_POINTER);
    
    ChkBool(FileTimeToSystemTime(pftValue, &stValue), E_FAIL);
    
    pmldtDest->year     = stValue.wYear;
    pmldtDest->month    = stValue.wMonth;
    pmldtDest->day      = stValue.wDay;
    pmldtDest->hour     = stValue.wHour;
    pmldtDest->minute   = stValue.wMinute;
    pmldtDest->second   = stValue.wSecond;
    pmldtDest->fraction = stValue.wMilliseconds * (1000L * 1000L); // fraction is "billionths" of a second
    
Cleanup:
    return hr;
}

See Also

Concepts

Build and Run the Sample Media Parser
Step 1: Create the Dynamic-Link Library Subproject in Your OS Design
Step 2: Configure the Subproject Properties for Use with COM and Media Library
Step 3: Modify Subproject Parameter and Source Files for Use with COM and Media Library
Step 5: Add the SampleMediaParser Source Code to the Subproject
Step 6: Build the OS Design and Subproject
Step 7: (Optional) Debug the Media Parser Plug-In Subproject