2017-01-02 5 views
1

Wie kann man den Namen des virtuellen bekannten Ordners (wie This Computer, Control Panel etc.) lokalisieren?Wie erhält man den lokalisierten Namen des bekannten Ordners?

Eg. für PL-pl wären sie jeweils "Zehn computer", "Panel sterowania".


Wie bereits angedeutet, habe ich versucht IKnownFolder von Shell32 zu verwenden. Es gibt eine ready-to-use-Implementierung dieser APIs, WinAPICodePack. Beispielcode:

Leider erwähnte "Diese Computer" und "Systemsteuerung" Einträge hat keinen lokalisierten Namen.

+0

https://blogs.msdn.microsoft.com/oldnewthing/20160208 -00 /? P = 93001, aber Sie suchen nach dem Feld pszLocalizedName –

+0

Eigentlich rate ich vom Lesen der Dokumente hier https://msdn.microsoft.com/en-us/library/windows/desktop/bb773325(v=vs. 85). Aspx, dass Sie den Namen aus desktop.ini lesen müssen, da es sich ändern kann –

+0

@DavidHeffernan: vielleicht tut GetFolderDefinition das für Sie? –

Antwort

1

Hinweis: .NET-Lösung auf der Unterseite.


Sie benötigen bekam IShellItem Schnittstelle für Ihre Ordner und rufen IShellItem::GetDisplayName mit SIGDN_NORMALDISPLAY

In UI dieser Name zur Anzeige für den Anwender in der Regel ideal ist.

diese Rückkehr lokalisierte Namen

Code in C++ kann wie sein diese

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

auch wenn wir nur auf Vista + laufen wir SHGetKnownFolderIDList statt SHGetFolderLocation mit FOLDERID_ComputerFolder anstelle verwenden können CSIDL_DRIVES oder wir können (oder haben) bereits IKnownFolder Schnittstelle zuerst und dann IShellItem davon durcherhalten- so noch zwei alternative Varianten beginnen von 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(); 
    } 
} 

sonst ein Weg - IShellFolder::GetDisplayNameOf mit diesem Code verwenden wie

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

Sie können WinApiCodePack Bibliothek verwenden aussehen werden (Download von Nuget), die .NET-Implementierung mehrerer der zuvor genannten APIs bietet. Beispielcode würde aussehen wie folgt:

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

Auch die WinApiInterop-Klasse, die lokalisierte Strings aus desktop.ini löst:

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

'S_OK" ist einer der vielen Erfolgscodes in COM. Wenn Sie nach Erfolg suchen möchten, verwenden Sie [ERFOLGREICH] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms687197.aspx) (oder [FAILED] (https: // msdn. microsoft.com/en-us/library/windows/desktop/ms693474.aspx)) Makro. – IInspectable

+0

0 <= hr ist auch sehr undurchsichtig. Wie ist die Verwendung von 0 statt NULL. –

+0

mag sein wie du sagst und korrekter, aber COM ist darin nicht ausreichend klar. Ich benutze '(hr == S_OK)', wo in MSDN direkt schreiben: 'Wenn diese Methode erfolgreich ist, wird S_OK zurückgegeben. Andernfalls wird ein HRESULT-Fehlercode zurückgegeben. 'Für general -' Laut der COM-Spezifikation bedeutet ein Ergebnis von Null Erfolg und ein Ergebnis ungleich Null zeigt Fehler an 'und 'Eine einfache Überprüfung auf Null oder ungleich Null ist ausreichend.' - https://msdn.microsoft.com/en-us/library/windows/desktop/ms688560(v=vs.85).aspx. – RbMm

Verwandte Themen