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;
}
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;
}