From 9b05174a97005bba1997c260c69764efc8049e8a Mon Sep 17 00:00:00 2001 From: Vic <10308169+VictoriousRaptor@users.noreply.github.com> Date: Fri, 30 Dec 2022 20:56:30 +0800 Subject: [PATCH] Use localized name for shell link programs --- .../Programs/ShellLocalization.cs | 92 +++++++++++++++++++ .../Programs/Win32.cs | 20 ++-- 2 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs new file mode 100644 index 000000000..4f344d89e --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/ShellLocalization.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + + +namespace Flow.Launcher.Plugin.Program.Programs +{ + // From PT Run + /// + /// Class to get localized name of shell items like 'My computer'. The localization is based on the 'windows display language'. + /// Reused code from https://stackoverflow.com/questions/41423491/how-to-get-localized-name-of-known-folder for the method + /// + public static class ShellLocalization + { + internal const uint DONTRESOLVEDLLREFERENCES = 0x00000001; + internal const uint LOADLIBRARYASDATAFILE = 0x00000002; + + [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); + + [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); + + /// + /// Returns the localized name of a shell item. + /// + /// Path to the shell item (e. g. shortcut 'File Explorer.lnk'). + /// The localized name as string or . + public static string GetLocalizedName(string path) + { + StringBuilder resourcePath = new StringBuilder(1024); + StringBuilder localizedName = new StringBuilder(1024); + int len, id; + len = resourcePath.Capacity; + + // If there is no resource to localize a file name the method returns a non zero value. + if (SHGetLocalizedName(path, resourcePath, ref len, out id) == 0) + { + _ = ExpandEnvironmentStrings(resourcePath.ToString(), resourcePath, resourcePath.Capacity); + IntPtr hMod = LoadLibraryEx(resourcePath.ToString(), IntPtr.Zero, DONTRESOLVEDLLREFERENCES | LOADLIBRARYASDATAFILE); + if (hMod != IntPtr.Zero) + { + if (LoadString(hMod, id, localizedName, localizedName.Capacity) != 0) + { + string lString = localizedName.ToString(); + _ = FreeLibrary(hMod); + return lString; + } + + _ = FreeLibrary(hMod); + } + } + + return string.Empty; + } + + /// + /// This method returns the localized path to a shell item (folder or file) + /// + /// The path to localize + /// The localized path or the original path if localized version is not available + public static string GetLocalizedPath(string path) + { + path = Environment.ExpandEnvironmentVariables(path); + string ext = Path.GetExtension(path); + var pathParts = path.Split("\\"); + string[] locPath = new string[pathParts.Length]; + + for (int i = 0; i < pathParts.Length; i++) + { + int iElements = i + 1; + string lName = GetLocalizedName(string.Join("\\", pathParts[..iElements])); + locPath[i] = !string.IsNullOrEmpty(lName) ? lName : pathParts[i]; + } + + string newPath = string.Join("\\", locPath); + newPath = !newPath.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase) ? newPath + ext : newPath; + + return newPath; + } + } +} diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f8c220610..3bbe55d38 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -44,6 +44,9 @@ namespace Flow.Launcher.Plugin.Program.Programs public bool Enabled { get; set; } public string Location => ParentDirectory; + // Localized name based on windows display language + public string LocalizedName { get; set; } = string.Empty; + private const string ShortcutExtension = "lnk"; private const string UrlExtension = "url"; private const string ExeExtension = "exe"; @@ -69,27 +72,30 @@ namespace Flow.Launcher.Plugin.Program.Programs string title; MatchResult matchResult; + // Name of the result + string resultName = string.IsNullOrEmpty(LocalizedName) ? Name : LocalizedName; + // We suppose Name won't be null - if (!Main._settings.EnableDescription || Description == null || Name.StartsWith(Description)) + if (!Main._settings.EnableDescription || Description == null || resultName.StartsWith(Description)) { - title = Name; + title = resultName; matchResult = StringMatcher.FuzzySearch(query, title); } - else if (Description.StartsWith(Name)) + else if (Description.StartsWith(resultName)) { title = Description; matchResult = StringMatcher.FuzzySearch(query, Description); } else { - title = $"{Name}: {Description}"; - var nameMatch = StringMatcher.FuzzySearch(query, Name); + title = $"{resultName}: {Description}"; + var nameMatch = StringMatcher.FuzzySearch(query, resultName); var desciptionMatch = StringMatcher.FuzzySearch(query, Description); if (desciptionMatch.Score > nameMatch.Score) { for (int i = 0; i < desciptionMatch.MatchData.Count; i++) { - desciptionMatch.MatchData[i] += Name.Length + 2; // 2 is ": " + desciptionMatch.MatchData[i] += resultName.Length + 2; // 2 is ": " } matchResult = desciptionMatch; } @@ -297,6 +303,8 @@ namespace Flow.Launcher.Plugin.Program.Programs } } + program.LocalizedName = ShellLocalization.GetLocalizedName(path); + return program; } catch (COMException e)