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>
Im Both-Modus wurde ein auf einer Seite geloeschter Kontakt bisher auf der
anderen Seite einfach wieder angelegt, statt die Loeschung zu spiegeln.
Jetzt wird anhand der gespeicherten Baseline (LastOutlookHash /
LastStarfaceHash) entschieden:
- Gegenseite seit letztem Sync unveraendert -> es war eine Loeschung ->
auf der anderen Seite ebenfalls loeschen.
- Gegenseite wurde geaendert -> Bearbeitung gewinnt -> neu anlegen
(kein Datenverlust).
In den Ein-Richtungs-Modi bleibt die Quelle fuehrend: eine Loeschung im
Ziel wird aus der Quelle wiederhergestellt (StarfaceToOutlook legt einen
in Outlook geloeschten Kontakt jetzt ebenfalls wieder an statt ein totes
Mapping zu behalten).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der vorherige Fix war zu konservativ: bei einem in der geladenen Liste
fehlenden Starface-Kontakt wurde das Mapping immer behalten und nichts neu
angelegt - auch wenn der Kontakt in Starface wirklich geloescht war. In
Richtung Outlook->Starface wurden geloeschte Kontakte dadurch nie wieder
angelegt.
Jetzt wird der Kontakt per ID abgefragt:
- existiert noch (anderes Adressbuch) -> Mapping behalten, nichts anlegen
- 404 (wirklich geloescht) -> in Both/OutlookToStarface neu anlegen
(Phase 2), in StarfaceToOutlook Loeschung nach Outlook spiegeln
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Behebt Dubletten auf beiden Seiten und sehr langsame Syncs:
- Getrennte Hash-Baselines pro Seite (LastOutlookHash/LastStarfaceHash)
statt eines gemeinsamen Hashes. Outlook und Starface stellen denselben
Kontakt unterschiedlich dar, wodurch der gemeinsame Hash nie passte und
bei jedem Lauf praktisch jeder Kontakt neu geschrieben wurde.
- Update-Methoden geben den frisch eingelesenen Stand zurueck, damit die
Baseline nach dem Schreiben korrekt gesetzt wird (sauberes Konvergieren).
- Unvollstaendig geladene Starface-Liste bricht jetzt mit Fehler ab
(inkl. Retry) statt still mit Teil-Liste weiterzuarbeiten - das liess
Kontakte faelschlich als geloescht erscheinen und erzeugte Dubletten.
- Fehlender Starface-Kontakt (anderes Adressbuch) behaelt das Mapping,
statt es zu verwerfen und neu anzulegen.
- Lockereres Re-Matching: gleicher E-Mail- oder voller Namens-Treffer
reicht; umformatierte Telefonnummern blockieren ihn nicht mehr.
- Starface-Kontaktdetails werden parallel geladen (8 gleichzeitig).
Bestehende Mappings werden beim ersten Sync automatisch migriert.
CHANGELOG.md hinzugefuegt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two fixes for delete propagation in Phase 1:
1. When Outlook contact not found by EntryID, try to re-match
by name/email/phone before assuming it was deleted. Outlook
can change EntryIDs on restart or profile changes, causing
the sync to think ALL contacts were deleted.
2. When Starface contact not found in current list, DON'T delete
from Outlook. The contact may belong to a different address
book. Just drop the mapping and let Phase 2/3 re-link it.
These changes make delete propagation much safer and prevent
accidental mass-deletion of contacts.
Co-Authored-By: Claude Opus 4.6 (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>
On Terminal Servers, normal users cannot write to HKCU\Software\
Policies. Now also writes to HKCU\Software\Microsoft\Office\...\
Security which is always writable and also read by Outlook.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Apply to both 16.0 (2016-2024/365) and 15.0 (2013) registry
paths. Costs nothing and ensures it works regardless of which
Office version is installed.
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>
- Include tags in PUT request (Starface may require it)
- Log failed updates with status code and response body
- Also committed: SafeGet for Outlook COM property reading
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dynamic COM with ?? operator can fail silently for properties
that return COM null vs .NET null. Use GetType().InvokeMember()
which reliably reads any Outlook property and catches COM errors
per field instead of crashing the whole contact read.
Fixes Fax and other fields that may not have been read correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fax and HomePhone are now checked for compatibility (filled on
one side, empty on other = different contacts)
- Fax number can serve as a strong match together with Company
- Prevents fax-only contacts from being missed or mismatched
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New matching logic checks ALL identifying fields for compatibility:
- Empty vs filled field = different contacts (not a match)
- Both empty = compatible (ignored for matching)
- Both filled = must be equal
Fields checked: Email, FirstName, LastName, Company, PhoneWork,
PhoneMobile. Requires at least one strong match (email, name,
or phone) plus no conflicting fields.
Example: Two "Max Mustermann" where one has Company="Firma A"
and the other has empty Company are now correctly identified as
different contacts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a mapped contact is deleted on one side, delete it on the
other side too (respecting sync direction setting):
- Deleted in Outlook -> delete in Starface (if direction allows)
- Deleted in Starface -> delete in Outlook (if direction allows)
- Deleted on both sides -> just remove the mapping
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>
Starface requires every contact to be assigned to a tag.
- Load tag IDs when fetching address books (folder/all for central,
folder/private for personal)
- Include tags array in POST /contacts body
- Debug log all discovered tags for troubleshooting
Important: Existing profiles need to reload address books (edit
profile -> load address books -> save) to pick up the tag IDs.
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>
Log the full request body and response when POST /contacts fails
so we can see why new contacts are not being created.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>