Die Fallstricke von Netzlaufwerken und UNC-Pfaden ── Fileserver (Freigabeordner) in Business-Anwendungen richtig einsetzen

· · Netzlaufwerk, UNC-Pfad, SMB, Dateifreigabe, Windows-Dienst, Fileserver, C#, .NET, Netzwerktechnik, Fehleranalyse, Windows-Entwicklung, Technische Beratung

„Auf dem Entwicklungsrechner lief es, aber beim Kunden heißt es plötzlich ‚Z:\ wurde nicht gefunden‘.“ „Sobald wir es in die Aufgabenplanung gelegt haben, schlug die Ausgabe in den Freigabeordner fehl.“ „Sobald wir daraus einen Windows-Dienst gemacht haben, war der Fileserver nicht mehr sichtbar.“ ── In Beratungsgesprächen zu Business-Anwendungen gehören Probleme rund um den Fileserver (Freigabeordner) zu den absoluten Klassikern. Auftragsdaten einlesen, Berichte oder CSV-Dateien ausgeben, Dateien überwachen, die ein Gerät ablegt: Bei On-Premises-Geschäftssystemen ist der Freigabeordner nach wie vor eine aktiv genutzte Integrationsschicht.

Das Tückische daran: Ein Großteil dieser Probleme ist gar kein Fehler im Code, sondern hat seine Wurzeln in Windows-Mechanismen – im Zusammenspiel von Laufwerksbuchstaben und Anmeldesitzungen, im Ausführungskonto des Dienstes und der Authentifizierung, im Verbindungsmanagement von SMB. Mit dem Debugger kommt man dabei nicht ans Ziel, und „auf meinem Rechner tritt das nicht auf“ frisst Zeit, ohne weiterzuhelfen.

Dieser Artikel richtet sich an Entwickler von Business-Anwendungen (WinForms/WPF/Windows-Dienste), die Dateiausgabe, -einlese oder -überwachung gegen einen Freigabeordner implementieren. Er ordnet die Fallstricke von Netzlaufwerken und UNC-Pfaden anhand der zugrunde liegenden Mechanik und fasst die praxiserprobten Regeln in einer Entscheidungstabelle zusammen.

1. Zunächst das Fazit

  • Ein Laufwerksbuchstabe (Z: und Ähnliches) ist keine systemweite Ressource, sondern gehört zu genau einer Anmeldesitzung. Jede Anmeldesitzung erhält ihren eigenen vollständigen Satz an Laufwerksbuchstaben von A bis Z, sodass ein von einem Benutzer zugeordnetes Laufwerk für einen Prozess unter einem anderen Benutzer oder für einen Dienst unter einer anderen Anmeldesitzung unsichtbar ist.1
  • Bei aktivierter UAC werden für einen Administrator zwei Anmeldesitzungen erstellt – eine normale und eine erhöhte –, und Laufwerkszuordnungen (symbolische DosDevices-Links) sind je Sitzung unabhängig. Deshalb verschwindet Z: „nur aus der erhöhten Anwendung“. Es gibt zwar einen Umgehungsweg über den Registrierungswert EnableLinkedConnections, der Zuordnungen zwischen beiden Sitzungen teilt, doch Microsoft dokumentiert ihn ausdrücklich als nicht unterstützte Einstellung, die „das System möglicherweise weniger sicher macht“.23
  • Wenn ein Dienst (oder ein Prozess unter einem anderen Sicherheitskontext) auf eine Remoteressource zugreifen muss, lautet die offizielle Empfehlung, einen UNC-Pfad (\\server\share\...) zu verwenden. Von der Zuordnung eines Laufwerksbuchstabens innerhalb eines Dienstes über net use oder die WNet-APIs wird wegen der Gefahr der Preisgabe von Anmeldeinformationen und gegenseitiger Beeinträchtigung zwischen Diensten abgeraten.1
  • Wem auf der Freigabeseite Berechtigungen erteilt werden, bestimmt das Ausführungskonto des Dienstes. LocalSystem und NetworkService authentifizieren sich im Netzwerk als „Anmeldeinformationen des Computers“, während LocalService anonyme Anmeldeinformationen präsentiert und für den Freigabezugriff ungeeignet ist. Ein Dienst unter einem lokalen Benutzerkonto kann überhaupt nicht auf Netzwerkressourcen zugreifen. In der Praxis bleiben als echte Optionen ein Domänenkonto oder ein gMSA, bei dem das Betriebssystem die Passwortverwaltung übernimmt.45678
  • Zum selben Server lassen sich nicht gleichzeitig mehrere Verbindungen mit unterschiedlichen Anmeldeinformationen halten. Die zweite Verbindung scheitert mit Fehler 1219 (ERROR_SESSION_CREDENTIAL_CONFLICT), und das ist so vorgesehen (by design).910
  • „Langsam, unterbrochen, manchmal nicht erreichbar“ ist der Normalfall eines Freigabeordners, kein Ausnahmefall. Eine untätige Verbindung wird standardmäßig nach 15 Minuten serverseitig getrennt (und beim nächsten Zugriff wieder verbunden). File.Exists liefert bei fehlender Berechtigung oder anderen Fehlern ohne Ausnahme false zurück, sodass sich „die Datei existiert nicht“ nicht von „der Server ist nicht erreichbar“ unterscheiden lässt.1112
  • FileSystemWatcher unterstützt zwar die Überwachung von Netzlaufwerken und Remotecomputern, muss aber so entworfen werden, dass Ereignisse verloren gehen können. Ein Pufferüberlauf kann Ereignisse verwerfen, und bei Überwachung über das Netzwerk ist der interne Puffer auf 64 KB begrenzt. Die Kombination mit Polling (vollständigem Scan) ist die bewährte Praxis.13

2. Der Zusammenhang zwischen Laufwerksbuchstaben und UNC-Pfaden ── Z: gehört zu „Ihrer Anmeldesitzung“

Ordnen Sie im Explorer \\fileserver\share dem Laufwerk Z: zu, sieht es ganz danach aus, als sei für die gesamte Maschine ein „Z:-Laufwerk“ entstanden. Das ist der erste Irrtum.

Die offizielle Dokumentation ist hier eindeutig: Ein Laufwerksbuchstabe ist nicht systemglobal, sondern jede Anmeldesitzung erhält ihren eigenen Satz von A bis Z. Ein umgeleitetes Laufwerk (ein Netzlaufwerk) lässt sich nicht zwischen Prozessen teilen, die unter unterschiedlichen Benutzerkonten laufen, und ein Dienst, der unter einer anderen Anmeldesitzung läuft, kann nicht auf einen Laufwerksbuchstaben zugreifen, der in einer anderen Sitzung eingerichtet wurde.1 Das System verwaltet Laufwerkszuordnungen anhand der Anmelde-SID, die jede Anmeldesitzung eindeutig identifiziert.1

Anders gesagt: Z: ist lediglich „eine Kurzform für einen Pfad, ein Satz pro Anmeldesitzung“ – der eigentliche Inhalt ist immer ein UNC-Pfad. Von hier aus lässt sich eine ganze Reihe in der Praxis häufig auftretender Symptome kettenartig erklären.

Symptom Zugrunde liegender Mechanismus
Z: fehlt bei Ausführung unter einem anderen Benutzer Laufwerksbuchstaben sind je Anmeldesitzung; die Zuordnung eines anderen Benutzers ist unsichtbar1
Z: fehlt bei Ausführung „als Administrator“ UAC erzeugt zwei Anmeldesitzungen (normal und erhöht), Zuordnungen werden nicht geteilt2
Z: fehlt, sobald es in die Aufgabenplanung oder einen Dienst gelegt wird Läuft unter einer anderen Anmeldesitzung. Selbst wenn der Dienst mit einem Benutzerkonto konfiguriert ist, erzeugt das System dafür eine neue Anmeldesitzung1

Der UAC-Fall lohnt eine genauere Betrachtung. Meldet sich ein Benutzer aus der Administratorgruppe an, erstellt das System zwei verknüpfte Anmeldesitzungen: eine mit eingeschränktem Token und eine mit dem vollen Administrator-Token. Der eigentliche Träger einer Laufwerkszuordnung ist ein symbolisches Link-Objekt (DosDevices), das eine Laufwerksbuchstaben-UNC-Zuordnung darstellt; dieses Objekt ist an eine bestimmte Anmeldesitzung gebunden und wird nicht zwischen Sitzungen geteilt.2 Anmeldeskripte laufen in der normalen Sitzung, sodass diese Zuordnung aus einem erhöhten Prozess schlicht nicht sichtbar ist – das ist die gesamte Erklärung.

Setzt man EnableLinkedConnections (einen DWORD-Wert unter HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System) auf 1, wird der symbolische Link in beide verknüpften Sitzungen geschrieben, wodurch dieses Symptom verschwindet. Die offizielle Dokumentation stellt jedoch ausdrücklich klar: „Dieser Workaround kann das System weniger sicher machen. Microsoft unterstützt diesen Workaround nicht; er wird ohne Gewähr bereitgestellt.“3 Eine nicht unterstützte Registrierungseinstellung in der Kundenumgebung einzupflanzen, ist für eine Business-Anwendung keine gute Lösung. Der richtige Weg ist, die Anwendung mit UNC-Pfaden zu schreiben. Auch die offizielle Fehlerbehebung zur Ordnerumleitung empfiehlt, „stets UNC-Pfade statt eines zugeordneten Laufwerksbuchstabens zu verwenden“.3

Implementiert ist das einfach: Speichern Sie die Pfade in der Konfiguration als UNC.

// appsettings.json ── Pfad als UNC speichern, nicht als Laufwerksbuchstabe
{
  "FileTransfer": {
    "IncomingDir": "\\\\fileserver01\\edi\\incoming",
    "ProcessedDir": "\\\\fileserver01\\edi\\processed"
  }
}

Kann der Benutzer über einen Ordnerauswahldialog einen Ort unter Z: wählen, verhindert eine Normalisierung auf UNC beim Speichern, dass es später bricht, wenn sich der Ausführungskontext ändert. Für die Umwandlung eines laufwerksbasierten Pfads in einen UNC-Pfad steht die API WNetGetUniversalName bereit.14

3. Auf den Freigabeordner von einem Windows-Dienst aus zugreifen ── UNC ist Pflicht, und „als wer“ erfolgt der Zugriff?

Wie im vorigen Kapitel dargelegt, steht einem Dienst kein zugeordnetes Laufwerk zur Verfügung. Die offizielle Dokumentation besagt, dass ein Dienst (und jeder Prozess, der unter einem anderen Sicherheitskontext läuft) Remoteressourcen über den UNC-Namen ansprechen sollte, und rät ausdrücklich davon ab, innerhalb eines Dienstes zur Laufzeit über net use oder die WNet-APIs einen Laufwerksbuchstaben zuzuordnen. Als Gründe werden genannt, dass die Zuordnung für andere Dienste im selben Kontext sichtbar wird, dass an net use übergebene Anmeldeinformationen über die Grenzen des Dienstes hinaus durchsickern können und dass mehrere Dienste, die dieselbe Zuordnung herzustellen versuchen, sich gegenseitig mit einem „bereits verbunden“-Fehler stören.1

Sobald der UNC-Pfad steht, folgt als Nächstes die Authentifizierung. Als wer greift der Dienst auf den Fileserver zu? Das bestimmt das Ausführungskonto, und danach richtet sich auch, wem auf der Freigabeseite Berechtigungen erteilt werden müssen.

Ausführungskonto Netzwerkidentität Berechtigungsvergabe auf der Freigabe Bewertung
LocalSystem Anmeldeinformationen des Computers4 In einer Domäne: Freigabe- und NTFS-Berechtigungen für das Computerkonto (DOMAIN\MACHINE$) Funktioniert, aber überprivilegiert. Bei Maschinenwechsel müssen Berechtigungen neu eingerichtet werden
NetworkService Anmeldeinformationen des Computers5 Wie oben Minimale lokale Rechte, im Netzwerk aber dieselbe Identität wie LocalSystem
LocalService Anonyme Anmeldeinformationen6 Es gibt nichts, was sich vergeben ließe Für Freigabezugriff ungeeignet
Lokaler Benutzer Kann nicht auf Netzwerkressourcen zugreifen7
Domänenbenutzer Dieses Konto Diesem Konto erteilen Der praktische Standard. Der Umgang mit Passwortänderungen ist der Betriebsaufwand
gMSA Dieses Konto Diesem Konto erteilen Das Passwort wird vom Betriebssystem automatisch verwaltet (alle 30 Tage automatisch geändert). Die beste Option, wo verfügbar815

Dass LocalSystem und NetworkService „als Computer“ ins Netzwerk auftreten, ist offiziell dokumentiert.45 Die BITS-Dokumentation beschreibt die praktische Konsequenz unmittelbar: Beschränkt die ACL der Quelldatei den Zugriff auf ein Benutzerkonto, erhält ein Dienst (der sich mit Computeranmeldeinformationen authentifiziert) eine Zugriffsverweigerung, und Systemkonten sollten keine zugeordneten Laufwerke verwenden.16

Daraus lassen sich drei praktische Leitlinien ableiten:

  • Die erste Anlaufstelle bei „Zugriff bricht ab, sobald es ein Dienst ist“ ist das Ausführungskonto. Was auf dem Desktop unter „Ihren“ Rechten lief, wird als Dienst gegen die Identität aus obiger Tabelle geprüft. Verifizieren Sie sowohl die Freigabeberechtigungen als auch die NTFS-ACL gegen diese Identität.
  • Für den langfristigen Betrieb in einer Domänenumgebung lassen Sie den Dienst unter einem Domänenkonto oder einem gMSA laufen. Ein gMSA besitzt ein 240 Byte langes zufälliges Passwort, das das Betriebssystem alle 30 Tage automatisch rotiert, wodurch sich Vorfälle der Art „am Montagmorgen stand alles still, weil das Passwort des Dienstkontos abgelaufen war“ strukturell ausschließen lassen.15
  • In einer Arbeitsgruppenumgebung (ohne Domäne) greift die Authentifizierung über Computeranmeldeinformationen nicht, sodass hier die im nächsten Kapitel behandelten expliziten Anmeldeinformationen zum Einsatz kommen.

Wie der Dienst selbst aufgebaut wird (Einrichtung des Ausführungskontos, Wiederherstellungsoptionen, sicheres Beenden), behandeln wir in „Wie man einen Windows-Dienst erstellt und betreibt“, die Mechanik von Sitzungen und Anmeldungen in „Windows-Sitzungsisolierung richtig verstehen“.

4. Der Umgang mit Anmeldeinformationen ── net use, Anmeldeinformationsverwaltung und Fehler 1219

Kann dem Ausführungskonto selbst keine Berechtigung auf der Freigabe erteilt werden (etwa in einer Arbeitsgruppe oder wenn ein NAS ein eigenes Kontosystem verwendet), müssen beim Verbindungsaufbau explizit Anmeldeinformationen übergeben werden. Es gibt dafür im Wesentlichen drei Wege:

  • net use \\server\share /user:... ── stellt die Verbindung für diese Anmeldesitzung her. Praktisch für die interaktive Bestätigung, aber wie im vorigen Kapitel erläutert wird von der Ausführung innerhalb eines Dienstes abgeraten.1
  • Anmeldeinformationsverwaltung (cmdkey) ── Speichern Sie Anmeldeinformationen mit cmdkey /add:server /user:svc-file /pass:..., werden sie bei anschließender Authentifizierung gegen diesen Server automatisch verwendet.1718 Die Speicherung erfolgt jedoch pro Benutzerprofil – ein Fallstrick, denn soll sie in einem Dienst genutzt werden, muss die Registrierung „im Kontext des Ausführungskontos des Dienstes“ erfolgen.
  • Programmatische Verbindung (WNetAddConnection2) ── ermöglicht den Aufbau einer Verbindung zu einer Netzwerkressource unter Angabe der Client-Anmeldeinformationen. Auch die offizielle Dokumentation nennt dies als eine der Strategien, mit denen ein Serverprozess auf Netzwerkressourcen zugreifen kann.19 Mit einer „geräteunabhängigen Verbindung“, die keinen Laufwerksbuchstaben zuweist, lässt sich weiterhin über den UNC-Pfad zugreifen.

Und die berüchtigtste Falle in diesem Bereich ist Fehler 1219.

Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. (ERROR_SESSION_CREDENTIAL_CONFLICT, 1219)10

Zum selben Server lassen sich aus derselben Anmeldesitzung heraus nicht mehrere Verbindungen mit unterschiedlichen Benutzernamen aufbauen. Versuchen Sie, mit „für den Vertriebsanteil das eigene Konto, für die Systemanbindung ein eigenes Konto“ zwei Arten von Anmeldeinformationen mit demselben Fileserver zu verbinden, scheitert die zweite Verbindung mit Fehler 1219. Die offizielle Dokumentation bezeichnet dies ausdrücklich als „by design“ (so vorgesehen); als Umgehung werden „per IP-Adresse verbinden“ oder „einen separaten DNS-Alias anlegen und darüber verbinden“ genannt ── also die Methode, so zu tun, als sei es ein anderer Server.9

In der Praxis ist es die erste Wahl, von vornherein eine Konfiguration zu vermeiden, bei der für einen einzigen Server mehrere Anmeldeinformationen im Wechsel verwendet werden: Konsolidieren Sie auf ein Integrationskonto und erteilen Sie diesem Konto Berechtigungen auf allen benötigten Freigaben. Nur wenn dies nicht möglich ist, trennen Sie die Pfade über einen Alias.

Ein Alias ist jedoch nicht damit erledigt, „einfach einen CNAME-Eintrag in DNS anzulegen“. In einer Kerberos-Umgebung versucht der SMB-Client, sich anhand des zum Zielnamen passenden SPN (Service Principal Name) zu authentifizieren, sodass der Zugriff über den CNAME komplett scheitern kann, wenn für den Alias kein SPN registriert ist. Auch die offizielle Fehlerbehebung nennt dieses fehlende SPN als eine der Ursachen und empfiehlt, den Alias nicht über einen DNS-CNAME, sondern über netdom computername <Servername> /add:<Alias> als Computernamen-Alias zu konfigurieren.20 Bevor Sie eine aliasbasierte Route als Lösung für Fehler 1219 fest im Entwurf verankern, überprüfen Sie unbedingt, dass sie in der Zielumgebung tatsächlich funktioniert.

Ein weiterer Hinweis: Die anspruchsvollere Anforderung „ein Dienst soll mit den Rechten des jeweils verbundenen Clientbenutzers auf den Fileserver zugreifen“ gehört nicht in den Bereich des Wiederverwendens von Anmeldeinformationen, sondern in den Bereich der Identitätsübernahme (Impersonation). Auch die offizielle Dokumentation empfiehlt Impersonation gegenüber einem Dienst, der eigene Anmeldeinformationen vorhält.1 Wie man Impersonation korrekt implementiert und welche zusätzlichen Aspekte bei Remoteressourcen zu beachten sind, behandelt „Windows-Impersonationstoken richtig handhaben“.

5. Entwerfen unter der Annahme „langsam, unterbrochen, manchmal nicht erreichbar“

Code, der mit demselben Denkmodell wie für eine lokale Festplatte geschrieben wurde, geht gegenüber einem Freigabeordner irgendwann garantiert schief. Drei Realitäten müssen von Anfang an eingeplant werden.

Erstens ist es normal, dass Verbindungen abbrechen. Eine untätige Verbindung wird standardmäßig nach 15 Minuten Zeitüberschreitung getrennt, um Serverressourcen nicht zu verschwenden – genau das ist das bekannte rote Kreuz auf einem zugeordneten Netzlaufwerk im Explorer, und beim nächsten Zugriff wird prompt neu verbunden.11 Anders gesagt: Sowohl eine Business-Anwendung, die nur gelegentlich auf die Freigabe zugreift und bei jedem Zugriff kurz stockt, als auch ein Überwachungstool, das „getrennt!“ meldet, ohne dass ein tatsächlicher Schaden entsteht, sind beides schlicht das planmäßige Verhalten des Systems. Prüfen Sie nicht, ob die Verbindung besteht, sondern ob die tatsächliche E/A gelingt.

Zweitens sind Fehlermeldungen wenig hilfreich. File.Exists liefert – ohne Ausnahme zu werfen – false, egal ob der Pfad ungültig ist, Berechtigungen fehlen oder ein Festplattenfehler vorliegt.12 Lokal bereitet „false bedeutet, es ist nicht da“ kaum Probleme; gegenüber einer Freigabe fallen jedoch „die Datei existiert nicht“ und „nicht erreichbar/keine Berechtigung“ beide auf dasselbe false zurück, sodass Code, der anhand von Exists eine geschäftliche Entscheidung trifft, bei einem Netzwerkausfall stillschweigend als ‚nichts gefunden‘ erfolgreich durchläuft – eine unangenehme Art zu versagen. Ein Entwurf, der die Existenzprüfung überspringt, direkt öffnet und anhand der geworfenen Ausnahme unterscheidet, lässt sich gegenüber einer Freigabe leichter diagnostizieren.

Drittens ist die Gegenseite manchmal noch nicht da. Direkt nach dem Hochfahren einer Maschine kann der Dienststart der Netzwerkbereitschaft vorausgehen, oder der Fileserver selbst befindet sich mitten im Neustart. Eine dauerhafte Verbindung (persistent connection) ist ein Mechanismus, der bei der Anmeldung des Benutzers wiederhergestellt wird,21 sodass es in der Welt eines Dienstes, der nie eine Anmeldung durchläuft, keine Garantie dafür gibt, dass „die Freigabe sichtbar ist, sobald der Dienst startet“. Das korrekte Verhalten besteht nicht darin, die Erreichbarkeit einmal beim Start zu prüfen und bei Fehlschlag zu beenden, sondern mit Wiederholungsversuchen zu warten.

Fließen alle drei Punkte ein, sieht das Grundgerüst der Schreibseite so aus:

// Vorübergehende Netzwerkfehler wiederholen, Geschäftsfehler sofort scheitern lassen
private static async Task WriteToShareAsync(string finalPath, byte[] content, CancellationToken ct)
{
    var dir = Path.GetDirectoryName(finalPath)!;
    var tempPath = Path.Combine(dir, $"~{Guid.NewGuid():N}.tmp");

    try
    {
        for (var attempt = 1; ; attempt++)
        {
            try
            {
                await File.WriteAllBytesAsync(tempPath, content, ct);
                File.Move(tempPath, finalPath); // Rename im selben Verzeichnis macht „Fertig“ öffentlich
                return;
            }
            catch (IOException ex) when (attempt < 5 && IsRetryable(ex))
            {
                // Nur vorübergehende Netzwerkfehler mit begrenztem Backoff wiederholen
                _logger.LogWarning(ex, "Schreiben in die Freigabe fehlgeschlagen (Versuch {Attempt}). Wiederholung", attempt);
                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), ct);
            }
        }
    }
    catch
    {
        // Wird aufgegeben und die Ausnahme weitergereicht, die temporäre Datei nach bestem Vermögen bereinigen
        try { File.Delete(tempPath); } catch { /* Fehler beim Bereinigen ignorieren */ }
        throw;
    }
}

// IOException kommt mit demselben Typ an, egal ob "Netzwerk getrennt", "am Ziel existiert
// bereits eine gleichnamige Datei" oder "Datenträger voll". Über die unteren 16 Bit des
// HResult (den Win32-Fehlercode) nur Fehler auswählen, bei denen eine Wiederholung sinnvoll ist
private static bool IsRetryable(IOException ex)
{
    var win32 = ex.HResult & 0xFFFF;
    return win32 is 53   // ERROR_BAD_NETPATH: Netzwerkpfad nicht gefunden
              or 59   // ERROR_UNEXP_NET_ERR: unerwarteter Netzwerkfehler
              or 64   // ERROR_NETNAME_DELETED: Netzwerkname nicht mehr verfügbar
              or 121; // ERROR_SEM_TIMEOUT: Zeitüberschreitung (sogenannter Semaphor-Timeout)
}

Wichtig ist auch, nicht leichtfertig „bei IOException wiederholen“ zu schreiben. Fehler, deren Ergebnis sich egal wie oft man es versucht nicht ändert – am Ziel existiert bereits eine gleichnamige Datei, ein Pfad ist zu lang, der Datenträger ist voll – kommen mit demselben IOException-Typ an, sodass eine Entscheidung allein anhand des Ausnahmetyps dazu führt, einen dauerhaften Fehler fünfmal zu wiederholen und mit Backoff unnötig Zeit zu vergeuden. Filtern Sie wie im Beispiel oben anhand des Win32-Fehlercodes (53/59/64/121 und ähnliche Trennungs-/Zeitüberschreitungscodes für den Freigabezugriff22) nur „Fehler, bei denen eine Wiederholung tatsächlich lohnt“ heraus. Diese Liste anhand tatsächlich in Produktionslogs beobachteter Codes aufzubauen, ist der realistische Weg, das zu betreiben.

Entscheidend ist nicht das Hinzufügen von Wiederholungsversuchen an sich, sondern die Kombination mit einem Übergabeprotokoll, das auch bei Wiederholung sicher bleibt (temp -> rename, Idempotenz). Bricht ein Schreibvorgang mittendrin ab, bleibt eine halbfertige Datei zurück. Ist zugesichert, dass der endgültige Name nur durch ein Rename nach dem Schließen der Datei entsteht, verhindern Sie, dass die empfangende Seite jemals eine unfertige Datei liest. Zu beachten ist zudem, dass bei einem Prozessabsturz oder Stromausfall der Bereinigungscode im Programm nie ausgeführt wird; deshalb lohnt sich zusätzlich eine regelmäßige Aufräumroutine im Übergabeordner – „lösche alle ~*.tmp-Dateien, die seit einer gewissen Zeit nicht mehr aktualisiert wurden“ –, damit sich kein Datenmüll ansammelt und die Verarbeitung blockiert. Das Gesamtbild dieses Übergabeentwurfs behandeln wir in „Grundlagen der Sperrverwaltung für Dateiintegration“.

Noch ein netzwerkspezifischer Stolperstein: Ein „Fehlgeschlagen“-Ergebnis garantiert nicht, dass tatsächlich ein Fehler eingetreten ist. Bricht die Verbindung genau dann ab, wenn das serverseitige Rename bereits abgeschlossen ist, kann der Client eine Ausnahme zurückerhalten, obwohl die Datei unter dem endgültigen Namen bereits veröffentlicht ist. Wiederholen Sie in diesem Fall gedankenlos, bleiben Sie entweder an einem „am Ziel existiert bereits eine gleichnamige Datei“-Fehler hängen, oder – hat die empfangende Seite die erste Datei bereits eingelesen – veröffentlichen Sie dieselben Daten doppelt. Die Lösung besteht darin, vor der Wiederholung eines fehlgeschlagenen Rename-Schritts den Zustand am Ziel zu prüfen und abzugleichen. Betten Sie im endgültigen Namen eine Verarbeitungs-ID ein (etwa eine Belegnummer oder eine Ausführungs-GUID), können Sie „am Ziel existiert bereits eine endgültige Datei mit derselben ID“ so interpretieren, dass das vorige Rename tatsächlich erfolgreich war, und den Vorgang als Erfolg abschließen; auch die empfangende Seite kann eine doppelte Übernahme anhand der ID-Dopplung abweisen.

6. Sperrverwaltung über SMB und die Zuverlässigkeit von FileSystemWatcher

6.1. Locks nicht blind vertrauen

Auf einen Freigabeordner greifen mehrere Clients gleichzeitig zu. Öffnen mit FileShare.None sorgt zwar für Exklusivität, solange das Handle offen ist – auch über SMB –, doch ein Entwurf, der sich auf „wenn ich das Lock bekomme, ist es sicher“ verlässt, bricht zusammen, sobald ein Handle durch eine Trennung verloren geht oder etwas mitmischt, das kein Lock nimmt (eine manuelle Kopie, ein anderes System). Der eigentliche Kern der gegenseitigen Ausschließung sollte nicht in einem betriebssystemseitigen Lock liegen, sondern im Übergabeprotokoll selbst: temp -> rename, ein atomarer Claim (nur der Gewinner eines Renames von incoming nach processing verarbeitet die Datei) und Idempotenz. Die Details dieser Denkweise finden Sie im oben verlinkten Artikel zur Sperrverwaltung.

6.2. Die Realität von FileSystemWatcher auf einer Remote-Freigabe

FileSystemWatcher unterstützt offiziell dokumentiert nicht nur die lokale Überwachung, sondern auch die Überwachung von Netzlaufwerken und Remotecomputern.13 Die Verwendung an sich ist legitim. Allerdings gibt es zwei Zuverlässigkeitsvorbehalte.

  • Benachrichtigungen laufen über einen Puffer, und bei Überlauf gehen sie verloren. Häufen sich Änderungen in kurzer Zeit, gehen Ereignisse jenseits der Puffergröße verloren.13
  • Bei Überwachung über das Netzwerk ist die Obergrenze von InternalBufferSize auf 64 KB begrenzt. Diese Einschränkung „geht nicht so groß wie lokal“ ist ausdrücklich dokumentiert.13

Zudem habe ich in der Fehleranalyse wiederholt das Symptom beobachtet, dass bei einem Neustart oder einer Trennung der Gegenseite die Überwachung schlicht lautlos stirbt, ohne dass jemals eine Benachrichtigung darüber eintrifft. Die praktische Schlussfolgerung lautet, einen FileSystemWatcher auf einer Remote-Freigabe nur als „Hinweis für schnellere Reaktion“ zu behandeln und einen vollständigen Scan (Polling) beim Start, im Fehlerfall und in regelmäßigen Abständen zur eigentlichen Wahrheitsquelle zu machen. Die Entwurfsmuster hierfür – einschließlich des Umgangs mit doppelten und außer der Reihe eintreffenden Ereignissen – behandeln wir ausführlich im „Praxisleitfaden zum FileSystemWatcher“.

7. Praxiserprobte Regeln (Entscheidungstabelle)

Hier die bisherigen Inhalte als Entscheidungstabelle für die immer wiederkehrenden Entwurfsfragen zusammengefasst.

Fragestellung Optionen Entscheidungsrichtlinie
Wie werden Pfade gespeichert Laufwerksbuchstabe / UNC-Pfad Jeder von der Anwendung verarbeitete Pfad sollte UNC sein. Einen Laufwerksbuchstaben als reine Bildschirmbequemlichkeit für den Benutzer betrachten1
Schreiben in die Freigabe Direktes Schreiben / temp -> rename Direktes Schreiben bietet keine Garantie, dass „die im Entstehen begriffene Datei nicht gelesen wird“. Der endgültige Name sollte grundsätzlich „fertig“ bedeuten
Erkennung neuer Dateien FileSystemWatcher allein / Polling / beides kombiniert Bei einer Remote-Freigabe von Ereignisverlusten ausgehen. Ist eine Verzögerung von einigen Minuten akzeptabel, ist reines Polling einfacher und robuster; bei Bedarf an Unmittelbarkeit beides kombinieren13
Ausführungskonto des Dienstes LocalSystem / Domänenkonto / gMSA Bei Freigabezugriff ein Domänenkonto oder gMSA verwenden. Wo ein gMSA verfügbar ist, lässt sich der gesamte Passwortbetrieb eliminieren8
Wenn gesonderte Anmeldeinformationen unvermeidbar sind net use aus dem Code aufrufen / WNetAddConnection2 / vorab registriertes cmdkey net use innerhalb eines Dienstes wird nicht empfohlen. Mehrere Anmeldeinformationen für denselben Server werden durch Fehler 1219 blockiert; das lässt sich bereits im Entwurf durch Kontenkonsolidierung oder einen DNS-Alias vermeiden19
Direktzugriff vieler Clients Jedes Gerät greift direkt per UNC zu / über einen Vermittlungsdienst (eine API) Wird das Produkt aus Geräteanzahl, Anmeldeinformationen und gleichzeitigem Zugriff unüberschaubar, den Freigabezugriff in einem einzigen Dienst bündeln und die Clients über eine API sprechen lassen

Die letzte Zeile verdient eine Anmerkung. Die Integration über einen Freigabeordner ist zwar bequem, doch je mehr Zugriffspunkte es gibt, desto exponentiell schwieriger wird es, zu verwalten, wer mit welcher Berechtigung zugreift und wer mit wem in Konflikt gerät. Ab einer gewissen Größenordnung bündeln Sie den Prozess, der den Fileserver berührt, in einem einzigen Windows-Dienst und lassen jeden Client über HTTP oder gRPC mit diesem sprechen. Den Freigabeordner von „der Schnittstelle zwischen Systemen“ zu „einem internen Implementierungsdetail dieses einen Dienstes“ herabzustufen, ist langfristig die am besten wartbare Lösung.

8. Zusammenfassung

  • Ein Laufwerksbuchstabe ist nichts weiter als ein an eine Anmeldesitzung gebundenes Symbol. Dass er für einen anderen Benutzer, einen erhöhten Prozess oder einen Dienst unsichtbar ist, ist so vorgesehen – schreiben Sie Ihre Anwendung mit UNC-Pfaden. EnableLinkedConnections ist ein nicht unterstützter Workaround.
  • Der Freigabezugriff von einem Dienst aus erfordert UNC. Als wer authentifiziert wird, bestimmt das Ausführungskonto: LocalSystem/NetworkService nutzen Computeranmeldeinformationen, LocalService ist anonym. In der Praxis wird die Freigabeberechtigung einem Domänenkonto oder einem gMSA erteilt.
  • Gleichzeitige Verbindungen zum selben Server mit mehreren Anmeldeinformationen scheitern mit Fehler 1219 (so vorgesehen). Vermeiden Sie das bereits im Entwurf durch Kontenkonsolidierung oder einen DNS-Alias.
  • „Langsam, unterbrochen, manchmal nicht erreichbar“ ist der Normalfall einer Freigabe. Eine untätigkeitsbedingte Trennung (standardmäßig 15 Minuten) ist keine Anomalie, und ein false von File.Exists lässt sich nicht von einem Netzwerkfehler unterscheiden. Bauen Sie Wiederholungsversuche, temp -> rename und Idempotenz gemeinsam ein.
  • FileSystemWatcher unterstützt Remote-Überwachung, doch ein Pufferüberlauf kann Ereignisse verwerfen, und die Obergrenze liegt bei 64 KB, weshalb die Kombination mit einem vollständigen Scan die bewährte Praxis ist.
  • Im Zweifel hilft die Entscheidungstabelle in Kapitel 7 weiter – und wächst die Größenordnung, ziehen Sie in Betracht, den Freigabezugriff in einem Vermittlungsdienst zu bündeln.

Verwandte Artikel

Verwandte Beratungsbereiche

KomuraSoft LLC (合同会社小村ソフト) übernimmt den Entwurf und die Implementierung von Dateiintegrationssystemen über Freigabeordner, die Konzeption von Zugriffsrechten und Authentifizierung bei der Umsetzung als Windows-Dienst sowie die Untersuchung von Fehlern wie „sobald es ein Dienst wird, ist die Freigabe nicht mehr sichtbar“ oder „schlägt nur in einer bestimmten Umgebung fehl“.

  1. Microsoft Learn, Services and Redirected Drives. Dazu, dass Laufwerksbuchstaben nicht systemglobal, sondern je Anmeldesitzung vergeben werden, dass Dienste nicht auf Laufwerksbuchstaben anderer Sitzungen zugreifen können und stattdessen UNC-Namen verwenden sollten, aus welchen Gründen die Laufwerkszuordnung per net use bzw. WNet-Funktionen innerhalb eines Dienstes nicht empfohlen wird (Preisgabe von Anmeldeinformationen, gegenseitige Beeinträchtigung von Diensten u. a.), dass auch für einen mit Benutzerkonto konfigurierten Dienst eine neue Anmeldesitzung erzeugt wird, sowie dazu, dass Client-Impersonation gegenüber dem Vorhalten eigener Anmeldeinformationen durch den Dienst empfohlen wird.  2 3 4 5 6 7 8 9 10 11

  2. Microsoft Learn, Mapped drives are not available from an elevated prompt when UAC is configured to Prompt for credentials. Dazu, dass bei aktivierter UAC zwei verknüpfte Anmeldesitzungen erzeugt werden, dass der eigentliche Träger einer Laufwerkszuordnung ein an die Anmeldesitzung gebundenes symbolisches Link-Objekt (DosDevices) ist, das nicht zwischen Sitzungen geteilt wird, sowie dazu, dass EnableLinkedConnections das Schreiben des symbolischen Links in beide Sitzungen erzwingt.  2 3

  3. Microsoft Learn, Folder Redirection fails to apply when redirected to mapped drive letter, instead of UNC path. Dazu, dass die LSA bei der Anmeldung eines Administrators zwei Zugriffstoken erzeugt und das Laufwerk unter dem Standardtoken zugeordnet wird, zum Vorgehen beim Setzen des Registrierungswerts EnableLinkedConnections samt der Warnung „kann das System weniger sicher machen, wird von Microsoft nicht unterstützt“, sowie dazu, dass stets UNC-Pfade statt Laufwerksbuchstaben empfohlen werden.  2 3

  4. Microsoft Learn, LocalSystem Account. Dazu, dass LocalSystem lokal über weitreichende Rechte verfügt und im Netzwerk die Anmeldeinformationen des Computers gegenüber Remoteservern vorlegt.  2 3

  5. Microsoft Learn, NetworkService Account. Dazu, dass NetworkService lokal nur minimale Rechte besitzt und im Netzwerk die Anmeldeinformationen des Computers gegenüber Remoteservern vorlegt.  2 3

  6. Microsoft Learn, LocalService Account. Dazu, dass LocalService im Netzwerk anonyme Anmeldeinformationen vorlegt.  2

  7. Microsoft Learn, About Service Logon Accounts. Dazu, dass das Anmeldekonto eines Dienstes dessen Sicherheitskontext zur Laufzeit bestimmt und dass ein Dienst im Sicherheitskontext eines lokalen Benutzerkontos nicht auf Netzwerkressourcen zugreifen kann.  2

  8. Microsoft Learn, Group Managed Service Accounts overview. Dazu, dass ein gMSA ein Domänenkonto ist, das automatische Passwortverwaltung und vereinfachte SPN-Verwaltung bietet und die Passwortverwaltung dem Windows-Betriebssystem überlassen werden kann.  2 3

  9. Microsoft Learn, The network folder specified is currently mapped using a different user name and password error. Dazu, dass der Fehler beim Versuch, mehrere Verbindungen mit unterschiedlichen Anmeldeinformationen zu demselben Server aufzubauen, so vorgesehen ist (by design), sowie dazu, dass als Umgehung die Verbindung per IP-Adresse oder das Anlegen eines separaten DNS-Alias genannt werden.  2 3

  10. Microsoft Learn, System Error Codes (1000-1299). Zur Definition und Meldung des Fehlers 1219 (ERROR_SESSION_CREDENTIAL_CONFLICT).  2

  11. Microsoft Learn, Mapped drive connection to network share may be lost. Dazu, dass eine untätige Verbindung standardmäßig nach 15 Minuten Zeitüberschreitung getrennt wird (Autodisconnect) und dass das Laufwerkssymbol im Explorer zwar ein rotes Kreuz zeigt, beim Zugriff aber prompt neu verbunden wird.  2

  12. Microsoft Learn, File.Exists(String) Method. Dazu, dass die Methode ohne fehlende Leseberechtigung keine Ausnahme wirft, sondern false zurückgibt, und dass sie auch bei jeglichem Fehler während der Existenzprüfung (ungültiger Pfad, Festplattenfehler, fehlende Berechtigung u. a.) false zurückgibt.  2

  13. Microsoft Learn, FileSystemWatcher Class. Dazu, dass die Überwachung von Dateien auf dem lokalen Computer, auf Netzlaufwerken und auf Remotecomputern unterstützt wird, dass bei Überschreiten der Puffergröße Ereignisse verloren gehen können, sowie dazu, dass der Höchstwert von InternalBufferSize bei Überwachung über das Netzwerk 64 KB beträgt.  2 3 4 5

  14. Microsoft Learn, WNet Functions. Dazu, dass WNetGetUniversalName die Funktion ist, die aus einem laufwerksbasierten Pfad einen universellen (UNC-)Namen ermittelt. 

  15. Microsoft Learn, Secure group managed service accounts. Dazu, dass das Passwort eines gMSA zufällig mit 240 Byte erzeugt und vom Betriebssystem alle 30 Tage automatisch geändert wird, wodurch Administratoren weder Passwortänderungen planen noch Dienststillstände einkalkulieren müssen.  2

  16. Microsoft Learn, Service Accounts and BITS. Dazu, dass sich LocalSystem/NetworkService im Netzwerk mit Computeranmeldeinformationen authentifizieren und LocalService mit anonymen Anmeldeinformationen, dass bei einer auf ein Benutzerkonto beschränkten ACL der Zugriff verweigert wird, sowie dazu, dass Systemkonten keine zugeordneten Laufwerke verwenden sollten. 

  17. Microsoft Learn, cmdkey. Dazu, dass sich mit dem Befehl cmdkey gespeicherte Benutzernamen und Passwörter (Anmeldeinformationen) erstellen, auflisten und löschen lassen. 

  18. Microsoft Learn, Credentials processes in Windows authentication. Dazu, dass die Anmeldeinformationsverwaltung Anmeldeinformationen im Windows-Anmeldeinformationscontainer speichert und sie bei nachfolgenden Authentifizierungen automatisch vorlegt. 

  19. Microsoft Learn, Client Access to Network Resources. Dazu, dass der Verbindungsaufbau per WNetAddConnection2 unter Angabe der Client-Anmeldeinformationen als eine der Strategien genannt wird, mit denen ein Serverprozess auf Netzwerkressourcen zugreifen kann. 

  20. Microsoft Learn, SMB file server share access is unsuccessful through DNS CNAME alias. Dazu, dass eine Ursache für das Scheitern des SMB-Zugriffs über einen CNAME das fehlende SPN für den Alias ist, sowie dazu, dass empfohlen wird, den Alias nicht per DNS-CNAME, sondern über den Befehl netdom computername zu definieren. 

  21. Microsoft Learn, Windows Networking Operations. Dazu, dass eine dauerhafte Verbindung (persistent connection) eine Netzwerkverbindung ist, die das System bei der Anmeldung des Benutzers automatisch wiederherstellt. 

  22. Microsoft Learn, System Error Codes (0-499). Zu den Definitionen von ERROR_BAD_NETPATH (53), ERROR_UNEXP_NET_ERR (59), ERROR_NETNAME_DELETED (64) und ERROR_SEM_TIMEOUT (121). 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Warum kann ein Windows-Dienst nicht auf ein zugeordnetes Netzlaufwerk (Z:) zugreifen?
Weil ein Laufwerksbuchstabe keine systemweite Ressource ist – er gehört zu genau einer Anmeldesitzung. Ein Z:-Laufwerk, das ein Benutzer im Explorer zuordnet, ist nichts weiter als ein Symbol innerhalb der Anmeldesitzung dieses Benutzers und für einen Dienst, der unter einer anderen Anmeldesitzung läuft, unsichtbar. Selbst wenn ein Dienst unter einem Benutzerkonto läuft, erzeugt das System für den Dienst eine neue Anmeldesitzung, sodass eine Desktop-seitige Zuordnung für dasselbe Konto nicht übernommen wird. Der korrekte Weg für einen Dienst besteht darin, die Ressource über ihren UNC-Pfad \\server\share anzusprechen.
Wie kann ein als LocalSystem laufender Dienst auf einen Freigabeordner zugreifen?
Im Netzwerk authentifiziert sich LocalSystem mit den eigenen Anmeldeinformationen des Computers. In einer Domänenumgebung können Sie dem Computerkonto der Maschine (DOMAIN\MACHINE$) Berechtigungen auf der Freigabe erteilen (sowohl Freigabeberechtigungen als auch NTFS-ACL), damit das funktioniert. Diese Konfiguration ist jedoch im Betrieb unhandlich – ein Maschinenwechsel bedeutet, die Berechtigungen neu einzurichten –, weshalb wir empfehlen, den Dienst stattdessen unter einem Domänenkonto oder einem gMSA (Group Managed Service Account) laufen zu lassen und diesem Konto die Berechtigung auf der Freigabe zu erteilen.
Ist es sicher, Dateien auf einem Freigabeordner mit FileSystemWatcher zu überwachen?
Sie können ihn verwenden, sollten sich aber nicht allein darauf verlassen. FileSystemWatcher unterstützt zwar die Überwachung von Netzlaufwerken und Remotecomputern, aber Änderungsbenachrichtigungen laufen über einen internen Puffer, und wenn Benachrichtigungen gehäuft eintreffen, kann der Puffer überlaufen und Ereignisse gehen verloren. Bei der Überwachung über das Netzwerk ist die Puffergröße zudem auf 64 KB begrenzt. Behandeln Sie Benachrichtigungen ausschließlich als Auslöser für einen erneuten Scan und kombinieren Sie sie stets mit einem vollständigen Scan (Polling) beim Start, im Fehlerfall und in regelmäßigen Abständen.
Wie gestalte ich einen Dateiaustausch, der Netzwerkunterbrechungen standhält?
Gestalten Sie das System so, dass „die Freigabe ist langsam, bricht ab und ist manchmal nicht erreichbar“ der Normalfall ist, nicht ein Randfall. Konkret: Schreiben Sie unter einem temporären Dateinamen und benennen Sie die Datei erst nach dem Schließen des Handles um (temp -> rename), kapseln Sie Lese- und Schreibvorgänge in Wiederholungslogik und unterscheiden Sie vorübergehende Netzwerkfehler von echten Geschäftsfehlern. Beachten Sie, dass File.Exists auch dann false zurückgibt, wenn die Datei nicht erreichbar ist, und daher „die Datei existiert nicht“ nicht von „der Server ist nicht erreichbar“ unterscheiden kann – und sorgen Sie als letzte Verteidigungslinie dafür, dass die Verarbeitung idempotent bleibt, sodass eine erneute Verarbeitung nie zu Beschädigungen führt.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog