Skip to content

Commit

Permalink
Reduce CPU usage during KeePass startup, and fix original KeePass ico…
Browse files Browse the repository at this point in the history
…n appearing in the system tray in scenarios like canceling unlocking the DB and saving program options.
  • Loading branch information
Aldaviva committed Oct 24, 2023
1 parent 4a00e95 commit 5c4f76b
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 24 deletions.
4 changes: 2 additions & 2 deletions KeePassTrayIconLockState.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ VisualStudioVersion = 17.7.34018.315
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeePassTrayIconLockState", "KeePassTrayIconLockState\KeePassTrayIconLockState.csproj", "{E980ABDC-E56D-4E9C-A322-AFBE9D9092B4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Elevated", "Test.Elevated\Test.Elevated.csproj", "{F190E2D7-A3FE-4DE8-8CB0-339F41C4F599}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Elevated", "Test.Elevated\Test.Elevated.csproj", "{F190E2D7-A3FE-4DE8-8CB0-339F41C4F599}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{7A2AA320-04B9-4A1F-88BA-225849CCEE5A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{7A2AA320-04B9-4A1F-88BA-225849CCEE5A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
3 changes: 3 additions & 0 deletions KeePassTrayIconLockState/KeePassTrayIconLockState.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
<PackageReference Include="KoKo">
<Version>2.2.0</Version>
</PackageReference>
<PackageReference Include="Lib.Harmony">
<Version>2.2.2</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
Expand Down
43 changes: 32 additions & 11 deletions KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#nullable enable

using Dark.Net;
using HarmonyLib;
using KeePass.Forms;
using KeePass.Plugins;
using KeePass.Resources;
using KoKo.Property;
using System;
using System.Collections.Generic;
using System.Drawing;
Expand All @@ -8,20 +14,19 @@
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using Dark.Net;
using KeePass.Plugins;
using KeePass.Resources;
using KoKo.Property;

namespace KeePassTrayIconLockState;

// ReSharper disable once UnusedType.Global
public class KeePassTrayIconLockStateExt: Plugin {

internal static readonly TimeSpan STARTUP_DURATION = TimeSpan.FromMilliseconds(2000);
private static readonly TimeSpan FIXUP_INTERVAL = TimeSpan.FromSeconds(15);
private static readonly IDictionary<string, Icon?> EXTERNAL_ICON_CACHE = new Dictionary<string, Icon?>(6);
private static readonly string PLUGIN_INSTALLATION_DIRECTORY = getPluginInstallationDirectory();

private static KeePassTrayIconLockStateExt instance = null!;

private readonly StoredProperty<DatabaseOpenState> databaseOpenState = new();
private readonly IDarkNet darkNet = new DarkNet();

Expand All @@ -33,6 +38,7 @@ public class KeePassTrayIconLockStateExt: Plugin {
public override string UpdateUrl { get; } = @"https://raw.githubusercontent.com/Aldaviva/KeePassTrayIconLockState/master/KeePassTrayIconLockState/version.txt";

public override bool Initialize(IPluginHost host) {
instance = this;
keePassHost = host;

keePassHost.MainWindow.FileOpened += delegate { databaseOpenState.Value = DatabaseOpenState.OPEN; };
Expand All @@ -57,7 +63,7 @@ public override bool Initialize(IPluginHost host) {
if (!args.NewValue && databaseOpenState.Value == DatabaseOpenState.OPENING) {
/*
* When the database is being opened and the progress bar gets hidden, it means the database was finished being decrypted, but was it successful or unsuccessful?
* Sadly there is no good way to tell, so instead we wait 100 ms for the FileOpen event to be fired.
* Sadly there is no good way to tell, so instead we wait 100 ms for the FileOpened event to be fired.
* If it is fired, the db open state moves from OPENING to OPEN (above).
* Otherwise, assume that the database failed to decrypt and set the db open state to CLOSED.
*/
Expand All @@ -83,14 +89,10 @@ public override bool Initialize(IPluginHost host) {

trayIcon.PropertyChanged += delegate { renderTrayIcon(); };

/*
* KeePass sets its own icon at some indeterminate time after startup, so repeatedly set our own icon every 8 ms for 2 seconds to make sure our icon isn't overridden.
*/
Timer startupTimer = new() { Enabled = true, Interval = 8 };
startupTimer.Tick += delegate { renderTrayIcon(); };
Task.Delay(STARTUP_DURATION).ContinueWith(_ => startupTimer.Dispose());
renderTrayIcon();

hookKeepassTrayIconUpdates();

return true;
}

Expand Down Expand Up @@ -149,6 +151,25 @@ private static string getPluginInstallationDirectory() {
: Path.GetDirectoryName(executingDllPath)!; // KeePass scenario
}

/// <summary>
/// Use Harmony to modify the bytecode of KeePass' MainForm.UpdateTrayIcon(bool) method to call our own tray icon rendering method.
/// I do this because KeePass sometimes renders its tray icon without exposing any events to indicate that it's doing so, therefore I can't receive any notifications to trigger my own icon to render. Two examples of this are clicking OK in the Options dialog box and canceling the Unlock Database dialog box when minimize to tray, minimize after locking, and minimize after opening are all enabled.
/// Also, all of the types in KeePass are static, private, sealed, and don't implement interfaces, so there is no other way to receive notifications that it has rendered its tray icon.
/// </summary>
private static void hookKeepassTrayIconUpdates() {
Harmony harmony = new("com.aldaviva.keepasstrayiconlockstate");

MethodInfo originalUpdateTrayIconMethod = AccessTools.Method(typeof(MainForm), "UpdateTrayIcon", new[] { typeof(bool) }) ??
throw new MissingMethodException("Cannot find MainForm.UpdateTrayIcon(bool) method");

harmony.Patch(originalUpdateTrayIconMethod, postfix: new HarmonyMethod(AccessTools.Method(typeof(KeePassTrayIconLockStateExt), nameof(onUpdateTrayIcon))));
}

// ReSharper disable once UnusedMember.Local
private static void onUpdateTrayIcon() {
instance.renderTrayIcon();
}

public override void Terminate() {
darkNet.Dispose();
base.Terminate();
Expand Down
4 changes: 2 additions & 2 deletions KeePassTrayIconLockState/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

// Remember to also update version.txt when changing these version attributes so that online update checking works.
// https://keepass.info/help/v2_dev/plg_index.html#upd
[assembly: AssemblyVersion("1.2.1.0")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyVersion("1.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]

[assembly: InternalsVisibleTo("Test")]
[assembly: InternalsVisibleTo("Test.Elevated")]
2 changes: 1 addition & 1 deletion KeePassTrayIconLockState/version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:
KeePassTrayIconLockState:1.2.0
KeePassTrayIconLockState:1.3.0
:
8 changes: 4 additions & 4 deletions Test.Elevated/Test.Elevated.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.console" Version="2.5.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.console" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
8 changes: 4 additions & 4 deletions Test/Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.runner.console" Version="2.5.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.console" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down

0 comments on commit 5c4f76b

Please sign in to comment.