diff --git a/KeePassTrayIconLockState.sln b/KeePassTrayIconLockState.sln index e15f141..236c3b1 100644 --- a/KeePassTrayIconLockState.sln +++ b/KeePassTrayIconLockState.sln @@ -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 diff --git a/KeePassTrayIconLockState/KeePassTrayIconLockState.csproj b/KeePassTrayIconLockState/KeePassTrayIconLockState.csproj index aabc8a2..31b7ac7 100644 --- a/KeePassTrayIconLockState/KeePassTrayIconLockState.csproj +++ b/KeePassTrayIconLockState/KeePassTrayIconLockState.csproj @@ -81,6 +81,9 @@ 2.2.0 + + 2.2.2 + diff --git a/KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs b/KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs index 5af60aa..afe3884 100644 --- a/KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs +++ b/KeePassTrayIconLockState/KeePassTrayIconLockStateExt.cs @@ -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; @@ -8,10 +14,6 @@ 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; @@ -19,9 +21,12 @@ namespace KeePassTrayIconLockState; 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 EXTERNAL_ICON_CACHE = new Dictionary(6); private static readonly string PLUGIN_INSTALLATION_DIRECTORY = getPluginInstallationDirectory(); + private static KeePassTrayIconLockStateExt instance = null!; + private readonly StoredProperty databaseOpenState = new(); private readonly IDarkNet darkNet = new DarkNet(); @@ -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; }; @@ -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. */ @@ -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; } @@ -149,6 +151,25 @@ private static string getPluginInstallationDirectory() { : Path.GetDirectoryName(executingDllPath)!; // KeePass scenario } + /// + /// 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. + /// + 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(); diff --git a/KeePassTrayIconLockState/Properties/AssemblyInfo.cs b/KeePassTrayIconLockState/Properties/AssemblyInfo.cs index 7ff0399..42916ad 100644 --- a/KeePassTrayIconLockState/Properties/AssemblyInfo.cs +++ b/KeePassTrayIconLockState/Properties/AssemblyInfo.cs @@ -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")] \ No newline at end of file diff --git a/KeePassTrayIconLockState/version.txt b/KeePassTrayIconLockState/version.txt index 14ac272..876e45d 100644 --- a/KeePassTrayIconLockState/version.txt +++ b/KeePassTrayIconLockState/version.txt @@ -1,3 +1,3 @@ : -KeePassTrayIconLockState:1.2.0 +KeePassTrayIconLockState:1.3.0 : \ No newline at end of file diff --git a/Test.Elevated/Test.Elevated.csproj b/Test.Elevated/Test.Elevated.csproj index 20e5df0..a9f8002 100644 --- a/Test.Elevated/Test.Elevated.csproj +++ b/Test.Elevated/Test.Elevated.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Test/Test.csproj b/Test/Test.csproj index f4f5a8b..07f3c0e 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -11,13 +11,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all