mirror of
https://github.com/Hofknecht/SystemTrayMenu.git
synced 2024-09-30 09:06:32 +13:00
Improve Icon factory performance
Workers are by default part of MTA thread model and are CoInitialized. This means COM calls can be made without spawning additional STA threads. Additionally pass file icon factory for dictionary search instead of providing (potentially not used) new Icon beforehand.
This commit is contained in:
parent
9fd7da2e98
commit
045fb9dd7e
5 changed files with 73 additions and 94 deletions
|
@ -699,7 +699,7 @@ namespace SystemTrayMenu.Business
|
|||
|
||||
IconReader.RemoveIconFromCache(rowData.Path);
|
||||
rowDataRenamed.HiddenEntry = hasHiddenFlag;
|
||||
rowDataRenamed.ReadIcon(true);
|
||||
rowDataRenamed.LoadIcon();
|
||||
rowDatas.Add(rowDataRenamed);
|
||||
}
|
||||
else
|
||||
|
@ -765,7 +765,7 @@ namespace SystemTrayMenu.Business
|
|||
}
|
||||
|
||||
rowData.HiddenEntry = hasHiddenFlag;
|
||||
rowData.ReadIcon(true);
|
||||
rowData.LoadIcon();
|
||||
|
||||
var items = menu.GetDataGridView().Items;
|
||||
List<RowData> rowDatas = new(items.Count + 1) { rowData };
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace SystemTrayMenu
|
|||
public static class Config
|
||||
{
|
||||
private static readonly Icon SystemTrayMenu = new(Properties.Resources.SystemTrayMenu, (int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight);
|
||||
private static readonly Icon? IconRootFolder = GetIconSTA(Path, Path, false, IconSize.Small, true);
|
||||
private static readonly Icon? IconRootFolder = GetIcon(Path, Path, false, true, IconSize.Small);
|
||||
|
||||
private static bool readDarkModeDone;
|
||||
private static bool isDarkMode;
|
||||
|
|
|
@ -204,17 +204,17 @@ namespace SystemTrayMenu.DataClasses
|
|||
/// <param name="propertyName">Name of the changing property.</param>
|
||||
public void CallPropertyChanged([CallerMemberName] string? propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
internal void ReadIcon(bool updateIconInBackground)
|
||||
internal void LoadIcon()
|
||||
{
|
||||
bool cacheHit;
|
||||
|
||||
if (IsPointingToFolder)
|
||||
{
|
||||
cacheHit = IconReader.GetFolderIconWithCache(Path, ShowOverlay, updateIconInBackground, Level == 0, UpdateFinalIcon);
|
||||
cacheHit = IconReader.GetFolderIconWithCache(Path, ShowOverlay, Level == 0, UpdateFinalIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheHit = IconReader.GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, updateIconInBackground, Level == 0, UpdateFinalIcon);
|
||||
cacheHit = IconReader.GetFileIconWithCache(Path, ResolvedPath, ShowOverlay, Level == 0, UpdateFinalIcon);
|
||||
}
|
||||
|
||||
if (!cacheHit)
|
||||
|
|
|
@ -138,7 +138,7 @@ namespace SystemTrayMenu.Helpers
|
|||
rowData.HiddenEntry = hasHiddenFlag;
|
||||
}
|
||||
|
||||
rowData.ReadIcon(true);
|
||||
rowData.LoadIcon();
|
||||
}
|
||||
|
||||
menuData.RowDatas = menuData.RowDatas.Except(rowDatasToRemove).ToList();
|
||||
|
|
|
@ -58,7 +58,6 @@ namespace SystemTrayMenu.Utilities
|
|||
string path,
|
||||
string resolvedPath,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool checkPersistentFirst,
|
||||
Action<Icon?> onIconLoaded)
|
||||
{
|
||||
|
@ -79,22 +78,17 @@ namespace SystemTrayMenu.Utilities
|
|||
!DictIconCache(!checkPersistentFirst).TryGetValue(key, out icon))
|
||||
{
|
||||
cacheHit = false;
|
||||
if (updateIconInBackground)
|
||||
{
|
||||
IconSize size = IconSize.Small;
|
||||
if (Scaling.Factor >= 1.25f ||
|
||||
Scaling.FactorByDpi >= 1.25f ||
|
||||
Properties.Settings.Default.IconSizeInPercent / 100f >= 1.25f)
|
||||
{
|
||||
size = IconSize.Large;
|
||||
}
|
||||
|
||||
new Thread(UpdateIconInBackground).Start();
|
||||
void UpdateIconInBackground()
|
||||
{
|
||||
Icon? icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, GetIconSTA(path, resolvedPath, linkOverlay, size, false));
|
||||
onIconLoaded(icon);
|
||||
}
|
||||
new Thread(UpdateIconInBackground).Start();
|
||||
void UpdateIconInBackground()
|
||||
{
|
||||
Icon? icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, FactoryIconFile);
|
||||
onIconLoaded(icon);
|
||||
}
|
||||
|
||||
Icon? FactoryIconFile(string keyExtension)
|
||||
{
|
||||
return GetIcon(path, resolvedPath, linkOverlay, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -109,7 +103,6 @@ namespace SystemTrayMenu.Utilities
|
|||
public static bool GetFolderIconWithCache(
|
||||
string path,
|
||||
bool linkOverlay,
|
||||
bool updateIconInBackground,
|
||||
bool checkPersistentFirst,
|
||||
Action<Icon?> onIconLoaded)
|
||||
{
|
||||
|
@ -121,37 +114,25 @@ namespace SystemTrayMenu.Utilities
|
|||
{
|
||||
cacheHit = false;
|
||||
|
||||
if (updateIconInBackground)
|
||||
if (IsPreloading)
|
||||
{
|
||||
IconSize size = IconSize.Small;
|
||||
if (Scaling.Factor >= 1.25f ||
|
||||
Scaling.FactorByDpi >= 1.25f ||
|
||||
Properties.Settings.Default.IconSizeInPercent / 100f >= 1.25f)
|
||||
cacheHit = true;
|
||||
icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, FactoryIconFolder);
|
||||
onIconLoaded(icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
new Thread(UpdateIconInBackground).Start();
|
||||
void UpdateIconInBackground()
|
||||
{
|
||||
// IconSize.Large returns another folder icon than windows explorer
|
||||
size = IconSize.Large;
|
||||
}
|
||||
|
||||
if (IsPreloading)
|
||||
{
|
||||
cacheHit = true;
|
||||
icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, GetFolder);
|
||||
Icon? icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, FactoryIconFolder);
|
||||
onIconLoaded(icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
new Thread(UpdateIconInBackground).Start();
|
||||
void UpdateIconInBackground()
|
||||
{
|
||||
Icon? icon = DictIconCache(checkPersistentFirst).GetOrAdd(key, GetFolder);
|
||||
onIconLoaded(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Icon? GetFolder(string keyExtension)
|
||||
{
|
||||
return GetIconSTA(path, path, linkOverlay, size, true);
|
||||
}
|
||||
Icon? FactoryIconFolder(string keyExtension)
|
||||
{
|
||||
return GetIcon(path, path, linkOverlay, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -163,24 +144,46 @@ namespace SystemTrayMenu.Utilities
|
|||
return cacheHit;
|
||||
}
|
||||
|
||||
public static Icon? GetIconSTA(string path, string resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
public static Icon? GetIcon(string path, string resolvedPath, bool linkOverlay, bool isFolder, IconSize? forceSize = null)
|
||||
{
|
||||
Icon? icon = null;
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
IconSize size;
|
||||
if (forceSize.HasValue)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
size = forceSize.Value;
|
||||
}
|
||||
else if (Scaling.Factor >= 1.25f ||
|
||||
Scaling.FactorByDpi >= 1.25f ||
|
||||
Properties.Settings.Default.IconSizeInPercent / 100f >= 1.25f)
|
||||
{
|
||||
// IconSize.Large returns another folder icon than windows explorer
|
||||
size = IconSize.Large;
|
||||
}
|
||||
else
|
||||
{
|
||||
Thread staThread = new(new ParameterizedThreadStart(StaThreadMethod));
|
||||
void StaThreadMethod(object? obj)
|
||||
{
|
||||
icon = GetIcon(path, resolvedPath, linkOverlay, size, isFolder);
|
||||
}
|
||||
size = IconSize.Small;
|
||||
}
|
||||
|
||||
staThread.SetApartmentState(ApartmentState.STA);
|
||||
staThread.Start(icon);
|
||||
staThread.Join();
|
||||
Icon? icon;
|
||||
if (Path.GetExtension(path).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(path);
|
||||
}
|
||||
else if (File.Exists(resolvedPath) &&
|
||||
Path.GetExtension(resolvedPath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(resolvedPath);
|
||||
if (linkOverlay && icon != null)
|
||||
{
|
||||
icon = AddIconOverlay(icon, Properties.Resources.LinkArrow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeMethods.SHFILEINFO shFileInfo = default;
|
||||
uint flags = GetFlags(linkOverlay, size);
|
||||
uint attribute = isFolder ? NativeMethods.FileAttributeDirectory : NativeMethods.FileAttributeNormal;
|
||||
IntPtr imageList = NativeMethods.Shell32SHGetFileInfo(path, attribute, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), flags);
|
||||
icon = GetIcon(path, linkOverlay, shFileInfo, imageList);
|
||||
}
|
||||
|
||||
return icon;
|
||||
|
@ -214,35 +217,6 @@ namespace SystemTrayMenu.Utilities
|
|||
return isExtensionWithSameIcon;
|
||||
}
|
||||
|
||||
private static Icon? GetIcon(string path, string resolvedPath, bool linkOverlay, IconSize size, bool isFolder)
|
||||
{
|
||||
Icon? icon;
|
||||
if (Path.GetExtension(path).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(path);
|
||||
}
|
||||
else if (File.Exists(resolvedPath) &&
|
||||
Path.GetExtension(resolvedPath).Equals(".ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
icon = Icon.ExtractAssociatedIcon(resolvedPath);
|
||||
if (linkOverlay && icon != null)
|
||||
{
|
||||
icon = AddIconOverlay(icon, Properties.Resources.LinkArrow);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeMethods.SHFILEINFO shFileInfo = default;
|
||||
uint flags = GetFlags(linkOverlay, size);
|
||||
uint attribute = isFolder ? NativeMethods.FileAttributeDirectory : NativeMethods.FileAttributeNormal;
|
||||
IntPtr imageList = NativeMethods.Shell32SHGetFileInfo(
|
||||
path, attribute, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), flags);
|
||||
icon = GetIcon(path, linkOverlay, shFileInfo, imageList);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static uint GetFlags(bool linkOverlay, IconSize size)
|
||||
{
|
||||
uint flags = NativeMethods.ShgfiIcon | NativeMethods.ShgfiSYSICONINDEX;
|
||||
|
@ -276,12 +250,17 @@ namespace SystemTrayMenu.Utilities
|
|||
}
|
||||
else
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(
|
||||
imageList, shFileInfo.iIcon, NativeMethods.IldTransparent);
|
||||
hIcon = NativeMethods.ImageList_GetIcon(imageList, shFileInfo.iIcon, NativeMethods.IldTransparent);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Note: Destroying hIcon after FromHandle will invalidate the Icon, so we do NOT destroy it, despite the request from documentation:
|
||||
// https://learn.microsoft.com/en-us/dotnet/api/system.drawing.icon.fromhandle?view=dotnet-plat-ext-7.0
|
||||
// Reason is https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Icon.cs,555 (FromHandle)
|
||||
// It is not taking over the ownership, data will be deleted upon destroying the original icon, so a clone is required.
|
||||
// With Clone we actually get a new handle, so we can free up the original handle without killing our copy.
|
||||
// Using Clone will also restore the ownership of the new icon handle, so we do not have to call DestroyIcon on it by ourself.
|
||||
icon = (Icon)Icon.FromHandle(hIcon).Clone();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -300,4 +279,4 @@ namespace SystemTrayMenu.Utilities
|
|||
return icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue