如何获取已知文件夹的本地化名称?

时间:2017-01-02 08:30:03

标签: c# winapi

如何获得虚拟已知文件夹的本地化名称(例如此计算机,控制面板等)?

EG。对于PL-pl,它们分别是“Ten komputer”,“Panel sterowania”。

根据建议,我尝试使用Shell32中的IKnownFolder。这些API的第三方即用型实现WinAPICodePack。示例代码:

class Program
{
    static void Main(string[] args)
    {
        // Add from nuget: WindowsAPICodePack-Shell

        foreach (var folder in KnownFolders.All)
        {
            Console.WriteLine($"Canonical name: {folder.CanonicalName}");
            Console.WriteLine($"\tPath exists: {folder.PathExists}");
            Console.WriteLine($"\tLocalized name: {folder.LocalizedName}");
        }

        Console.ReadLine();
    }
}

不幸的是,提到“此计算机”和“控制面板”条目没有本地化名称。

1 个答案:

答案 0 :(得分:2)

注意:底部的.NET解决方案。

您需要为您的文件夹设置IShellItem界面,并使用IShellItem::GetDisplayName

致电SIGDN_NORMALDISPLAY
  

在UI中,此名称通常适合显示给用户。

返回本地化名称

c ++中的代码可以像这样

HRESULT GetKnownFolderName(int csidl, PWSTR* ppszName)
{
    PIDLIST_ABSOLUTE pidl;

    HRESULT hr = SHGetFolderLocation(0, csidl, 0, 0, &pidl);

    if (S_OK == hr)
    {
        IShellItem* pItem;

        hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&pItem));

        ILFree(pidl);

        if (S_OK == hr)
        {
            hr = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);

            pItem->Release();
        }
    }

    return hr;
}

void testDN()
{
    if (0 <= CoInitialize(0))
    {
        PWSTR szName;
        // CSIDL_CONTROLS - for "Control Panel"
        // CSIDL_DRIVES - for "My Computer"
        if (S_OK == GetKnownFolderName(CSIDL_DRIVES, &szName))
        {
            DbgPrint("%S\n", szName);
            CoTaskMemFree(szName);
        }

        CoUninitialize();
    }
}

如果我们仅在Vista +上运行,我们可以使用SHGetKnownFolderIDList代替SHGetFolderLocation FOLDERID_ComputerFolder到位CSIDL_DRIVES,或者我们可以获得(或已经拥有){{3}首先是接口,然后通过IKnownFolder从它获得IShellItem - 所以两个替代变体从vista开始:

HRESULT GetKnownFolderName(IKnownFolder* kf, PWSTR* ppszName)
{
    IShellItem* psi;

    HRESULT hr = kf->GetShellItem(KF_FLAG_DEFAULT_PATH, IID_PPV_ARGS(&psi));

    if (S_OK == hr)
    {
        hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);

        psi->Release();
    }

    return hr;
}

HRESULT GetKnownFolderNameVista2(REFKNOWNFOLDERID rfid, PWSTR* ppszName)
{
    IKnownFolderManager* mgr;

    HRESULT hr = CoCreateInstance(__uuidof(KnownFolderManager), 0, CLSCTX_ALL, IID_PPV_ARGS(&mgr));

    if (0 <= hr)
    {
        IKnownFolder* kf;

        hr = mgr->GetFolder(rfid, &kf);

        mgr->Release();

        if (S_OK == hr)
        {
            hr = GetKnownFolderName(kf, ppszName);
            kf->Release();
        }
    }

    return hr;
}

HRESULT GetKnownFolderNameVista(REFKNOWNFOLDERID rfid, PWSTR* ppszName)
{
    PIDLIST_ABSOLUTE pidl;

    HRESULT hr = SHGetKnownFolderIDList(rfid, KF_FLAG_NO_ALIAS, 0, &pidl);

    if (S_OK == hr)
    {
        IShellItem* pItem;

        hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&pItem));

        ILFree(pidl);

        if (S_OK == hr)
        {
            hr = pItem->GetDisplayName(SIGDN_NORMALDISPLAY, ppszName);

            pItem->Release();
        }
    }

    return hr;
}

void testDN()
{
    if (0 <= CoInitialize(0))
    {
        PWSTR szName;
        if (S_OK == GetKnownFolderNameVista(FOLDERID_ControlPanelFolder, &szName))
        {
            DbgPrint("%S\n", szName);
            CoTaskMemFree(szName);
        }

        if (S_OK == GetKnownFolderNameVista2(FOLDERID_ComputerFolder, &szName))
        {
            DbgPrint("%S\n", szName);
            CoTaskMemFree(szName);
        }

        CoUninitialize();
    }
}

其他方式 - 使用此代码IKnownFolder::GetShellItem看起来像

HRESULT GetKnownFolderName(int csidl, PWSTR* ppszName)
{
    PIDLIST_ABSOLUTE pidl;

    HRESULT hr = SHGetFolderLocation(0, csidl, 0, 0, &pidl);

    if (S_OK == hr)
    {
        IShellFolder* psf;
        PCUITEMID_CHILD pidlLast;

        hr = SHBindToParent(pidl, IID_PPV_ARGS(&psf), &pidlLast);

        if (S_OK == hr)
        {
            STRRET str;
            hr = psf->GetDisplayNameOf(pidlLast, SHGDN_NORMAL, &str);
            psf->Release();

            if (hr == S_OK)
            {
                str.uType == STRRET_WSTR ? *ppszName = str.pOleStr, S_OK : hr = E_FAIL;
            }
        }
    }

    return hr;
}

void testDN()
{
    if (0 <= CoInitialize(0))
    {
        PWSTR szName;
        if (S_OK == GetKnownFolderName(CSIDL_DRIVES, &szName))
        {
            DbgPrint("%S\n", szName);
            CoTaskMemFree(szName);
        }

        if (S_OK == GetKnownFolderName(CSIDL_CONTROLS, &szName))
        {
            DbgPrint("%S\n", szName);
            CoTaskMemFree(szName);
        }

        CoUninitialize();
    }
}

您可以使用IShellFolder::GetDisplayNameOf库(从Nuget下载),它提供了之前提到的几个API的.NET实现。示例代码如下所示:

private static string GenerateLocalizedName(IKnownFolder shellFolder)
{
    // Attempt to obtain localized name of folder

    // 1. Directly from KnownFolder
    string localizedName = shellFolder.LocalizedName;

    // 2. From ShellObject (this solves This Computer and Control Panel issue)
    if (String.IsNullOrEmpty(localizedName))
        localizedName = (shellFolder as ShellObject)?.Name;

    // 3. If folder is not virtual, use its localized name from desktop.ini
    if (String.IsNullOrEmpty(localizedName) && Directory.Exists(shellFolder.Path))
    {
        try
        {
            localizedName = WinApiInterop.GetLocalizedName(shellFolder.Path);
        }
        catch
        {
            // Intentionally left empty
        }
    }

    // 4. If folder is not virtual, use its filename
    if (String.IsNullOrEmpty(localizedName) && Directory.Exists(shellFolder.Path))
        localizedName = Path.GetFileName(shellFolder.Path);

    // 5. If everything else fails, use its canonicalName (eg. MyComputerFolder)
    if (String.IsNullOrEmpty(localizedName))
        localizedName = shellFolder.CanonicalName;

    return localizedName;
}

private void LoadShellFolders()
{
    foreach (var shellFolder in KnownFolders.All)
    {
        string localizedName = GenerateLocalizedName(shellFolder);

        string comment = shellFolder.PathExists ? shellFolder.Path : $"shell:{shellFolder.CanonicalName}";

        infos.Add(new ShellFolderInfo(shellFolder.CanonicalName,
            localizedName,
            comment,
            shellFolder.CanonicalName,
            shellFolder.PathExists ? shellFolder.Path : null));
    }
}

另外,WinApiInterop类可以解析desktop.ini中的本地化字符串:

static class WinApiInterop
{
    [DllImport("shell32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
    internal static extern int SHGetLocalizedName(string pszPath, StringBuilder pszResModule, ref int cch, out int pidsRes);

    [DllImport("user32.dll", EntryPoint = "LoadStringW", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
    internal static extern int LoadString(IntPtr hModule, int resourceID, StringBuilder resourceValue, int len);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "LoadLibraryExW")]
    internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);

    internal const uint DONT_RESOLVE_DLL_REFERENCES = 0x00000001;
    internal const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002;

    [DllImport("kernel32.dll", ExactSpelling = true)]
    internal static extern int FreeLibrary(IntPtr hModule);

    [DllImport("kernel32.dll", EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, ExactSpelling = true)]
    internal static extern uint ExpandEnvironmentStrings(string lpSrc, StringBuilder lpDst, int nSize);

    public static string GetFullPath(string path)
    {
        StringBuilder sb = new StringBuilder(1024);

        ExpandEnvironmentStrings(path, sb, sb.Capacity);

        return sb.ToString();
    }

    public static string GetLocalizedName(string path)
    {
        StringBuilder resourcePath = new StringBuilder(1024);
        StringBuilder localizedName = new StringBuilder(1024);
        int len, id;
        len = resourcePath.Capacity;

        if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0)
        {               
            ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity);
            IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
            if (hMod != IntPtr.Zero)
            {
                if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0)
                {
                    return localizedName.ToString();
                }
                FreeLibrary(hMod);
            }
        }

        return null;
    }
}