Add cross-client sync lock via shared directory

Verhindert, dass mehrere Arbeitsplaetze gleichzeitig dasselbe Starface-
Adressbuch synchronisieren (Dubletten/Lost-Updates bei echter Ueberlappung).

- Neues optionales Setting "Gemeinsames Verzeichnis" (UserSettings.SharedDirectory)
  in der Einstellungen-Maske inkl. Ordner-Browser.
- SyncLock: atomare Lock-Datei (FileMode.CreateNew) im gemeinsamen Verzeichnis,
  waehrend des Syncs offen gehalten -> bei Absturz gibt das OS das Handle frei
  und ein anderer Client uebernimmt die verwaiste Datei (Stale-Erkennung 15 Min,
  Loeschen scheitert am offenen Handle eines lebenden Halters).
- MainForm wartet vor dem Sync bis zu 2 Min auf eine freie Sperre, sonst wird
  der Lauf uebersprungen. Ohne/bei nicht erreichbarem Verzeichnis laeuft der
  Sync ohne diese Sperre weiter (lokaler Interlocked-Schutz bleibt).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 12:38:45 +02:00
parent d3fa452504
commit 212ced4c81
5 changed files with 233 additions and 5 deletions
+49 -5
View File
@@ -7,6 +7,8 @@ namespace StarfaceOutlookSync.UI
public class SettingsForm : Form
{
private CheckBox _chkStartMinimized, _chkSyncOnStart, _chkAutoAcceptOutlook;
private TextBox _txtSharedDir;
private Button _btnBrowseShared;
private Button _btnSave, _btnCancel;
private readonly UserSettings _settings;
@@ -54,35 +56,77 @@ namespace StarfaceOutlookSync.UI
var lblHint = new Label
{
Text = hintText,
Left = 38, Top = 102, Width = 310, Height = 36,
Left = 38, Top = 102, Width = 320, Height = 36,
ForeColor = UserSettings.IsOutlookSecurityLockedByPolicy() ? Color.OrangeRed : Color.Gray,
Font = new Font("Segoe UI", 8)
};
var lblShared = new Label
{
Text = "Gemeinsames Verzeichnis fuer Sync-Sperre (Mehrplatz, optional):",
Left = 20, Top = 150, AutoSize = true
};
_txtSharedDir = new TextBox
{
Left = 20, Top = 172, Width = 250,
Text = _settings.SharedDirectory
};
_btnBrowseShared = new Button
{
Text = "...", Left = 274, Top = 171, Width = 36, Height = 24
};
_btnBrowseShared.Click += (s, e) => BrowseSharedDir();
var lblSharedHint = new Label
{
Text = "Netzlaufwerk/UNC, das alle Arbeitsplaetze erreichen. Leer = keine\n" +
"clientuebergreifende Sperre (nur Schutz auf diesem PC).",
Left = 20, Top = 198, Width = 330, Height = 32,
ForeColor = Color.Gray, Font = new Font("Segoe UI", 8)
};
_btnSave = new Button
{
Text = "Speichern", Left = 95, Top = 170, Width = 85, Height = 28,
Text = "Speichern", Left = 95, Top = 240, Width = 85, Height = 28,
DialogResult = DialogResult.None
};
_btnSave.Click += (s, e) => Save();
_btnCancel = new Button
{
Text = "Abbrechen", Left = 189, Top = 170, Width = 85, Height = 28,
Text = "Abbrechen", Left = 189, Top = 240, Width = 85, Height = 28,
DialogResult = DialogResult.Cancel
};
Size = new Size(380, 260);
Controls.AddRange(new Control[] { _chkStartMinimized, _chkSyncOnStart, _chkAutoAcceptOutlook, lblHint, _btnSave, _btnCancel });
Size = new Size(380, 330);
Controls.AddRange(new Control[] { _chkStartMinimized, _chkSyncOnStart, _chkAutoAcceptOutlook, lblHint,
lblShared, _txtSharedDir, _btnBrowseShared, lblSharedHint, _btnSave, _btnCancel });
AcceptButton = _btnSave;
CancelButton = _btnCancel;
}
private void BrowseSharedDir()
{
using (var dlg = new FolderBrowserDialog())
{
dlg.Description = "Gemeinsames Verzeichnis fuer die Sync-Sperre waehlen";
if (!string.IsNullOrWhiteSpace(_txtSharedDir.Text))
{
try { dlg.SelectedPath = _txtSharedDir.Text; } catch { }
}
if (dlg.ShowDialog(this) == DialogResult.OK)
_txtSharedDir.Text = dlg.SelectedPath;
}
}
private void Save()
{
_settings.StartMinimized = _chkStartMinimized.Checked;
_settings.SyncOnStart = _chkSyncOnStart.Checked;
_settings.AutoAcceptOutlookPrompt = _chkAutoAcceptOutlook.Checked;
_settings.SharedDirectory = _txtSharedDir.Text.Trim();
_settings.Save();
DialogResult = DialogResult.OK;
Close();