c++winapiwindows-shellwindows-property-system

Why does changing the thread language and back fail for known folders?


I am trying to get language-specific text for known folders.

Thanks to the reply to my earlier post "How can I get column property names in different languages?" I use SetThreadUILanguage() to select the language and then try to get the locale name for a known folder.

My PC has English and Thai installed. English is the current PC language.

When changing the thread language, I find that for the property name, as per the previous post, IPropertyDescription::GetDisplayName() works perfectly.

However, getting property values is not working correctly.

A call to SHGetFileInfo() works after the first change of language, but not after the second.

A call to IPropertyStore::GetValue() only returns the PC language text.

There may be other methods to retrieve the text, but I haven't found any others.

I would like to use SHGetFileInfo(), or an alternative, to get the text in the thread's current language.

I attach a working console program that shows the problem.


#include <iostream>
#include <objbase.h>
#include <propsys.h>
#include <propkey.h>
#include <shellapi.h>
#include <Shlobj.h>


#pragma comment(lib, "Ole32.lib")          // Included libraries here to help build the test program.
#pragma comment(lib, "Propsys.lib")
#pragma comment(lib, "Shell32.lib")



void ShowPropName(const PROPERTYKEY& infoKey)
{
    IPropertyDescription   *pIDesc;
    WCHAR                  *pczPropName;

    if(PSGetPropertyDescription(infoKey, IID_PPV_ARGS(&pIDesc)) == S_OK)
    {
        pIDesc->GetDisplayName(&pczPropName);

        wprintf(L"Property Name <%s>\n", pczPropName);

        CoTaskMemFree(pczPropName);
        pIDesc->Release();
    }
}


void ShowItemNameFromFileInfo(REFKNOWNFOLDERID fidFolder)
{
    LPITEMIDLIST            pidlFolder;
    SHFILEINFO              infoFile;

    if(SHGetKnownFolderIDList(fidFolder, 0, NULL, &pidlFolder) == S_OK)
    {
        if(SHGetFileInfo((LPCTSTR)pidlFolder, 0, &infoFile, sizeof(infoFile), SHGFI_PIDL | SHGFI_DISPLAYNAME) != 0)
        {
            wprintf(L"Item Name <%s>\n", infoFile.szDisplayName);
        }

        ILFree(pidlFolder);
    }
}


void ShowItemNameFromPropStore(const PROPERTYKEY& infoKey, REFKNOWNFOLDERID fidFolder)
{
    LPITEMIDLIST            pidlFolder;
    IPropertyStore         *pIStore;
    PROPVARIANT             propValue;

    if(SHGetKnownFolderIDList(fidFolder, 0, NULL, &pidlFolder) == S_OK)
    {
        if(SHGetPropertyStoreFromIDList(pidlFolder, GPS_DEFAULT, IID_IPropertyStore, (void **)&pIStore) == S_OK)
        {
            PropVariantInit(&propValue);
    
            if(pIStore->GetValue(infoKey, &propValue) == S_OK)
            {
                wprintf(L"Item Name <%s>\n", propValue.bstrVal);

                PropVariantClear(&propValue);
            }

            if(pIStore != NULL)
            {
                pIStore->Release();
            }
        }
    }
}


int main()
{
    LANGID         nLangPC;
    LANGID         nLang2;
    
    CoInitialize(nullptr);

    nLang2  = 0x041E;
    nLangPC = GetThreadUILanguage();

    wprintf(L"\n------- 0x%04X ------------\n", nLang2);

    SetThreadUILanguage(nLang2);                                // Set language 2
    
    ShowPropName(PKEY_FileName);                                // Language 2 - Correct

    ShowItemNameFromFileInfo(FOLDERID_Music);                   // Language 2 - Correct
    ShowItemNameFromPropStore(PKEY_FileName, FOLDERID_Music);   // Language PC - Wrong

    wprintf(L"\n------- 0x%04X ------------\n", nLangPC);

    SetThreadUILanguage(nLangPC);                               // Set language PC

    ShowPropName(PKEY_FileName);                                // Language PC - Correct

    ShowItemNameFromFileInfo(FOLDERID_Music);                   // Language 2 - Wrong
    ShowItemNameFromPropStore(PKEY_FileName, FOLDERID_Music);   // Language PC - Correct


    CoUninitialize();

    return 0;
}


Solution

  • A file/directory name cannot be "localized". But Windows has a specific Known Folders API that you can use to determine a known folder's localized name in any supported/installed Windows language.

    Once you get an IKnownFolder COM reference, you can use the IKnownFolder::GetFolderDefinition method to get its pszLocalizedName. It's usually in a form like this: @%SystemRoot%\system32\windows.storage.dll,-21790

    From this string you can use the SHLoadIndirectString function which will automatically determine the localized value using the current thread's UI language.

    Here is a complete sample code:

    #include <windows.h>
    #include <stdio.h>
    #include <propkey.h>
    #include <shlwapi.h>
    #include <Shlobj.h>
    
    #pragma comment(lib, "propsys.lib")
    #pragma comment(lib, "shlwapi.lib")
    
    void ShowPropName(REFPROPERTYKEY key)
    {
        IPropertyDescription* desc;
        PSGetPropertyDescription(key, IID_PPV_ARGS(&desc));
        if (desc)
        {
            WCHAR* name;
            desc->GetDisplayName(&name);
            if (name)
            {
                wprintf(L"Property Name <%s>\n", name);
                CoTaskMemFree(name);
            }
            desc->Release();
        }
    }
    
    void ShowKnownFolderName(REFKNOWNFOLDERID id)
    {
        IKnownFolderManager* mgr;
        CoCreateInstance(CLSID_KnownFolderManager, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&mgr));
        if (mgr)
        {
            IKnownFolder* folder;
            mgr->GetFolder(id, &folder);
            if (folder)
            {
                KNOWNFOLDER_DEFINITION def;
                if (SUCCEEDED(folder->GetFolderDefinition(&def)) && def.pszLocalizedName)
                {
                    WCHAR localizedName[256];
                    SHLoadIndirectString(def.pszLocalizedName, localizedName, _countof(localizedName), nullptr);
                    wprintf(L"Folder Name <%s>\n", localizedName);
                }
                folder->Release();
            }
            mgr->Release();
        }
    }
    
    int main()
    {
        LANGID nLangPC;
        LANGID nLang2;
    
        CoInitialize(nullptr);
    
        nLang2 = 0x040C; // french
        nLangPC = GetThreadUILanguage();
    
        wprintf(L"\n------- 0x%04X ------------\n", nLang2);
    
        SetThreadUILanguage(nLang2);
        ShowPropName(PKEY_FileName);
        ShowKnownFolderName(FOLDERID_Music);
    
        wprintf(L"\n------- 0x%04X ------------\n", nLangPC);
    
        SetThreadUILanguage(nLangPC);
        ShowPropName(PKEY_FileName);
        ShowKnownFolderName(FOLDERID_Music);
    
        CoUninitialize();
        return 0;
    }