[BUG] Icons of shortcuts not displayed correctly (#209, #218, #155), version 1.0.21.2

This commit is contained in:
Markus Hofknecht 2021-10-13 17:13:11 +02:00
parent a16a3687fa
commit 1e330d40a8
11 changed files with 234 additions and 105 deletions

View file

@ -545,7 +545,6 @@ namespace SystemTrayMenu.Business
foreach (DataGridViewRow row in dgv.Rows) foreach (DataGridViewRow row in dgv.Rows)
{ {
RowData rowData = (RowData)row.Cells[2].Value; RowData rowData = (RowData)row.Cells[2].Value;
rowData?.Dispose();
DisposeMenu(rowData.SubMenu); DisposeMenu(rowData.SubMenu);
} }
} }
@ -759,13 +758,13 @@ namespace SystemTrayMenu.Business
if (!AsEnumerable.Any(m => m.Visible)) if (!AsEnumerable.Any(m => m.Visible))
{ {
openCloseState = OpenCloseState.Default;
if (IconReader.ClearIfCacheTooBig()) if (IconReader.ClearIfCacheTooBig())
{ {
GC.Collect(); GC.Collect();
MainPreload(); MainPreload();
} }
openCloseState = OpenCloseState.Default;
} }
} }

View file

@ -463,8 +463,8 @@ namespace SystemTrayMenu
private static void UpgradeIfNotUpgraded() private static void UpgradeIfNotUpgraded()
{ {
string path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming).FilePath; // string path = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoaming).FilePath;
path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); // path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (!Settings.Default.IsUpgraded) if (!Settings.Default.IsUpgraded)
{ {
Settings.Default.Upgrade(); Settings.Default.Upgrade();

View file

@ -17,7 +17,7 @@ namespace SystemTrayMenu.DataClasses
using TAFactory.IconPack; using TAFactory.IconPack;
using Menu = SystemTrayMenu.UserInterface.Menu; using Menu = SystemTrayMenu.UserInterface.Menu;
internal class RowData : IDisposable internal class RowData
{ {
private static readonly Icon White50PercentageIcon = Properties.Resources.White50Percentage; private static readonly Icon White50PercentageIcon = Properties.Resources.White50Percentage;
private static readonly Icon NotFoundIcon = Properties.Resources.NotFound; private static readonly Icon NotFoundIcon = Properties.Resources.NotFound;
@ -26,8 +26,6 @@ namespace SystemTrayMenu.DataClasses
private string arguments; private string arguments;
private string text; private string text;
private Icon icon; private Icon icon;
private bool diposeIcon = true;
private bool isDisposed;
internal RowData() internal RowData()
{ {
@ -59,13 +57,7 @@ namespace SystemTrayMenu.DataClasses
internal bool IconLoading { get; set; } internal bool IconLoading { get; set; }
public void Dispose() internal string FilePathIcon { get; set; }
{
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
internal void SetText(string text) internal void SetText(string text)
{ {
@ -79,9 +71,7 @@ namespace SystemTrayMenu.DataClasses
if (HiddenEntry) if (HiddenEntry)
{ {
row[0] = IconReader.AddIconOverlay( row[0] = IconReader.AddIconOverlay(data.icon, White50PercentageIcon);
data.icon,
White50PercentageIcon);
} }
else else
{ {
@ -111,10 +101,8 @@ namespace SystemTrayMenu.DataClasses
} }
else if (isDirectory) else if (isDirectory)
{ {
icon = IconReader.GetFolderIconSTA( icon = IconReader.GetFolderIconWithCache(TargetFilePathOrig, IconReader.FolderType.Closed, false, true, out bool loading);
TargetFilePath, IconLoading = loading;
IconReader.FolderType.Closed,
false);
} }
else else
{ {
@ -138,18 +126,18 @@ namespace SystemTrayMenu.DataClasses
{ {
handled = SetSln(); handled = SetSln();
} }
else if (fileExtension == ".appref-ms")
{
showOverlay = true;
}
if (!handled) if (!handled)
{ {
try try
{ {
icon = IconReader.GetFileIconWithCache( FilePathIcon = TargetFilePathOrig;
TargetFilePathOrig, icon = IconReader.GetFileIconWithCache(FilePathIcon, showOverlay, true, out bool loading);
showOverlay,
true,
out bool loading);
IconLoading = loading; IconLoading = loading;
diposeIcon = false;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -215,8 +203,7 @@ namespace SystemTrayMenu.DataClasses
OpenItem(e, ref toCloseByDoubleClick); OpenItem(e, ref toCloseByDoubleClick);
} }
if (ContainsMenu && if (ContainsMenu && (e == null || e.Button == MouseButtons.Left))
(e == null || e.Button == MouseButtons.Left))
{ {
Log.ProcessStart(TargetFilePath); Log.ProcessStart(TargetFilePath);
if (!Properties.Settings.Default.StaysOpenWhenItemClicked) if (!Properties.Settings.Default.StaysOpenWhenItemClicked)
@ -228,21 +215,31 @@ namespace SystemTrayMenu.DataClasses
internal Icon ReadLoadedIcon() internal Icon ReadLoadedIcon()
{ {
bool showOverlay = false; if (ContainsMenu)
string fileExtension = Path.GetExtension(TargetFilePath);
if (fileExtension == ".lnk" || fileExtension == ".url")
{ {
showOverlay = true; icon = IconReader.GetFolderIconWithCache(TargetFilePathOrig, IconReader.FolderType.Closed, false, false, out bool loading);
IconLoading = loading;
}
else
{
bool showOverlay = false;
string fileExtension = Path.GetExtension(TargetFilePath);
if (fileExtension == ".lnk" || fileExtension == ".url" || fileExtension == ".appref-ms")
{
showOverlay = true;
}
string filePath = FilePathIcon;
if (string.IsNullOrEmpty(filePath))
{
filePath = TargetFilePathOrig;
}
icon = IconReader.GetFileIconWithCache(filePath, showOverlay, false, out bool loading);
IconLoading = loading;
} }
icon = IconReader.GetFileIconWithCache( if (!IconLoading && icon == null)
TargetFilePathOrig,
showOverlay,
false,
out bool loading);
IconLoading = loading;
if (!loading && icon == null)
{ {
icon = NotFoundIcon; icon = NotFoundIcon;
} }
@ -250,19 +247,6 @@ namespace SystemTrayMenu.DataClasses
return icon; return icon;
} }
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (diposeIcon)
{
icon?.Dispose();
}
}
isDisposed = true;
}
private void OpenItem(MouseEventArgs e, ref bool toCloseByOpenItem) private void OpenItem(MouseEventArgs e, ref bool toCloseByOpenItem)
{ {
if (!ContainsMenu && if (!ContainsMenu &&
@ -285,7 +269,8 @@ namespace SystemTrayMenu.DataClasses
if (string.IsNullOrEmpty(Path.GetExtension(resolvedLnkPath))) if (string.IsNullOrEmpty(Path.GetExtension(resolvedLnkPath)))
{ {
icon = IconReader.GetFolderIconSTA(TargetFilePath, IconReader.FolderType.Open, true); icon = IconReader.GetFolderIconWithCache(TargetFilePathOrig, IconReader.FolderType.Open, true, true, out bool loading);
IconLoading = loading;
handled = true; handled = true;
isLnkDirectory = true; isLnkDirectory = true;
} }
@ -300,8 +285,7 @@ namespace SystemTrayMenu.DataClasses
else else
{ {
IWshShell shell = new WshShell(); IWshShell shell = new WshShell();
IWshShortcut lnk = shell.CreateShortcut(TargetFilePath) IWshShortcut lnk = shell.CreateShortcut(TargetFilePath) as IWshShortcut;
as IWshShortcut;
arguments = lnk.Arguments; arguments = lnk.Arguments;
workingDirectory = lnk.WorkingDirectory; workingDirectory = lnk.WorkingDirectory;
TargetFilePath = resolvedLnkPath; TargetFilePath = resolvedLnkPath;
@ -324,15 +308,17 @@ namespace SystemTrayMenu.DataClasses
{ {
if (FileUrl.GetDefaultBrowserPath(out string browserPath)) if (FileUrl.GetDefaultBrowserPath(out string browserPath))
{ {
icon = IconReader.GetFileIconWithCache(browserPath, true, true, out bool loading); FilePathIcon = browserPath;
icon = IconReader.GetFileIconWithCache(FilePathIcon, true, true, out bool loading);
IconLoading = loading; IconLoading = loading;
diposeIcon = false;
handled = true; handled = true;
} }
} }
else if (System.IO.File.Exists(iconFile)) else if (System.IO.File.Exists(iconFile))
{ {
icon = Icon.ExtractAssociatedIcon(iconFile); FilePathIcon = iconFile;
icon = IconReader.GetFileIconWithCache(FilePathIcon, true, true, out bool loading);
IconLoading = loading;
handled = true; handled = true;
} }
else else
@ -342,10 +328,7 @@ namespace SystemTrayMenu.DataClasses
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Warn( Log.Warn($"path:'{TargetFilePath}', iconFile:'{iconFile}'", ex);
$"path:'{TargetFilePath}', " +
$"iconFile:'{iconFile}'",
ex);
} }
SetText($"{FileInfo.Name[0..^4]}"); SetText($"{FileInfo.Name[0..^4]}");
@ -356,24 +339,15 @@ namespace SystemTrayMenu.DataClasses
private bool SetSln() private bool SetSln()
{ {
bool handled = false; bool handled = false;
StringBuilder executable = new StringBuilder(1024);
try try
{ {
DllImports.NativeMethods.Shell32FindExecutable(TargetFilePath, string.Empty, executable); icon = IconReader.GetExtractAllIconsLastWithCache(TargetFilePathOrig, true, out bool loading);
IconLoading = loading;
// icon = IconReader.GetFileIcon(executable, false);
// e.g. VS 2019 icon, need another icom in imagelist
List<Icon> extractedIcons = IconHelper.ExtractAllIcons(
executable.ToString());
icon = extractedIcons.Last();
handled = true; handled = true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Warn( Log.Warn($"path:'{TargetFilePath}'", ex);
$"path:'{TargetFilePath}', " +
$"executable:'{executable}'",
ex);
} }
return handled; return handled;

View file

@ -31,9 +31,7 @@ namespace SystemTrayMenu.Helper
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
#if DEBUG
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
#endif
} }
internal void CellMouseEnter(object sender, DataGridViewCellEventArgs newEventArgs) internal void CellMouseEnter(object sender, DataGridViewCellEventArgs newEventArgs)

View file

@ -85,7 +85,7 @@ namespace SystemTrayMenu.Helpers
} }
title = Truncate(title, 128); // max 255 title = Truncate(title, 128); // max 255
string Truncate(string value, int maxLength) static string Truncate(string value, int maxLength)
{ {
if (!string.IsNullOrEmpty(value) && if (!string.IsNullOrEmpty(value) &&
value.Length > maxLength) value.Length > maxLength)

View file

@ -54,9 +54,7 @@ namespace SystemTrayMenu.UserInterface
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
#if DEBUG
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
#endif
} }
internal void Fade(FadingState state) internal void Fade(FadingState state)

View file

@ -18,7 +18,6 @@ namespace SystemTrayMenu.DllImports
[Flags] [Flags]
internal enum TPM : uint internal enum TPM : uint
{ {
#pragma warning disable SA1602 // Enumeration items should be documented
LEFTBUTTON = 0x0000, // LEFTALIGN = 0x0000, // TOPALIGN = 0x0000, // HORIZONTAL = 0x0000, LEFTBUTTON = 0x0000, // LEFTALIGN = 0x0000, // TOPALIGN = 0x0000, // HORIZONTAL = 0x0000,
RIGHTBUTTON = 0x0002, RIGHTBUTTON = 0x0002,
CENTERALIGN = 0x0004, CENTERALIGN = 0x0004,
@ -35,7 +34,6 @@ namespace SystemTrayMenu.DllImports
VERNEGANIMATION = 0x2000, VERNEGANIMATION = 0x2000,
NOANIMATION = 0x4000, NOANIMATION = 0x4000,
LAYOUTRTL = 0x8000, LAYOUTRTL = 0x8000,
#pragma warning restore SA1602 // Enumeration items should be documented
} }
/// <summary> /// <summary>

View file

@ -39,5 +39,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.21.1")] [assembly: AssemblyVersion("1.0.21.2")]
[assembly: AssemblyFileVersion("1.0.21.1")] [assembly: AssemblyFileVersion("1.0.21.2")]

View file

@ -295,7 +295,7 @@
// //
// timerUpdateIcons // timerUpdateIcons
// //
this.timerUpdateIcons.Interval = 300; this.timerUpdateIcons.Interval = 100;
this.timerUpdateIcons.Tick += new System.EventHandler(this.TimerUpdateIcons_Tick); this.timerUpdateIcons.Tick += new System.EventHandler(this.TimerUpdateIcons_Tick);
// //
// Menu // Menu

View file

@ -1063,9 +1063,7 @@ namespace SystemTrayMenu.Utilities
int nResult = DllImports.NativeMethods.Shell32SHGetDesktopFolder(out IntPtr pUnkownDesktopFolder); int nResult = DllImports.NativeMethods.Shell32SHGetDesktopFolder(out IntPtr pUnkownDesktopFolder);
if (nResult != ResultOK) if (nResult != ResultOK)
{ {
#pragma warning disable CA1303 // Do not pass literals as localized parameters
throw new ShellContextMenuException("Failed to get the desktop shell folder"); throw new ShellContextMenuException("Failed to get the desktop shell folder");
#pragma warning restore CA1303 //=> Exceptions not translated in logfile => OK
} }
oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder)); oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder));

View file

@ -10,9 +10,11 @@ namespace SystemTrayMenu.Utilities
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using TAFactory.IconPack;
// from https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using // from https://www.codeproject.com/Articles/2532/Obtaining-and-managing-file-and-folder-icons-using
// added ImageList_GetIcon, IconCache, AddIconOverlay // added ImageList_GetIcon, IconCache, AddIconOverlay
@ -62,7 +64,81 @@ namespace SystemTrayMenu.Utilities
return cleared; return cleared;
} }
public static Icon GetFileIconWithCache(string filePath, bool linkOverlay, bool updateIconInBackground, out bool loading) public static Icon GetExtractAllIconsLastWithCache(string filePath, bool updateIconInBackground, out bool loading)
{
bool linkOverlay = false;
loading = false;
string key = filePath;
string extension = Path.GetExtension(filePath);
if (IsExtensionWithSameIcon(extension))
{
key = extension + linkOverlay;
}
ClearBadIcons();
if (!DictIconCache.TryGetValue(key, out Icon icon))
{
icon = LoadingIcon;
loading = true;
if (updateIconInBackground)
{
new Thread(UpdateIconInBackground).Start();
void UpdateIconInBackground()
{
DictIconCache.GetOrAdd(key, GetExtractAllIconsLast);
}
}
}
Icon GetExtractAllIconsLast(string keyExtension)
{
return GetExtractAllIconsLastSTA(filePath);
}
return icon;
}
public static Icon GetExtractAllIconsLastSTA(string filePath)
{
Icon icon = null;
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
icon = GetExtractAllIconsLast(filePath);
}
else
{
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
void StaThreadMethod(object obj)
{
icon = GetExtractAllIconsLast(filePath);
}
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start(icon);
staThread.Join();
}
static Icon GetExtractAllIconsLast(string filePath)
{
StringBuilder executable = new StringBuilder(1024);
DllImports.NativeMethods.Shell32FindExecutable(filePath, string.Empty, executable);
// icon = IconReader.GetFileIcon(executable, false);
// e.g. VS 2019 icon, need another icom in imagelist
List<Icon> extractedIcons = IconHelper.ExtractAllIcons(
executable.ToString());
return extractedIcons.Last();
}
return icon;
}
public static Icon GetFileIconWithCache(string filePath, bool linkOverlay, bool updateIconInBackground, out bool loading, string keyPath = "")
{ {
loading = false; loading = false;
string extension = Path.GetExtension(filePath); string extension = Path.GetExtension(filePath);
@ -73,11 +149,18 @@ namespace SystemTrayMenu.Utilities
} }
string key = filePath; string key = filePath;
if (!string.IsNullOrEmpty(keyPath))
{
key = keyPath;
}
if (IsExtensionWithSameIcon(extension)) if (IsExtensionWithSameIcon(extension))
{ {
key = extension + linkOverlay; key = extension + linkOverlay;
} }
ClearBadIcons();
if (!DictIconCache.TryGetValue(key, out Icon icon)) if (!DictIconCache.TryGetValue(key, out Icon icon))
{ {
icon = LoadingIcon; icon = LoadingIcon;
@ -101,6 +184,48 @@ namespace SystemTrayMenu.Utilities
return icon; return icon;
} }
public static Icon GetFolderIconWithCache(string path, FolderType folderType, bool linkOverlay, bool updateIconInBackground, out bool loading)
{
loading = false;
IconSize size = IconSize.Small;
// with this we get another folder icon than windows explorer
// if (Scaling.Factor > 1)
// {
// size = IconSize.Large;
// }
string key = path;
// Maybe we can reduce object when checking if a standard folder
// if (IsStandardFolderIcon(key))
// {
// key = "folder" + linkOverlay;
// }
ClearBadIcons();
if (!DictIconCache.TryGetValue(key, out Icon icon))
{
icon = LoadingIcon;
loading = true;
if (updateIconInBackground)
{
new Thread(UpdateIconInBackground).Start();
void UpdateIconInBackground()
{
DictIconCache.GetOrAdd(key, GetFolder);
}
}
}
Icon GetFolder(string keyExtension)
{
return GetFolderIconSTA(path, folderType, linkOverlay, size);
}
return icon;
}
public static Icon GetFolderIconSTA( public static Icon GetFolderIconSTA(
string directoryPath, string directoryPath,
FolderType folderType, FolderType folderType,
@ -108,13 +233,30 @@ namespace SystemTrayMenu.Utilities
IconSize size = IconSize.Small) IconSize size = IconSize.Small)
{ {
Icon icon = null; Icon icon = null;
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
{
icon = GetFolderIcon(
directoryPath,
folderType,
linkOverlay,
size);
}
else
{
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
void StaThreadMethod(object obj)
{
icon = GetFolderIcon(
directoryPath,
folderType,
linkOverlay,
size);
}
Task<Icon> task = Task.Factory.StartNew(() => GetFolderIcon( staThread.SetApartmentState(ApartmentState.STA);
directoryPath, staThread.Start(icon);
folderType, staThread.Join();
linkOverlay, }
size));
icon = task.Result;
return icon; return icon;
} }
@ -182,20 +324,28 @@ namespace SystemTrayMenu.Utilities
Icon icon = null; Icon icon = null;
if (originalIcon != null) if (originalIcon != null)
{ {
using Bitmap target = new Bitmap( using Bitmap target = new Bitmap(originalIcon.Width, originalIcon.Height, PixelFormat.Format32bppArgb);
originalIcon.Width, using Graphics graphics = Graphics.FromImage(target);
originalIcon.Height,
PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(target);
graphics.DrawIcon(originalIcon, 0, 0); graphics.DrawIcon(originalIcon, 0, 0);
graphics.DrawIcon(overlay, 0, 0); graphics.DrawIcon(overlay, 0, 0);
target.MakeTransparent(target.GetPixel(1, 1)); target.MakeTransparent(target.GetPixel(1, 1));
icon = Icon.FromHandle(target.GetHicon()); IntPtr hIcon = target.GetHicon();
icon = (Icon)Icon.FromHandle(hIcon).Clone();
DllImports.NativeMethods.User32DestroyIcon(hIcon);
} }
return icon; return icon;
} }
private static void ClearBadIcons()
{
IEnumerable<string> badKeysList = DictIconCache.Where(x => x.Value == null).Select(x => x.Key);
foreach (string badKey in badKeysList)
{
DictIconCache.Remove(badKey, out _);
}
}
private static bool IsExtensionWithSameIcon(string fileExtension) private static bool IsExtensionWithSameIcon(string fileExtension)
{ {
bool isExtensionWithSameIcon = true; bool isExtensionWithSameIcon = true;
@ -213,8 +363,22 @@ namespace SystemTrayMenu.Utilities
{ {
Icon icon = null; Icon icon = null;
Task<Icon> task = Task.Factory.StartNew(() => GetFileIcon(filePath, linkOverlay, size)); if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
icon = task.Result; {
icon = GetFileIcon(filePath, linkOverlay, size);
}
else
{
Thread staThread = new Thread(new ParameterizedThreadStart(StaThreadMethod));
void StaThreadMethod(object obj)
{
icon = GetFileIcon(filePath, linkOverlay, size);
}
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start(icon);
staThread.Join();
}
return icon; return icon;
} }