如何获得虚拟已知文件夹的本地化名称(例如此计算机,控制面板等)?
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();
}
}
不幸的是,提到“此计算机”和“控制面板”条目没有本地化名称。
答案 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;
}
}