Bump to v3.1.3: single-instance lock + update/read-only reliability fixes
Fixes the chained-fault runaway Andre hit after an update (multiple copies
stacking, audio climbing to deafening, terminal kill to recover).
- Single-instance lock (SingleInstanceCoordinator + SingleInstanceDialog,
wired in Program.Main). A named mutex makes two copies impossible. A second
launch offers: switch to the running copy (default; surfaces it from the
tray via a named activation event), or force the running copy closed and
start fresh (Process.Kill, retried elevated if the target is elevated).
This is the structural fix that makes the stacking runaway impossible.
- Prompt-free update exit. InstallUpdateAsync sets updatingInProgress before
Application.Exit(); the close path's skipPrompt now honours it, so no
unsaved-changes dialog (whose default button is Cancel) can abort the
update's restart.
- Read-only persistence fix. BuildCurrentProfile now carries
currentProfileReadOnly into the saved snapshot. Previously a deliberate
save of a locked profile wrote ReadOnly=false, silently unlocking it on
disk — which re-armed the save prompt that then blocked the update.
- In-process double-install guard. updateInstallStarted stops the ~4 s
startup check and the background poll both staging an install + helper.
- Docs: About box, RELEASE_NOTES.md, readme.html + MANUAL.md ("Only one copy
of RemSound runs at a time").
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
baf50baf73
commit
9ab0a1a20a
@@ -0,0 +1,56 @@
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace RemSound.App;
|
||||
|
||||
internal enum SingleInstanceDecision
|
||||
{
|
||||
/// <summary>Bring the already-running copy to the front; this copy exits.</summary>
|
||||
SwitchToRunning,
|
||||
|
||||
/// <summary>Force the running copy to close, then start fresh.</summary>
|
||||
ForceClose,
|
||||
|
||||
/// <summary>Do nothing; this copy exits.</summary>
|
||||
Cancel,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shown when a SECOND copy of RemSound is launched while one is already running. Offers three
|
||||
/// choices, with the safe one (switch to the running copy) as the default so a habitual Enter
|
||||
/// can't accidentally kill a healthy session that might be mid-recording. The force-close
|
||||
/// option is the deliberate recovery path for a stuck copy (Andre's "make it go away"). Built
|
||||
/// as a native TaskDialog to match the rest of RemSound's dialogs and because NVDA reads its
|
||||
/// heading, body and buttons cleanly. No owner window — the main window doesn't exist yet.
|
||||
/// </summary>
|
||||
internal static class SingleInstanceDialog
|
||||
{
|
||||
public static SingleInstanceDecision Ask()
|
||||
{
|
||||
var switchButton = new TaskDialogButton("Switch to the running copy");
|
||||
var forceButton = new TaskDialogButton("Force the running copy to close and start fresh");
|
||||
var cancelButton = new TaskDialogButton("Cancel") { AllowCloseDialog = true };
|
||||
|
||||
var page = new TaskDialogPage
|
||||
{
|
||||
Caption = "RemSound is already running",
|
||||
Heading = "RemSound is already running",
|
||||
Text =
|
||||
"Only one copy of RemSound can run at a time. What would you like to do?\n\n"
|
||||
+ "• Switch to the running copy — bring the copy that's already running back to the "
|
||||
+ "front. It may be tucked away in the system tray, down by the clock.\n\n"
|
||||
+ "• Force the running copy to close and start fresh — use this only if the running "
|
||||
+ "copy is stuck or not responding. If it's in the middle of a recording, that "
|
||||
+ "recording will be lost.\n\n"
|
||||
+ "• Cancel — do nothing.",
|
||||
Icon = TaskDialogIcon.Warning,
|
||||
Buttons = { switchButton, forceButton, cancelButton },
|
||||
DefaultButton = switchButton,
|
||||
AllowCancel = true,
|
||||
};
|
||||
|
||||
var clicked = TaskDialog.ShowDialog(page);
|
||||
if (clicked == forceButton) return SingleInstanceDecision.ForceClose;
|
||||
if (clicked == switchButton) return SingleInstanceDecision.SwitchToRunning;
|
||||
return SingleInstanceDecision.Cancel;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user