Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/Core/Localizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static class Localizer

private static CultureInfo _culture;
private static Language _language;
private static List<Language> _languages;

#endregion

Expand Down Expand Up @@ -118,6 +119,9 @@ public static List<Language> Languages
{
get
{
if (_languages != null)
return _languages;

try
{
var resourceNames = Assembly.GetExecutingAssembly().GetManifestResourceNames()
Expand All @@ -140,7 +144,7 @@ public static List<Language> Languages
// ignored
}

return CultureInfo.GetCultures(CultureTypes.AllCultures)
_languages = CultureInfo.GetCultures(CultureTypes.AllCultures)
.Where(culture => resourceNames.Contains(culture.EnglishName, StringComparer.OrdinalIgnoreCase))
.OrderBy(culture => culture.EnglishName, StringComparer.InvariantCultureIgnoreCase)
.Select(culture => new Language(culture))
Expand All @@ -150,8 +154,10 @@ public static List<Language> Languages
{
Logger.Error(e);

return new List<Language> { new Language(new CultureInfo(Constants.Windows.Locale.Name.English)) };
_languages = new List<Language> { new Language(new CultureInfo(Constants.Windows.Locale.Name.English)) };
}

return _languages;
}
}

Expand Down
1 change: 0 additions & 1 deletion src/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public static class Settings
static Settings()
{
Load();
Save();
}

#endregion
Expand Down
22 changes: 8 additions & 14 deletions src/Service/ComputerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ private void OptimizeCombinedPageList()
if (!SetIncreasePrivilege(Constants.Windows.Privilege.SeProfSingleProcessName))
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorAdminPrivilegeRequired, Constants.Windows.Privilege.SeProfSingleProcessName));

var handle = GCHandle.Alloc(0);
var handle = default(GCHandle);

try
{
Expand Down Expand Up @@ -634,25 +634,19 @@ private void OptimizeStandbyList(bool lowPriority = false)
if (!SetIncreasePrivilege(Constants.Windows.Privilege.SeProfSingleProcessName))
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorAdminPrivilegeRequired, Constants.Windows.Privilege.SeProfSingleProcessName));

object memoryPurgeStandbyList = lowPriority ? Constants.Windows.SystemMemoryListCommand.MemoryPurgeLowPriorityStandbyList : Constants.Windows.SystemMemoryListCommand.MemoryPurgeStandbyList;
var handle = GCHandle.Alloc(memoryPurgeStandbyList, GCHandleType.Pinned);
var memoryPurgeStandbyList = lowPriority ? Constants.Windows.SystemMemoryListCommand.MemoryPurgeLowPriorityStandbyList : Constants.Windows.SystemMemoryListCommand.MemoryPurgeStandbyList;
var buffer = Marshal.AllocHGlobal(sizeof(int));

try
{
if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, handle.AddrOfPinnedObject(), (uint)Marshal.SizeOf(memoryPurgeStandbyList)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
Marshal.WriteInt32(buffer, (int)memoryPurgeStandbyList);

if (NativeMethods.NtSetSystemInformation(Constants.Windows.SystemInformationClass.SystemMemoryListInformation, buffer, (uint)sizeof(int)) != Constants.Windows.SystemErrorCode.ErrorSuccess)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
try
{
if (handle.IsAllocated)
handle.Free();
}
catch (InvalidOperationException)
{
// ignored
}
Marshal.FreeHGlobal(buffer);
}
}

Expand All @@ -669,7 +663,7 @@ private void OptimizeSystemFileCache()
if (!SetIncreasePrivilege(Constants.Windows.Privilege.SeIncreaseQuotaName))
throw new Exception(string.Format(Localizer.Culture, Localizer.String.ErrorAdminPrivilegeRequired, Constants.Windows.Privilege.SeIncreaseQuotaName));

var handle = GCHandle.Alloc(0);
var handle = default(GCHandle);

try
{
Expand Down
32 changes: 25 additions & 7 deletions src/Service/NotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ public class NotificationService : INotificationService
private int _currentRotationAngle;
private Icon _currentIcon;
private bool _disposed;
private readonly Font _cachedFont;
private readonly Icon _imageIcon;
private readonly NotifyIcon _notifyIcon;
private readonly object _disposeLock = new object();
private DispatcherTimer _rotationTimer;
private readonly StringFormat _cachedStringFormat;

#endregion

Expand All @@ -38,6 +40,8 @@ public class NotificationService : INotificationService
public NotificationService(NotifyIcon notifyIcon)
{
_currentRotationAngle = 0;
_cachedFont = new Font("Consolas", 14F, FontStyle.Regular, GraphicsUnit.Pixel);
_cachedStringFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
_imageIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
_notifyIcon = notifyIcon;

Expand Down Expand Up @@ -156,6 +160,26 @@ protected virtual void Dispose(bool disposing)
Logger.Debug(ex);
}

try
{
if (_cachedFont != null)
_cachedFont.Dispose();
}
catch (Exception ex)
{
Logger.Debug(ex);
}

try
{
if (_cachedStringFormat != null)
_cachedStringFormat.Dispose();
}
catch (Exception ex)
{
Logger.Debug(ex);
}

try
{
if (_notifyIcon != null)
Expand Down Expand Up @@ -293,15 +317,9 @@ private Icon GetMemoryUsageIcon(Memory memory, bool isOptimizing)
{
using (var image = new Bitmap(16, 16))
using (var graphics = Graphics.FromImage(image))
using (var font = new Font("Consolas", 14F, FontStyle.Regular, GraphicsUnit.Pixel))
using (var format = new StringFormat())
using (var backgroundBrush = GetBackgroundBrush(memory, isOptimizing))
using (var textBrush = GetTextBrush(memory, isOptimizing))
{
// Configure format
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;

// Configure graphics quality
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
Expand All @@ -324,7 +342,7 @@ private Icon GetMemoryUsageIcon(Memory memory, bool isOptimizing)
}

// Draw text
graphics.DrawString(string.Format(CultureInfo.InvariantCulture, "{0:00}", memory.Physical.Used.Percentage == 100 ? 99 : memory.Physical.Used.Percentage), font, textBrush, 8F, 9F, format);
graphics.DrawString(string.Format(CultureInfo.InvariantCulture, "{0:00}", memory.Physical.Used.Percentage == 100 ? 99 : memory.Physical.Used.Percentage), _cachedFont, textBrush, 8F, 9F, _cachedStringFormat);

var handle = image.GetHicon();

Expand Down
123 changes: 90 additions & 33 deletions src/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
Expand All @@ -20,6 +19,7 @@ public class MainViewModel : ViewModel, IDisposable
#region Fields

private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private ObservableCollection<SolidColorBrush> _cachedBrushes;
private Computer _computer;
private readonly IComputerService _computerService;
private readonly IHotkeyService _hotKeyService;
Expand Down Expand Up @@ -250,7 +250,10 @@ public ObservableCollection<SolidColorBrush> Brushes
{
get
{
return new ObservableCollection<SolidColorBrush>(App.IsInDesignMode ? new List<SolidColorBrush> { System.Windows.Media.Brushes.White } : ThemeManager.Brushes);
if (_cachedBrushes == null)
_cachedBrushes = new ObservableCollection<SolidColorBrush>(App.IsInDesignMode ? new List<SolidColorBrush> { System.Windows.Media.Brushes.White } : ThemeManager.Brushes);

return _cachedBrushes;
}
}

Expand Down Expand Up @@ -543,6 +546,7 @@ public Language Language
RaisePropertyChanged(() => Computer);

_trayIconItems = null;
_cachedBrushes = null;

NotificationService.Initialize();
NotificationService.Update(Computer.Memory, IsOptimizationRunning);
Expand Down Expand Up @@ -818,16 +822,32 @@ public ObservableCollection<string> Processes
{
get
{
var processes = new ObservableCollection<string>(Process.GetProcesses()
.Where(process => process != null && !process.ProcessName.Equals(Constants.App.Name) && !Settings.ProcessExclusionList.Contains(process.ProcessName, StringComparer.OrdinalIgnoreCase))
.Select(process => process.ProcessName.ToLower(Localizer.Culture).Replace(".exe", string.Empty))
.Distinct()
.OrderBy(name => name));
var allProcesses = Process.GetProcesses();

try
{
var names = allProcesses
.Where(process => process != null && !process.ProcessName.Equals(Constants.App.Name) && !Settings.ProcessExclusionList.Contains(process.ProcessName))
.Select(process => process.ProcessName.ToLower(Localizer.Culture).Replace(".exe", string.Empty))
.Distinct()
.OrderBy(name => name)
.ToList();

var processes = new ObservableCollection<string>(names);

if (!processes.Contains(SelectedProcess, StringComparer.OrdinalIgnoreCase))
SelectedProcess = processes.FirstOrDefault();
if (!processes.Contains(SelectedProcess, StringComparer.OrdinalIgnoreCase))
SelectedProcess = processes.FirstOrDefault();

return processes;
return processes;
}
finally
{
foreach (var process in allProcesses)
{
if (process != null)
process.Dispose();
}
}
}
}

Expand Down Expand Up @@ -1619,7 +1639,12 @@ private void MonitorApp()
{
// Check if it's busy
if (IsBusy)
{
if (_cancellationTokenSource.Token.WaitHandle.WaitOne(1000))
break;

continue;
}

// Delay
if (_cancellationTokenSource.Token.WaitHandle.WaitOne(60000))
Expand Down Expand Up @@ -1706,7 +1731,12 @@ private void MonitorComputer()
{
// Check if it's busy
if (IsBusy)
{
if (_cancellationTokenSource.Token.WaitHandle.WaitOne(1000))
break;

continue;
}

lock (_lockObject)
{
Expand Down Expand Up @@ -1748,9 +1778,12 @@ private void OnOptimizeProgressUpdate(byte value, string step)
/// <param name="reason">Optimization reason</param>
private void Optimize(Enums.Memory.Optimization.Reason reason)
{
lock (_lockObject)
try
{
try
long tempPhysicalAvailable;
long tempVirtualAvailable;

lock (_lockObject)
{
IsBusy = true;
IsOptimizationRunning = true;
Expand All @@ -1761,11 +1794,14 @@ private void Optimize(Enums.Memory.Optimization.Reason reason)
App.SetPriority(Settings.RunOnPriority);

// Memory optimize
var tempPhysicalAvailable = Computer.Memory.Physical.Free.Bytes;
var tempVirtualAvailable = Computer.Memory.Virtual.Free.Bytes;
tempPhysicalAvailable = Computer.Memory.Physical.Free.Bytes;
tempVirtualAvailable = Computer.Memory.Virtual.Free.Bytes;
}

_computerService.Optimize(reason, Settings.MemoryAreas);
_computerService.Optimize(reason, Settings.MemoryAreas);

lock (_lockObject)
{
// Update memory info
Computer.Memory = _computerService.Memory;
RaisePropertyChanged(() => Computer);
Expand All @@ -1783,29 +1819,32 @@ private void Optimize(Enums.Memory.Optimization.Reason reason)
Notify(message);
}
}
finally
}
finally
{
lock (_lockObject)
{
IsOptimizationRunning = false;
IsBusy = false;

NotificationService.Update(Computer.Memory, IsOptimizationRunning);
}

// Raise the event after IsOptimizationRunning is set to false
// Use BeginInvoke to ensure it runs after all property changes propagate
WpfApplication.Current.Dispatcher.BeginInvoke((Action)(() =>
{
// Force command manager to re-evaluate CanExecute on all commands
CommandManager.InvalidateRequerySuggested();
}), System.Windows.Threading.DispatcherPriority.Normal);
// Raise the event after IsOptimizationRunning is set to false
// Use BeginInvoke to ensure it runs after all property changes propagate
WpfApplication.Current.Dispatcher.BeginInvoke((Action)(() =>
{
// Force command manager to re-evaluate CanExecute on all commands
CommandManager.InvalidateRequerySuggested();
}), System.Windows.Threading.DispatcherPriority.Normal);

// Raise completion event with lower priority to ensure commands are refreshed first
if (OnOptimizeCommandCompleted != null)
{
WpfApplication.Current.Dispatcher.BeginInvoke((Action)(() =>
{
OnOptimizeCommandCompleted();
}), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
}
// Raise completion event with lower priority to ensure commands are refreshed first
if (OnOptimizeCommandCompleted != null)
{
WpfApplication.Current.Dispatcher.BeginInvoke((Action)(() =>
{
OnOptimizeCommandCompleted();
}), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
}
}
}
Expand All @@ -1818,12 +1857,12 @@ private void OptimizeAsync(Enums.Memory.Optimization.Reason reason)
{
try
{
if (IsOptimizationRunning)
if (_isOptimizationRunning)
return;

OptimizationProgressStep = Localizer.String.Optimize;
OptimizationProgressValue = 0;
OptimizationProgressTotal = (byte)(new BitArray(new[] { (int)Settings.MemoryAreas }).OfType<bool>().Count(x => x) + 1);
OptimizationProgressTotal = (byte)(CountSetBits((int)Settings.MemoryAreas) + 1);

ThreadPool.QueueUserWorkItem(_ => Optimize(reason));
}
Expand All @@ -1833,6 +1872,24 @@ private void OptimizeAsync(Enums.Memory.Optimization.Reason reason)
}
}

/// <summary>
/// Counts the number of set bits (1s) in the binary representation of the value.
/// </summary>
/// <param name="value">The value to count bits in.</param>
/// <returns>The number of set bits.</returns>
private static int CountSetBits(int value)
{
int count = 0;

while (value != 0)
{
count++;
value &= value - 1;
}

return count;
}

/// <summary>
/// Registers the optimization hotkey.
/// </summary>
Expand Down
Loading
Loading