You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切换线程语言后恢复,获取已知文件夹多语言文本失败原因?

问题:获取特定语言的已知文件夹文本异常

我想获取已知文件夹的特定语言文本,参考之前的思路用SetThreadUILanguage()切换线程语言后获取区域名称。我的系统安装了英文和泰语,当前系统语言为英文。切换线程语言后,IPropertyDescription::GetDisplayName()能正常获取对应语言的属性名称,但获取属性值时出现以下异常:

  • SHGetFileInfo()第一次切换到泰语时可正常返回对应名称,但切回系统语言后,依然返回泰语文本,失效
  • IPropertyStore::GetValue()始终只返回系统语言(英文)的文本

我需要用SHGetFileInfo()或替代方法,获取当前线程语言对应的文件夹文本,测试代码如下:

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

解决方法

修复SHGetFileInfo()缓存问题

SHGetFileInfo()会缓存获取到的显示名称,导致切换语言后返回旧值。两种解决方式:

  1. 禁用缓存:调用SHGetFileInfo时添加SHGFI_NO_CACHE标志,修改后的函数:
void ShowItemNameFromFileInfo(REFKNOWNFOLDERID fidFolder)
{
    LPITEMIDLIST            pidlFolder;
    SHFILEINFO              infoFile;

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

        ILFree(pidlFolder);
    }
}
  1. 刷新缓存:切换语言后调用SHChangeNotify()清除对应项的缓存:
// 在SetThreadUILanguage之后调用
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidlFolder, NULL);

替换IPropertyStore::GetValue()获取本地化名称

IPropertyStore获取的是文件原始属性值,不支持线程语言本地化。改用IShellFolder::GetDisplayNameOf(),它会尊重当前线程UI语言设置,新增以下函数:

void ShowItemNameFromShellFolder(REFKNOWNFOLDERID fidFolder)
{
    LPITEMIDLIST pidlFolder;
    if (SHGetKnownFolderIDList(fidFolder, 0, NULL, &pidlFolder) != S_OK)
        return;

    IShellFolder* pDesktopFolder;
    if (SHGetDesktopFolder(&pDesktopFolder) != S_OK)
    {
        ILFree(pidlFolder);
        return;
    }

    LPITEMIDLIST pidlRelative;
    // 将绝对PIDL转换为相对桌面的PIDL
    if (pDesktopFolder->ParseDisplayName(NULL, NULL, (LPOLESTR)pidlFolder, NULL, &pidlRelative, NULL) != S_OK)
    {
        pDesktopFolder->Release();
        ILFree(pidlFolder);
        return;
    }

    STRRET strDispName;
    // 获取本地化显示名称
    if (pDesktopFolder->GetDisplayNameOf(pidlRelative, SHGDN_NORMAL, &strDispName) == S_OK)
    {
        WCHAR szDisplayName[MAX_PATH];
        StrRetToBuf(&strDispName, pidlRelative, szDisplayName, MAX_PATH);
        wprintf(L"Item Name <%s>\n", szDisplayName);
    }

    ILFree(pidlRelative);
    pDesktopFolder->Release();
    ILFree(pidlFolder);
}

在main函数中调用这个函数,即可获取当前线程语言对应的文件夹名称。

额外注意事项

  • 切换线程语言后,所有Shell接口调用必须在同一线程上下文执行
  • 频繁切换语言场景优先使用SHGFI_NO_CACHE,避免缓存干扰
  • IShellFolder::GetDisplayNameOf()是获取本地化文件夹名称的可靠方案,比IPropertyStore更适配多语言需求

内容的提问来源于stack exchange,提问作者davecupper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:04:59