Wurde das Dubletten-Fenster geschlossen, waehrend Analyse/Zusammenfuehrung
noch lief, griff die Hintergrund-Fortsetzung auf das entsorgte Textfeld zu
-> ObjectDisposedException im async-void Handler -> App-Absturz.
- AppendLog ist jetzt disposed-sicher (IsDisposed/Disposing-Check, BeginInvoke,
ObjectDisposedException/InvalidOperationException abgefangen).
- OnFormClosing verhindert das Schliessen, solange _busy (Analyse/Merge laeuft),
sodass keine Fortsetzung auf entsorgte Controls trifft.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nach der Vorschau blieben Dubletten teils stehen. Ursachen abgesichert:
- Outlook- und Anlagen-Seite laufen jetzt in getrennten try-Bloecken. Vorher
brach ein Fehler beim Laden/Verarbeiten der Anlage die ganze Methode ab,
bevor die Outlook-Seite ueberhaupt bereinigt wurde.
- RunExclusiveAsync wartet bis zu 60s auf einen laufenden Sync, statt sofort
abzubrechen (Auto-Sync feuert jede Minute -> haeufige Kollision).
- Fehlgeschlagene Loeschungen werden jetzt als Fehler gemeldet (vorher still
verschluckt), plus Gruppen-Zaehler ins Log.
- DedupeForm zeigt einen deutlichen Hinweis (MessageBox), wenn wegen eines
laufenden Syncs nicht ausgefuehrt wurde, und laesst einen erneuten Versuch zu.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beim Tray-/Auto-Sync lief die Engine auf dem UI-Thread -> Fenster und
Tray-Kontextmenue froren ein (Menuepunkte reagierten nicht).
- MainForm.RunSync fuehrt die Engine jetzt via Task.Run im Hintergrund aus
(wie schon der manuelle Fenster-Sync).
- Balloon() marshalled Tray-Meldungen thread-sicher per BeginInvoke auf den
UI-Thread (die Fortsetzung kann nach Task.Run aus einem Pool-Thread kommen).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neuer Button "Dubletten" im Hauptfenster fuehrt doppelte Kontakte fuer das
ausgewaehlte Profil zusammen - je Seite (Telefonanlage + Outlook) getrennt.
- Matching in ContactMatcher extrahiert (Sync UND Dedupe nutzen exakt dieselbe
Erkennung).
- ContactMerger.FillEmptyInto / FilledFieldCount: leere Felder des Gewinners
auffuellen (kein Datenverlust), Gewinner-Wahl nach Zuordnung/Vollstaendigkeit.
- DedupeService: Analyse (Vorschau) + Ausfuehrung (Merge, Loeschen der
Dubletten, Bereinigen verwaister Zuordnungen).
- SyncCoordinator.RunExclusiveAsync: Dedupe laeuft unter demselben Guard +
Lock-Datei wie ein Sync (nie gleichzeitig; kein Konflikt zwischen Clients).
- DedupeForm: Vorschau mit optionaler Detailliste, Bestaetigung, Live-Log;
danach Hinweis, einmal zu synchronisieren.
- MainForm: Button "Dubletten" (Profil-Auswahl noetig), Fenster verbreitert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Die SyncEngine kennt jetzt nur noch IContactBackend. Was dahinter steckt,
entscheidet das Profil — der Ablauf mit getrennten Baselines, Wiederzuordnung
und Konfliktaufloesung bleibt unangetastet. In ihm steckt die Erfahrung, die
man nicht zweimal machen will.
FonariaApiClient spricht deren REST-API. Der Kern ist das Feld `verwaltet`,
das jeder Schreibvorgang mitschickt: Fonaria haelt den Kontakt als
vollstaendige vCard, dieses Sync-Modell bildet davon nur einen Ausschnitt ab.
Ohne diese Liste haette ein aus Outlook gespiegelter Kontakt nach dem ersten
Abgleich kein Bild, keine Kategorien und keinen Jahrestag mehr — nicht weil
jemand sie geloescht haette, sondern weil wir sie nicht kannten.
Zwei Verluste lagen darunter und sind gefunden worden, weil der Client gegen
eine echte Anlage lief:
- Die Abteilung steckt im zweiten Teil von ORG. Wer nur die Firma schickt,
loescht sie mit.
- Neben der geschaeftlichen Anschrift kann eine private stehen. Das
Sync-Modell traegt nur eine.
Beides wird jetzt aus dem gelesenen Stand unveraendert zurueckgeschrieben.
Umbenannt: StarfaceConnection -> SystemConnection, StarfaceAddressBook ->
RemoteAddressBook. Die JSON-Namen bleiben ueber JsonProperty erhalten, sonst
verloeren bestehende Profile ihre Verbindungsdaten. SyncMapping.StarfaceId
behaelt seinen Namen aus demselben Grund.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setup setzt den Autostart weiterhin fuer alle Benutzer (HKLM); die
Task-Beschreibung im Installer stellt das jetzt klar.
In den Einstellungen neuer Abschnitt "Autostart":
- "Nur fuer diesen Benutzer" -> HKCU\...\Run
- "Fuer alle Benutzer" -> HKLM\...\Run, nur mit Admin-Rechten aenderbar
(so laesst sich der vom Setup gesetzte All-User-Autostart auch entfernen).
Neuer AutostartManager kapselt Lesen/Setzen/Entfernen beider Run-Eintraege
und die Admin-Pruefung. Single-Instance-Mutex verhindert weiterhin einen
Doppelstart, falls beide Eintraege gesetzt sind.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neue Einstellung "Protokoll auto-leeren - Eintraege aelter als (Tage)".
0 = aus (alle Eintraege bleiben), >0 entfernt aeltere Eintraege.
- UserSettings.LogRetentionDays (Standard 0).
- Logger.PruneOlderThan(days): parst den Zeitstempel-Prefix je Zeile und
entfernt zu alte; Zeilen ohne Zeitstempel bleiben erhalten.
- Ausgefuehrt beim Start, vor jedem Sync (Coordinator), beim Oeffnen des
Protokolls und beim Speichern der Einstellungen.
- SettingsForm: NumericUpDown (0-3650).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der manuelle "Synchronisieren"-Button (SyncProgressForm) lief ueber eine eigene
Engine und umging die clientuebergreifende Lock-Datei, den lokalen Guard und
einen Teil des Protokolls. Jetzt teilen sich beide Pfade einen SyncCoordinator.
- Neuer SyncCoordinator kapselt: prozessweiten Re-Entrancy-Guard (statisch, so
koennen manueller + automatischer Sync nicht mehr gleichzeitig laufen),
Lock-Datei (mit Warte-Status), Konflikt-Notizen anderer Arbeitsplaetze,
Protokoll (Start/Ergebnis/Aenderungen/Konflikte/Fehler) und das Verteilen
eigener Konflikt-Notizen. runEngine wird vom Aufrufer uebergeben, damit das
Threading pro Pfad erhalten bleibt (UI-Thread vs. Task.Run).
- MainForm.RunSync und SyncProgressForm.RunSync nutzen den Coordinator; UI
(Tray-Meldung vs. Fenster) bleibt jeweils beim Aufrufer.
- Lock/Notiz/Guard-Logik aus MainForm entfernt (jetzt zentral).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Protokoll:
- SyncResult.Changes erfasst jede tatsaechliche Aenderung (erstellt/aktualisiert/
geloescht/verknuepft/zusammengefuehrt je Kontakt) per Action()-Helfer.
- Beide Sync-Pfade (Auto/Tray via RunSync UND manueller Sync via
SyncProgressForm) schreiben Start, Ergebnis, Aenderungen, Konflikte und Fehler
ins persistente Protokoll.
Benachrichtigungen:
- UserSettings: NotificationsEnabled (allgemein) + NotifyWarningsErrors
(Konflikte/Fehler), beide in der Einstellungen-Maske als Haken.
- MainForm.Balloon() zeigt Tray-Meldungen nur, wenn der passende Haken aktiv
ist; Zusammenfassung gilt als Warnung, wenn Fehler/Konflikte auftraten.
- Protokoll wird unabhaengig von den Benachrichtigungs-Einstellungen geschrieben.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tray-Meldungen verschwinden - daher ein dauerhaftes Protokoll.
- Logger: threadsicheres Datei-Log in %AppData%\StarfaceOutlookSync\sync.log
mit Rotation bei 2 MB.
- MainForm protokolliert Sync-Start, Ergebnis, Konflikte (lokal + von anderen
Arbeitsplaetzen), Fehler und uebersprungene Laeufe (Sperre).
- Neuer Button "Protokoll" oeffnet LogViewerForm (Aktualisieren/Leeren/Ordner
oeffnen, scrollt ans Ende).
- README/CHANGELOG aktualisiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bisher bekam jeder Arbeitsplatz, der den Konflikt-Kontakt im Adressbuch hat,
den Hinweis - auch wenn er den Wert gar nicht selbst gepflegt hat.
Jetzt zeigt ein Client eine fremde Konflikt-Notiz nur, wenn er den Kontakt hat
UND sein eigener Feldwert vom uebernommenen (Gewinner-)Wert abweicht. Die
Pruefung laeuft VOR dem Sync (gegen den eigenen Mapping-Snapshot), bevor der
Sync den Stand auf den Gewinner-Wert angleicht.
- FieldConflict/ConflictNotice: stabiler FieldKey zusaetzlich zum Anzeige-Label.
- ContactMerger: GetValue/ValuesEqual per FieldKey (telefon-normalisiert).
- ConflictNotifier.GetPending: Filter "eigener Wert != Gewinner-Wert", bekommt
StarfaceId -> eigener Kontaktstand.
- MainForm zeigt die Hinweise jetzt vor dem Sync und liefert den eigenen Stand
aus den Mapping-Snapshots.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wird ein echter Feld-Konflikt aufgeloest, erfahren jetzt auch die ANDEREN
Arbeitsplaetze davon - nicht nur der aufloesende Client.
- ConflictNotice-Modell + ConflictNotifier: schreibt pro Konflikt eine Notiz
in <shared>/conflicts/ (nach StarfaceId), liest beim Sync ungesehene Notizen
zu eigenen Kontakten, zeigt sie als Tray-Hinweis und merkt sich gezeigte
lokal (seen-conflicts.json). Veraltete Notizen (>7 Tage) werden aufgeraeumt.
- MainForm: schreibt nach einem Sync mit Konflikten die Notizen und zeigt
ausstehende Notizen anderer Clients (gefiltert auf eigene gemappte
StarfaceIds). AcquireCrossClientLock nimmt jetzt das gemeinsame Verzeichnis
als Parameter.
- README/CHANGELOG aktualisiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Das gemeinsame Verzeichnis darf ein UNC-Pfad sein (\\server\freigabe\...),
kein Netzlaufwerksbuchstabe noetig. Statt nur Directory.Exists zu pruefen
(und sonst still ohne Sperre zu syncen) wird das Verzeichnis bei Bedarf
angelegt; nur bei echtem Zugriffsfehler wird ohne Sperre fortgefahren.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Bisher wurde bei einem Konflikt (beide Seiten geaendert) der ganze Datensatz
ueberschrieben - eine gleichzeitige Aenderung an einem anderen Feld ging
verloren (z.B. A aendert Telefon in Outlook, B aendert Mail in Starface ->
eine Aenderung weg).
Jetzt:
- Mapping speichert je Seite einen Snapshot des letzten Sync-Stands
(LastOutlook/LastStarface), zusaetzlich zu den Hashes.
- Bei beidseitiger Aenderung im Both-Modus wird feldweise gemergt
(ContactMerger): unterschiedliche Felder bleiben beide erhalten, nur bei
echtem Konflikt am selben Feld gewinnt Outlook.
- Echte Feld-Konflikte landen in SyncResult.Conflicts und werden im MainForm
per Tray-Meldung angezeigt.
- Snapshots werden in allen Baseline-Punkten gesetzt (Phase 1-3) und fuer
aeltere Mappings beim naechsten unveraenderten Sync nachgetragen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der Schutz gegen gleichzeitige Syncs (manuell vs. Auto-Sync-Timer) war ein
nicht-atomares pruefen-und-setzen auf einem volatile bool. Zwischen Pruefung
und Setzen konnten ein UI-Klick und der Timer-Thread beide durchrutschen und
zwei Syncs gleichzeitig starten.
Jetzt per Interlocked.CompareExchange atomar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Outlook->Starface macht das Starface-Adressbuch zur exakten Kopie von
Outlook: Kontakte, die nur in Starface existieren, werden geloescht.
Starface->Outlook entsprechend umgekehrt (Phase 4).
Sicherheit:
- Loeschphase laeuft nur bei vollstaendig geladener Liste (unvollstaendige
Ladevorgaenge brechen schon vorher ab).
- Ist die Quelle komplett leer (z.B. falscher Ordner), wird die Loeschphase
uebersprungen statt die Zielseite zu leeren.
UI: Profil-Editor zeigt jetzt unter der Sync-Richtung einen Warnhinweis, der
das jeweilige Verhalten erklaert. README/CHANGELOG aktualisiert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
On domain PCs, HKCU policies are controlled by GPO and the
Trust Center settings are greyed out. Now also writes to HKLM
(requires admin rights) which overrides GPO settings.
Shows orange hint in settings when GPO lock is detected:
"Auf Domaenen-PCs: App einmalig als Admin starten!"
The app tries all 8 combinations: HKCU/HKLM x Policies/direct
x 16.0/15.0. Silently skips paths where permissions are denied.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Main window wider (830px) so all buttons fit without resizing
- Set ALL Outlook Object Model Guard registry values (not just 3)
- Clean removal: delete entire Security subkey when disabling
- Add hint in settings that Outlook restart is needed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New setting "Outlook-Sicherheitsabfrage automatisch erlauben"
sets registry keys under HKCU\Policies\Microsoft\Office\16.0\
Outlook\Security to auto-approve Object Model Guard prompts.
Applied at app startup and when saving settings. Disabling the
option removes the registry values (back to Outlook default).
Works with all Outlook versions (2016-2024, same registry path).
No admin rights needed (HKCU).
Outlook must be restarted after changing this setting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- "Ueber" menu item in tray context menu opens About dialog
- New user setting "Beim Start automatisch synchronisieren"
syncs all enabled profiles once at app startup
- Sync lock prevents concurrent sync runs (timer, manual,
on-start cannot overlap - second request is skipped)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When editing a profile and switching to a different Starface
address book, the old contact mappings are invalid (different
Starface IDs). Now automatically clears mappings and shows a
notification. Custom address books are already listed as tags.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Generated programmatically, no external ICO file needed.
Blue rounded square with white person silhouette and green/yellow
sync arrows. Used for main window title bar and system tray.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SetupTrayIcon was called on every RefreshProfileList, creating
a new NotifyIcon each time. Split into SetupTrayIcon (once) and
UpdateTrayMenu (on refresh).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deletes all contact mappings and resets LastSync timestamp.
Next sync will re-match all contacts fresh without creating
duplicates (thanks to the duplicate detection in sync engine).
No contacts are deleted on either side.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>