Properties coding expedition #6 - Developer friendly output

Using the tool I developed in this series, I know that my test photo has "Rating: 5 Stars". But how is this value actually represented in the JPG itself? Let's answer that by adding some developer friendly output. Here's what I did:

First, I need a developer-friendly label. I can get the canonical name using PSGetNameFromPropertyKey. If a property doesn't have a canonical name, I just grab the "{GUID} PID" representation.

 HRESULT _PrintPropertyValue(__in REFPROPERTYKEY key, __in REFPROPVARIANT propvar)
{
    // 1. Get a canonical name for this property
    PWSTR pszName = NULL;
    HRESULT hr = PSGetNameFromPropertyKey(key, &pszName);
    if (FAILED(hr))
    {
        // 1b. There's no canonical name: get the "{GUID} PID" string.
        WCHAR szKey[100];
        hr = PSStringFromPropertyKey(key, szKey, ARRAYSIZE(szKey));
        if (SUCCEEDED(hr))
        {
            hr = SHStrDupW(szKey, &pszName);
        }
    }

Here, I get the type of the value. I'll show the helper later.

     PCWSTR pszType = NULL;
    if (SUCCEEDED(hr))
    {
        // 2. Get the type
        pszType = _GetVarTypeString(propvar.vt);
    }

Next, I get the raw value as a string. PropVariantToStringAlloc can convert most types to strings, but failing that I just print a message.

     PWSTR pszRawValue = NULL;
    if (SUCCEEDED(hr))
    {
        // 3. Get the raw value
        hr = PropVariantToStringAlloc(propvar, &pszRawValue);
        if (FAILED(hr))
        {
            hr = SHStrDupW(L"", &pszRawValue);
        }
    }

I also need the formatted value.

     PWSTR pszFormattedValue = NULL;
    if (SUCCEEDED(hr))
    {
        // 4. Get the formatted value
        hr = PSFormatForDisplayAlloc(key, propvar, PDFF_DEFAULT, &pszFormattedValue);
        if (FAILED(hr))
        {
            hr = SHStrDupW(L"", &pszFormattedValue);
        }
    }

Finally, I check to see if the formatted value differs from the raw value. Sometimes it does and sometimes it doesn't depending on the formatting that is performed. This results in slightly cleaner output:

     if (SUCCEEDED(hr))
    {
        // LRM RLM LRE RLE PDF LRO RLO
        _StripCharacters(pszFormattedValue, L"\x200e\x200f\x202a\x202b\x202c\x202d\x202e");
        _StripCharacters(pszRawValue, L"\x200e\x200f\x202a\x202b\x202c\x202d\x202e");
        if (StrCmpCW(pszFormattedValue, pszRawValue))
        {
            // The values are different: print them both
            wprintf(L"%s: %s: %s --> %s\n", pszName, pszType, pszRawValue, pszFormattedValue);
        }
        else
        {
            // The values are the same: print just one
            wprintf(L"%s: %s == %s\n", pszName, pszType, pszFormattedValue);
        }
    }

    CoTaskMemFree(pszName);
    CoTaskMemFree(pszRawValue);
    CoTaskMemFree(pszFormattedValue);

    return hr;
}

As promised, here's theĀ helper function to obtain a string form of a VARTYPE:

 PCWSTR _GetVarTypeString(__in VARTYPE vt)
{
#define VARTYPEENTRY(v) case v: return L ## #v
    switch (vt)
    {
        VARTYPEENTRY(VT_EMPTY);
        VARTYPEENTRY(VT_UI1);
        ...[snip]...
    }
    return L"<Unknown type>";
}

I'll go over the output tomorrow.

-Ben Karas