Named Pipes in der Praxis — Windows-Standard-IPC von der Auslegung bis zur Sicherheit
· Aktualisiert am: · Go Komura · Windows, IPC, Windows-Entwicklung, C#, C++, Sicherheit, Win32-API
Änderungsverlauf (Erstfassung, veröffentlicht am 22. Aug 2026)
- Erstveröffentlichung
Diesen Artikel zitieren(DOI (registriertes Archiv): 10.5281/zenodo.22176763)
Die folgenden DOIs verweisen auf bereits archivierte Versionen, die vom aktuellen Text abweichen können. Verwenden Sie die URL dieser Seite, um auf den aktuellen Text zu verweisen.
Go Komura (2026). Named Pipes in der Praxis — Windows-Standard-IPC von der Auslegung bis zur Sicherheit. KomuraSoft LLC. https://comcomponent.com/de/blog/windows-named-pipes-practical-guide/
- DOI (registriertes Archiv)
- 10.5281/zenodo.22176763
- DOI (zuletzt registrierte Version)
- 10.5281/zenodo.22176764
„Ich will von einem Einstellungsbildschirm Befehle an einen residenten Dienst schicken.“ „Ich will nur die Arbeit, die Administratorrechte braucht, in einen eigenen Prozess auslagern.“ Wenn Sie unter Windows diese Art von Prozesskommunikation (IPC) bauen, ist das Erste, das Sie prüfen sollten, eine Named Pipe.
Der Grund, warum sie der erste Kandidat für die Kommunikation innerhalb desselben PCs ist, ist nicht nur die Leichtigkeit des Lesens und Schreibens. Der große Vorteil ist, dass Sie mit Windows-ACLs eingrenzen können, wer sich verbindet, und bei Bedarf das Windows-Konto des Gegenübers prüfen und die Arbeit mit dessen Rechten ausführen können. Eine Pipe anzulegen macht sie für sich genommen nicht sicher; Verbindung und Rechte müssen konfiguriert werden.123
Dieser Artikel ordnet den Entwurf in der Reihenfolge Form der Verbindung, Nachrichtengrenzen, Serveraufbau, Sicherheit und Verhalten bei Fehlschlag. Er richtet sich an Entwickler, die Geschäftsanwendungen und Dienste unter Windows schreiben. Er nimmt den „ersten Kandidaten für IPC auf demselben Rechner“ aus dem Artikel zur Entscheidungstabelle der Prozesskommunikation und arbeitet ihn bis zu den Entscheidungen bei der Umsetzung durch.
1. Zuerst das Fazit: Fünf Entwurfsentscheidungen
Bevor Sie eine API wählen, legen Sie Kommunikationspartner, Dateneinheit, gleichzeitige Verbindungen, Rechte und den Umgang mit Fehlschlägen fest.
| Entscheidung | Grundsatz | Wo Sie mehr lesen |
|---|---|---|
| Mit wem kommunizieren | Für Windows-Prozesse auf demselben PC ist eine Named Pipe der erste Kandidat. Wenn Remote-Ausbringung, andere Betriebssysteme oder die Nutzung eines bestehenden Protokolls im Vordergrund stehen, kommt eine TCP-basierte Option in Betracht | Kapitel 2 |
| Was zählt als eine Einheit | Wenn ein Schreibvorgang als eine Einheit gelten soll, Nachrichtenmodus. Wenn Sie bereits Framing haben, Byte-Modus | Kapitel 3 |
| Wie mehrere Clients angenommen werden | Mehrere Instanzen desselben Namens bereitstellen. Bei einer neuen .NET-Umsetzung sind asynchrones I/O und async/await der geradlinige Weg | Kapitel 4 |
| Wer sich verbinden und Rechte ausleihen darf | Remote-Ablehnung, ACL, Garantie der ersten Instanz und Impersonationsstufe des Clients als Satz entwerfen | Kapitel 5 und 6 |
| Was tun, wenn die Kommunikation fehlschlägt | Startwartezeiten, erneutes Verbinden, Nachrichtenobergrenze und Antwortbestätigung ins Protokoll aufnehmen | Kapitel 7 |
Mit einer Named Pipe stehen Verbindungskontrolle über ACLs und Identitätswechsel des Clients als Windows-Mechanismen zur Verfügung. Bei Localhost-TCP müssen Sie eine eigene Authentifizierung entwerfen, um festzustellen, wer das Gegenüber ist. Wenn Sie die Kommunikation später auf Remote ausweiten, mit anderen Betriebssystemen sprechen oder bestehende Vermögen wie gRPC nutzen wollen, ist eine TCP-basierte Option vorteilhaft.23
Besonders wichtig ist, eine Anfrage, deren Identitätswechsel fehlgeschlagen ist, nicht auszuführen. Ignorieren Sie den Fehlschlag, läuft die Verarbeitung mit den eigenen Rechten des Servers weiter, nicht mit denen des Clients. Wenn Sie einen privilegierten Dienst bauen, lesen Sie die Kapitel 5 und 6, nicht nur die Codebeispiele.4
2. So funktionieren Verbindungen: Ein gemeinsamer Name, ein Kanal pro Client
2.1 Die Rollen von Anlegen, Verbinden und Lesen/Schreiben trennen
Eine Named Pipe ist ein einseitiger oder bidirektionaler Kanal, der durch einen Namen wie \\.\pipe\MyCompany.MyApp.Control identifiziert wird. Server und Client beginnen mit unterschiedlichen APIs.1
| Stufe | Serverseite | Clientseite |
|---|---|---|
| Den Kanal vorbereiten | Eine Instanz mit CreateNamedPipe anlegen |
Den vom Server vorbereiteten Pipe-Namen verwenden |
| Verbinden | Mit ConnectNamedPipe auf einen Client warten |
Denselben Namen mit CreateFile öffnen |
| Daten austauschen | Mit ReadFile / WriteFile lesen und schreiben |
Mit ReadFile / WriteFile lesen und schreiben |
Nach dem Verbinden behandeln beide Seiten sie in derselben Form wie Datei-I/O. Über die Namensangabe hinaus machen die folgenden Ideen von Instanz und Richtung mehrere Verbindungen und Verbindungsfehler leichter verständlich.
2.2 Eine Instanz bedient einen Client
Mehrere Pipes mit demselben Namen können angelegt werden, und eine Instanz wird zum Kanal mit einem Client. Ein gemeinsamer Name bedeutet nicht, dass alle Clients einen einzigen Kanal teilen. Der erste Aufruf von CreateNamedPipe legt die maximale Instanzzahl fest. Als Obergrenze steht auch PIPE_UNLIMITED_INSTANCES zur Verfügung.15
flowchart TB
accTitle: Grundstruktur einer Named Pipe
accDescr: Der Server legt mehrere Pipe-Instanzen desselben Namens an und wartet mit ConnectNamedPipe auf Verbindungen; jeder Client öffnet den Namen mit CreateFile und hat einen bidirektionalen Eins-zu-eins-Kanal mit einer Instanz
s["Server"] --> i1["Instanz 1"]
s --> i2["Instanz 2"]
s --> i3["Instanz 3"]
c1["Client A"] <--> i1
c2["Client B"] <--> i2
c3["Client C"] <--> i3
Abbildung 1: Beispiel einer bidirektionalen Pipe. Indem mehrere Instanzen desselben Namens bereitstehen, kommuniziert der Server mit mehreren Clients jeweils eins zu eins.
2.3 „Richtung“ ist vom Server aus benannt
Die Zugriffsspezifikation des Clients muss zur Richtung der vom Server angelegten Pipe passen. Stimmen sie nicht überein, schlägt CreateFile fehl.6
| Richtung, die der Server anlegt | Verhalten des Servers | Zugriff, den der Client angibt |
|---|---|---|
| Bidirektional | Liest und schreibt | Kann lesend, schreibend oder beides geöffnet werden |
| Outbound | Schreibt nur | Nur lesend öffnen |
| Inbound | Liest nur | Nur schreibend öffnen |
Sind alle Instanzen in Gebrauch, liefert das CreateFile des Clients ERROR_PIPE_BUSY. Warten Sie mit WaitNamedPipe auf eine freie Instanz und rufen Sie CreateFile erneut auf. Das ist ein anderer Fehlschlag als der, dass der Server die Pipe noch nicht angelegt hat; Abschnitt 7.1 behandelt ihn zusammen mit Startreihenfolge-Wettläufen.6
2.4 Auch bei lokaler Nutzung festlegen, wie Remote-Verbindungen behandelt werden
Eine Named Pipe kann in der Form \\server\pipe\Name auch für Remote-Verbindungen über SMB genutzt werden. In einem modernen Entwurf gibt es jedoch kaum einen Grund, diesen Weg aktiv zu nutzen, und bei lokaler IPC kommt es darauf an, einen Weg, den Sie nicht nutzen, nicht offen zu lassen. Lehnen Sie ihn in Abschnitt 5.1 ausdrücklich mit PIPE_REJECT_REMOTE_CLIENTS ab.17
3. Den Modus wählen: Entscheiden, wie viel eine Einheit ist
3.1 Nachrichtenmodus für Anfrage und Antwort, Byte-Modus bei vorhandenen Grenzen
Der Unterschied zwischen den Übertragungsmodi ist, ob die Pipe die Grenzen der Schreibvorgänge bewahrt.5
| Modus | Was der Empfänger sieht | Geeignet für |
|---|---|---|
Byte-Modus (PIPE_TYPE_BYTE) |
Ein ununterbrochener Bytestrom wie TCP | Daten mit eigenem Framing, etwa einem Längenpräfix, oder Stream-Übertragung |
Nachrichtenmodus (PIPE_TYPE_MESSAGE und Nachrichtenlesemodus) |
Einheiten, in denen ein Schreibvorgang eine Nachricht ist | Anfragen und Antworten einzeln |
flowchart TB
accTitle: Unterschied zwischen Byte-Modus und Nachrichtenmodus
accDescr: Im Byte-Modus werden drei Schreibvorgänge zu einem ununterbrochenen Bytestrom, den der Empfänger selbst trennen muss, während im Nachrichtenmodus die Einheit jedes Schreibvorgangs bewahrt wird und den Empfänger unverändert erreicht
bw["Byte-Modus: AAA, BB, CCCC schreiben"] --> br["Empfang als Bytestrom AAABBCCCC"]
br --> bf["Grenzen (Framing) selbst entwerfen"]
mw["Nachrichtenmodus: dieselben drei Schreibvorgänge"] --> mr["Empfang als drei Einheiten: AAA, BB, CCCC"]
mr --> mf["Die Einheit jedes Schreibvorgangs bleibt erhalten"]
Abbildung 2: Im Byte-Modus verwaltet der Empfänger die Grenzen. Der Nachrichtenmodus kann die Einheit jedes Schreibvorgangs bewahren, aber ein einzelner Lesevorgang empfängt nicht unbedingt die ganze Nachricht.
Wenn Sie noch keine eigenen Grenzen haben und einen Schreibvorgang als eine Anfrage behandeln wollen, ist der Nachrichtenmodus bequem. Wenn Sie bereits etwas wie ein serialisiertes Format mit Längenpräfix nutzen, ist der Byte-Modus in Ordnung. In .NET entspricht PipeTransmissionMode.Message der Angabe des Nachrichtentyps.8
Für eine bidirektionale Pipe vom Nachrichtentyp gibt es außerdem TransactNamedPipe, das eine Anfrage sendet und die Antwort in einem einzigen Aufruf empfängt.9
3.2 Nachrichtentyp und Lesemodus sind getrennte Einstellungen
Der Lesemodus wird pro Handle gesetzt. PIPE_READMODE_MESSAGE in CreateNamedPipe anzugeben ist eine Einstellung der Serverseite. Ein Handle, das der Client mit CreateFile geöffnet hat, ist standardmäßig im Byte-Lesemodus.6
| Umsetzung | Serverseite | Clientseite |
|---|---|---|
| Win32 | PIPE_TYPE_MESSAGE und PIPE_READMODE_MESSAGE angeben |
Nach dem Verbinden PIPE_READMODE_MESSAGE mit SetNamedPipeHandleState angeben |
| .NET | PipeTransmissionMode.Message angeben |
Nach dem Verbinden NamedPipeClientStream.ReadMode auf Message setzen |
Der Punkt ist, nicht anzunehmen, dass allein der Server im Nachrichtentyp den Client in denselben Einheiten lesen lässt.
3.3 Auch im Nachrichtenmodus müssen Sie den Rest nachlesen
Dass Nachrichtengrenzen bewahrt werden und dass die ganze Nachricht in den Empfangspuffer passt, sind zwei verschiedene Dinge. Ist der Puffer klein, gibt ReadFile ERROR_MORE_DATA zurück und die Nachricht wird geteilt. Sie brauchen eine Schleife, die den bisher empfangenen Teil behält, den Rest nachliest und zusammenfügt.56
| Leseergebnis | Behandlung |
|---|---|
| Erfolg | Die Nachricht als vollständig verarbeiten |
ERROR_MORE_DATA |
Es bleibt noch etwas, also den Rest nachlesen und zusammenfügen |
| Jeder andere Fehler | Die Anfrage nicht weiterverarbeiten; als Kommunikationsfehlschlag wie eine Trennung behandeln |
Wenn kleine Anfragen durchgehen, aber nur große Anfragen zerbrechen, prüfen Sie diese Nachleselogik. Unbegrenztes Zusammenfügen ist außerdem nicht zulässig. Legen Sie eine Maximalgröße für eine Nachricht als Teil des Protokolls fest und trennen Sie die Verbindung, wenn die Grenze überschritten wird. Das verhindert Speicherverschwendung durch riesige Nachrichten.
4. Serveraufbau: Annahme und Clientbehandlung trennen
4.1 Den synchronen Thread-Entwurf und den Overlapped-Entwurf vergleichen
Die Grundoperation des Servers ist der Zyklus Instanz anlegen, auf Verbindung warten, lesen und schreiben, trennen und zur nächsten übergehen. Um mehrere Clients gleichzeitig anzunehmen, führen Sie diesen Ablauf auf mehreren Instanzen.
| Entwurf | Mechanismus | Vorteile und Einschränkungen |
|---|---|---|
| Synchroner Thread-Entwurf | Einen Thread pro Instanz zuweisen und mit synchronem I/O verarbeiten | Der Code ist geradlinig. Er verbraucht jedoch einen Thread pro Client, und Sie brauchen einen Weg, beim Anhalten aus blockierendem I/O herauszukommen |
| Overlapped-Entwurf (asynchron) | Mit FILE_FLAG_OVERLAPPED anlegen und Verbinden, Lesen und Schreiben asynchron ausgeben |
Eine kleine Zahl von Threads kann Completions für mehrere Instanzen verarbeiten |
Microsofts offizielles Beispiel legt das Ereignis jeder Instanz in ein Array, wartet mit WaitForMultipleObjects und verarbeitet mehrere Verbindungen auf einem einzigen Thread. Es trennt die Clientzahl von der Threadzahl. Bei größerem Maßstab können Sie auch IOCP oder Threadpool-I/O anbinden.10
Wie asynchrones I/O selbst funktioniert, steht im Artikel zu synchronem und asynchronem I/O.
4.2 Bei einer neuen .NET-Implementierung ist async/await der geradlinige Weg
In .NET können Sie WaitForConnectionAsync / ReadAsync / WriteAsync von NamedPipeServerStream mit async/await nutzen. Ohne besonderen Grund empfehle ich für neue Umsetzungen diese asynchrone Form. Nimmt die Annahmeschleife eine Verbindung entgegen, übergibt sie sie an die Behandlung pro Client und kehrt zur Annahme der nächsten Instanz zurück.8
Das Folgende ist das Gerüst dieser Annahmeschleife. Als Beispiel für die Nutzung zwischen Prozessen desselben Benutzers gibt es PipeOptions.Asynchronous und PipeOptions.CurrentUserOnly an. Unter Windows beschränkt CurrentUserOnly Verbindungen auf denselben Benutzer und dieselbe Elevationsstufe. Das ist kein Beispiel dafür, einen Dienst und eine UI unter verschiedenen Konten zu verbinden. Dieser Fall braucht den ACL-Entwurf in Abschnitt 5.2.11
// C#: Gerüst eines Servers, der mehrere Clients entgegennimmt
while (!token.IsCancellationRequested)
{
var server = new NamedPipeServerStream(
"MyCompany.MyApp.Control",
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Message,
PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);
try
{
await server.WaitForConnectionAsync(token);
}
catch
{
await server.DisposeAsync(); // vor der Verbindung selbst freigeben
throw;
}
_ = HandleClientAsync(server, token); // nach dem Verbinden geht der Besitz an den Handler
}
Tritt vor einer Verbindung eine Ausnahme auf, gibt die Annahmeseite den Stream frei; nach einer Verbindung übernimmt HandleClientAsync den Besitz des Streams. Dieser Besitz umfasst nicht nur das Lesen und Schreiben, sondern auch das Freigeben am Ende.
Dieser Code ist ein Gerüst, das Kommunikationsprotokoll und Handlerkörper weglässt. Im Betrieb verwalten Sie Ausnahmen und Abschluss der abgetrennten Tasks und kombinieren ihn mit dem Nachlesen und der Größengrenze aus Kapitel 3, der Sicherheit aus den Kapiteln 5 und 6 sowie der Trennungsbehandlung und Antwortbestätigung aus Kapitel 7. CurrentUserOnly ist eine bequeme Option, Verbindungspartner einzugrenzen, ohne selbst eine ACL zu schreiben, aber allein vervollständigt sie keinen Entwurf für einen privilegierten Dienst.
5. Sicherheit: drei Punkte auf der Serverseite und einer auf der Clientseite
Die Sicherheitsvorteile von Named Pipes entstehen erst, wenn sie richtig konfiguriert sind. Besonders in dem Broker-Entwurf „ein Dienst mit Administratorrechten plus eine UI-App mit geringen Rechten“ ist die Pipe die Rechtegrenze.
5.1 Serverseite: unnötige Remote-Verbindungen ablehnen
Wenn Sie die Pipe als lokale IPC nutzen, geben Sie in CreateNamedPipe PIPE_REJECT_REMOTE_CLIENTS an. Remote-Clients werden automatisch abgelehnt, was den Weg schließt, auf dem eine Pipe, die Sie für Apps auf demselben PC gedacht haben, auch vom Netz geöffnet werden kann.7
5.2 Serverseite: Verbindungspartner und Rechte mit einer ACL eingrenzen
Übergeben Sie in SECURITY_ATTRIBUTES einen Sicherheitsdeskriptor und machen Sie ausdrücklich, welche Benutzer und Gruppen sich verbinden dürfen. Die Standard-ACL ist für Ihren Einsatz nicht unbedingt streng genug.2
Was hier leicht übersehen wird, ist das Schreibrecht, das Sie Clients geben. Gewährt die ACL generisches Schreiben (GENERIC_WRITE / FILE_GENERIC_WRITE), enthält das das Recht, das FILE_CREATE_PIPE_INSTANCE entspricht. Ein berechtigter Client kann dann selbst eine Serverinstanz desselben Namens anlegen und nachfolgende Verbindungen abfangen.2
Geben Sie Lesen und Schreiben als die einzelnen benötigten Rechte und überlassen Sie Clients nicht das Recht, Instanzen anzulegen. ACL-Entwurf umfasst nicht nur „wer darf“, sondern auch „was darf“.
5.3 Serverseite: prüfen, dass die erste Instanz nicht vorab beansprucht wurde
Legt ein bösartiger Prozess zuerst eine Pipe desselben Namens an, verbinden sich Clients mit diesem gefälschten Server. Der Server gibt FILE_FLAG_FIRST_PIPE_INSTANCE nur beim Anlegen der ersten Instanz an und garantiert damit, dass er der Erste ist. Schlägt das fehl, vermuten Sie Vorabbanspruchung und halten Sie an.5
Dieses Flag gilt nur für die erste Instanz, die den Namen sichert. Setzen Sie es auch auf die zweite und spätere Instanzen, schlägt das Anlegen fehl. Wiederholen Sie in einer Annahmeschleife für mehrere Clients dieselbe Angabe nicht bei jedem Anlegen.5
Das ist allerdings ein Mechanismus, mit dem der legitime Server beim Start eine Anomalie erkennt, keine Authentifizierung des Verbindungsziels durch den Client. Ist der legitime Dienst abwesend, bleibt einem Angreifer Raum, zuerst eine Pipe desselben Namens anzulegen und zu warten.
5.4 Clientseite: festlegen, wie weit der Server Rechte ausleihen darf
Als Schutz vor einem gefälschten Server hält der Client die Impersonationsstufe auf dem nötigen Minimum. Erlaubt der Entwurf nur Identitätsprüfung, geben Sie in CreateFile SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION an. Der Server kann dann das Konto prüfen, aber dessen Rechte nicht mehr ausleihen, um echten Zugriff auszuführen.3
| Was der Server tun soll | Richtlinie der Clientseite |
|---|---|
| Nur die Identität des Verbindungspartners prüfen | Auf Identifikationsstufe (SECURITY_IDENTIFICATION) eingrenzen |
| Echten Zugriff ausführen, etwa Dateien mit den Rechten des Clients öffnen | Impersonationsstufe (SECURITY_IMPERSONATION) muss erlaubt sein. Koppeln Sie das mit der Prüfung, dass Sie mit dem legitimen Server verbunden sind |
Auf Identifikationsstufe ist Broker-Verarbeitung, die echten Zugriff mit den Rechten des Clients ausführt, nicht möglich. Umgekehrt riskiert Identitätswechsel ohne Prüfung des Verbindungsziels, dass ein gefälschter Server Ihre Rechte ausleiht.
Der Grundsatz ist, den nötigen Identitätswechsel nur zu erlauben, wenn Sie das legitime Verbindungsziel bestätigen können, durch einen garantierten Dienststart oder gegenseitige Authentifizierung nach dem Verbinden. Gehen Sie nicht davon aus, dass die Prüfung auf der Clientseite unnötig ist, weil Abschnitt 5.3 Vorabbanspruchung erkennt.
6. Identitätswechsel nutzen: eine fehlgeschlagene Anfrage nicht ausführen
6.1 Nicht nur nach dem Verbinden, sondern nach dem Lesen der Anfrage identitätswechseln
Die API, mit der der Server die Identität des Clients prüft und mit dessen Rechten verarbeitet, ist ImpersonateNamedPipeClient. Ihr Gegenstand ist der Sicherheitskontext des Absenders der letzten von dieser Pipe gelesenen Nachricht. Lesen Sie zuerst die Anfrage, dann rufen Sie sie auf.4
Identitätswechsel gilt für den aufrufenden Thread. Öffnen Sie in diesem Zustand eine Datei, wird die Zugriffserlaubnis an den Rechten des Clients gemessen. Auch für einen privilegierten Dienst ist das der Mechanismus, „die angeforderte Operation mit den Rechten des Anforderers auszuführen“.3
6.2 Lesen, Erfolgsprüfung und Zurücksetzen als eine Einheit behandeln
Die erforderliche Reihenfolge ist Anfrage lesen, Identitätswechsel versuchen, Erfolg bestätigen, mit den Rechten des Clients arbeiten, in den ursprünglichen Kontext zurückkehren.
sequenceDiagram
accTitle: Ablauf der Anfragebehandlung mit Identitätswechsel
accDescr: Der Server liest eine Anfrage von der Pipe, bestätigt den Erfolg von ImpersonateNamedPipeClient, führt dann die Operation mit den Rechten des Clients aus und kehrt mit RevertToSelf in den eigenen Kontext zurück. Schlägt der Identitätswechsel fehl, lehnt er die Anfrage ab, ohne sie auszuführen
participant C as Client
participant S as Server
C->>S: Eine Anfrage senden
S->>S: Die Anfrage lesen
S->>S: ImpersonateNamedPipeClient
Note over S: Bei Fehlschlag ohne Ausführung ablehnen
S->>S: Die Operation mit den Rechten des Clients ausführen
S->>S: Mit RevertToSelf in den ursprünglichen Kontext zurückkehren
S->>C: Das Ergebnis antworten
Abbildung 3: Schlägt der Identitätswechsel fehl, führen Sie die Anfrage nicht aus. Auch bei Erfolg ist die Folge erst fertig, wenn RevertToSelf nach der Arbeit den ursprünglichen Kontext wiederherstellt.
Gehen Sie nicht zur Verarbeitung der Anfrage über, ohne den Rückgabewert von ImpersonateNamedPipeClient zu prüfen. Bei Fehlschlag ist der Thread nicht auf die Rechte des Clients umgeschaltet, und die eigenen Rechte des Serverprozesses werden genutzt. Läuft der Server unter einem privilegierten Konto, lässt er Operationen durch, die dem Client nicht erlaubt sind. Auch Microsofts Dokumentation sagt ausdrücklich, dass bei Fehlschlag die Anfrage des Clients nicht ausgeführt werden darf.4
Wenn die Arbeit erledigt ist, kehren Sie zuverlässig mit RevertToSelf in den ursprünglichen Kontext zurück. Halten Sie sowohl die Erfolgsprüfung als auch die Nachbereitung bereit. Einzelheiten zu Token, Impersonationsstufen und SeImpersonatePrivilege behandelt der Artikel zu Windows-Identitätswechseltoken.
7. Fallstricke der Praxis: unter der Annahme entwerfen, dass die Kommunikation abreißt
7.1 Warten auf den Start und Warten auf eine freie Instanz als unterschiedliche Fehlschläge behandeln
Wenn Sie sich nicht verbinden können, unterscheiden Sie, ob der Server die Pipe noch nicht angelegt hat oder ob alle angelegten Instanzen in Gebrauch sind.69
| Zustand | Aktion des Clients |
|---|---|
| Die Pipe existiert nicht | Den Start des Servers einplanen; kurz warten und erneut versuchen |
ERROR_PIPE_BUSY |
Mit WaitNamedPipe auf eine freie Instanz warten und CreateFile erneut versuchen |
| Verbunden | Zur Kommunikation übergehen. Bei Bedarf auch den Lesemodus aus Abschnitt 3.2 setzen |
flowchart TB
accTitle: Wiederholungsablauf der Clientverbindung
accDescr: Die Pipe mit CreateFile öffnen; existiert sie nicht, kurz warten und erneut versuchen; sind alle Instanzen in Gebrauch und kommt ERROR_PIPE_BUSY, mit WaitNamedPipe auf eine freie Instanz warten und dann erneut versuchen; bei Erfolg mit der Kommunikation beginnen
cf["Mit CreateFile öffnen"] --> ok{"Ergebnis?"}
ok -->|"Erfolg"| go["Kommunikation beginnen"]
ok -->|"Pipe existiert nicht"| wait1["Kurz warten (Server nicht gestartet)"]
ok -->|"ERROR_PIPE_BUSY"| wnp["Mit WaitNamedPipe auf eine freie Instanz warten"]
wait1 --> cf
wnp --> cf
Abbildung 4: „Existiert nicht“ und „voll“ sind unterschiedliche Zustände. In beiden Fällen warten Sie zustandsgerecht und versuchen dann erneut zu verbinden.
Auf der Serverseite ist der Grundsatz, mit ConnectNamedPipe zu warten, bevor der Client startet. Startreihenfolge-Wettläufe können trotzdem eintreten, also bauen Sie Wiederholungen auch auf der Clientseite ein.9
7.2 Eine Trennung zum normalen Verarbeitungspfad machen, nicht zum Notfall
Wenn der Gegenprozess endet, schlagen Lesen und Schreiben mit ERROR_BROKEN_PIPE und Ähnlichem fehl. Behandeln Sie das als Alltag der Kommunikation.
Erkennt der Server eine Trennung, löst er die Instanz mit DisconnectNamedPipe und bereitet die nächste Verbindung vor. Der Client verbindet erneut. Die Idee der „idempotenten Wiederverbindung“, die den Zustand unabhängig von der Wiederholungshäufigkeit nicht zerstört, erklärt im Artikel zum Fortsetzen aus dem Schlaf, gilt auch hier.
7.3 Große Nachrichten brauchen beides: Nachlesen und eine Obergrenze
Die Behandlung von ERROR_MORE_DATA in Abschnitt 3.3 ist die Logik, eine Nachricht stückweise zu lesen. Die maximale Nachrichtengröße ist dagegen eine Regel, die Menge der angenommenen Daten zu begrenzen. Verwechseln Sie die beiden nicht.
Sendet ein bösartiges oder fehlerhaftes Gegenüber eine riesige Nachricht, verschwendet eine Umsetzung, die den Rest ohne Grenze nachliest, Speicher. Nehmen Sie die Richtlinie an, eine Grenze im Protokoll festzulegen und bei Überschreitung zu trennen.
7.4 Einen erfolgreichen Schreibvorgang vom Abschluss der Verarbeitung auf der Gegenseite unterscheiden
Der Erfolg von WriteFile bedeutet nicht, dass die App der Gegenseite die Daten verarbeitet hat. Bei Operationen, bei denen Sie wissen müssen, dass sie tatsächlich ausgeführt wurden, bestätigen Sie das mit einer Antwortnachricht.
Nehmen Sie außerdem die Beziehung zwischen Anfragen und Antworten ins Protokoll auf, damit jede Antwort der Anfrage zugeordnet werden kann, auf die sie antwortet. „Es wurde gesendet“ von „die Verarbeitung ist fertig“ zu trennen führt zur Abschlussbestätigung im Geschäftssinn.
8. Zusammenfassung: Kanal, Daten und Rechte getrennt festlegen
Eine Named Pipe verbindet die Handhabbarkeit von Datei-I/O mit dem Windows-Sicherheitsmodell aus ACL und Identitätswechsel und ist der erste Kandidat für IPC auf demselben Rechner. Einen Kanal anzulegen ist jedoch etwas anderes, als ein sicheres, schwer zu zerbrechendes Protokoll zu bauen.
Beim Entwurf von Verbindung und Daten gehen Sie davon aus, dass eine Instanz einen Client bedient. Wählen Sie den Nachrichtenmodus für Anfrage und Antwort und den Byte-Modus, wenn Sie bereits Framing haben, und setzen Sie den Nachrichtenlesemodus auch auf der Clientseite. Die Behandlung geteilter Lesungen und eine Maximalgröße sind ebenfalls nötig. Bei einer neuen .NET-Umsetzung, die mehrere Verbindungen annimmt, sind asynchrones I/O und async/await der geradlinige Weg.
Beim Entwurf der Rechte behandeln Sie Remote-Ablehnung, eine ausdrückliche ACL, die Garantie der ersten Instanz und die Minimierung der Impersonationsstufe auf der Clientseite als Satz. Nutzen Sie Identitätswechsel, rufen Sie ihn nach dem Lesen der Anfrage auf, führen Sie bei Fehlschlag nicht aus und kehren Sie nach der Arbeit mit RevertToSelf zurück.
Schreiben Sie schließlich Startreihenfolge, Trennung, Nachrichtenobergrenze und Antwortbestätigung als Ihr eigenes Protokoll auf. Der praktische Punkt ist, vor dem Implementieren eine Form festzulegen, die bei abreißender Kommunikation wiederherstellen kann, ohne die Rechte des Gegenübers zu überschreiten, statt anzunehmen, „es ist auf demselben PC, also schlägt es nicht fehl“.
Weiterführende Artikel
- Windows-Prozesskommunikation richtig wählen ── Eine Entscheidungstabelle für Named Pipes / TCP / gRPC / Shared Memory / COM
- Wie man in einer Windows-App konkret nur die „Vorgänge, die Administratorrechte brauchen“ isoliert
- Windows-Identitätswechseltoken richtig handhaben ── Rechte pro Thread ausleihen und sicher zurückgeben
- Die Tiefen von Windows I/O (Teil 2) — Synchrones und asynchrones I/O: Was OVERLAPPED wirklich bedeutet
- Fallstricke bei Shared Memory und Best Practices für die Praxis
Zugehörige Beratungsfelder
KomuraSoft LLC übernimmt Entwurf und Umsetzung von Systemen mit Prozesskommunikation, etwa die Trennung von Dienst und UI-App und die Isolierung von Administratorrechten; die Ersetzung bestehender IPC (Shared Memory, selbstgebaute Sockets, COM und so weiter) durch Named Pipes; sowie Sicherheitsreviews der Pipe-Kommunikation eines privilegierten Dienstes. Sie können uns auch dann ansprechen, wenn Sie nur einen Protokollentwurf durchsprechen wollen.
- Windows-Anwendungsentwicklung
- Technische Beratung und Design-Review
- Fehleruntersuchung und Ursachenanalyse
- Kontakt
Quellen
-
Microsoft Learn, Named Pipes. Dazu, dass eine Named Pipe ein einseitiger oder bidirektionaler Kanal zwischen einem Pipe-Server und einem oder mehreren Pipe-Clients ist; dazu, dass alle Instanzen denselben Namen teilen, aber unabhängige Puffer und Handles haben; sowie zur Nutzung von lokalen und Remote-Prozessen. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Named Pipe Security and Access Rights. Zur Zusammensetzung der Zugriffsrechte von Named Pipes; dazu, dass GENERIC_WRITE FILE_CREATE_PIPE_INSTANCE enthält, sodass generisches Schreiben an einen Client auch das Anlegen einer Serverinstanz erlaubt; sowie dazu, dass Lesen und Schreiben von Daten als einzelne Zugriffsrechte gewährt werden sollten. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Impersonating a Named Pipe Client. Dazu, dass Identitätswechsel den Serverthread im Rahmen der Rechte des Clients arbeiten lässt; dazu, dass die Standard-Impersonationsstufe SecurityImpersonation ist; sowie dazu, dass der Client die Impersonationsstufe mit dem Flag SECURITY_SQOS_PRESENT bei CreateFile steuern kann (SECURITY_IDENTIFICATION erlaubt nur Identifikation). ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, ImpersonateNamedPipeClient function (namedpipeapi.h). Dazu, dass ein Serverthread den Identitätswechsel im Sicherheitskontext des Clients der letzten von der Pipe gelesenen Nachricht beginnt; zur Rückkehr mit RevertToSelf nach Abschluss; sowie dazu, dass Fortsetzen nach fehlgeschlagenem Identitätswechsel die Ausführung im eigenen (privilegierten) Kontext des Serverprozesses bewirkt, sodass der Rückgabewert immer geprüft und bei Fehlschlag die Anfrage des Clients nicht ausgeführt werden darf. ↩ ↩2 ↩3
-
Microsoft Learn, CreateNamedPipeW function (namedpipeapi.h). Zur Piperichtung (Inbound, Outbound, bidirektional), zum Byte-Typ und Nachrichtentyp (PIPE_TYPE_BYTE / PIPE_TYPE_MESSAGE) und zum Lesemodus (PIPE_READMODE_MESSAGE), zur maximalen Instanzzahl (PIPE_UNLIMITED_INSTANCES), zum asynchronen Modus über FILE_FLAG_OVERLAPPED, zur Garantie der ersten Instanz über FILE_FLAG_FIRST_PIPE_INSTANCE und zum Standardtimeout von WaitNamedPipe. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Named Pipe Client. Dazu, dass der Client die Pipe mit CreateFile öffnet; zu ERROR_PIPE_BUSY, wenn alle Instanzen in Gebrauch sind, und zum Warten auf eine freie mit WaitNamedPipe; sowie dazu, dass das geöffnete Handle standardmäßig Byte-Lesen, blockierend und nicht overlapped ist und mit SetNamedPipeHandleState in den Nachrichtenlesemodus geändert werden kann. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, CreateNamedPipeW function (namedpipeapi.h). Zu den zwei Remote-Client-Modi PIPE_ACCEPT_REMOTE_CLIENTS (Remote-Verbindungen annehmen und gegen den Sicherheitsdeskriptor prüfen) und PIPE_REJECT_REMOTE_CLIENTS (Verbindungen von Remote-Clients automatisch ablehnen). ↩ ↩2
-
Microsoft Learn, How to: Use Named Pipes for Network Interprocess Communication (.NET). Zum Verbinden und Lesen/Schreiben mit NamedPipeServerStream / NamedPipeClientStream, zur Übertragung in Nachrichteneinheiten über PipeTransmissionMode.Message und zur Behandlung mehrerer Clients mit asynchronen Methoden. ↩ ↩2
-
Microsoft Learn, Named Pipe Operations. Zu Overlapped-Operationen über ReadFileEx / WriteFileEx, zu einem nicht verbrauchenden Lesen über PeekNamedPipe, zu TransactNamedPipe, das auf einer bidirektionalen Pipe vom Nachrichtentyp Anfrage sendet und Antwort in einem Aufruf empfängt, sowie dazu, dass ein blockierendes Lesen vor dem Start des Clients einen Wettlauf verursachen kann. ↩ ↩2 ↩3
-
Microsoft Learn, Named Pipe Server Using Overlapped I/O. Zum offiziellen Beispiel eines Single-Thread-Servers, der gleichzeitige Verbindungen mit mehreren Clients über Overlapped-Operationen verarbeitet. Zur Struktur, die auf OVERLAPPED-Struktur und Ereignis jeder Instanz mit WaitForMultipleObjects wartet und die Zustandsmaschine der fertigen Instanz fortschaltet, sowie zur Bestätigung des Abschlusses ausstehender I/O mit GetOverlappedResult. ↩
-
Microsoft Learn, PipeOptions Enum (System.IO.Pipes). Zum Aktivieren asynchronen I/O mit Asynchronous und dazu, dass CurrentUserOnly Verbindungen nur mit Prozessen desselben Benutzers (und derselben Elevationsstufe) erlauben kann. ↩
Verwandte Artikel
Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.
Was nach dem Tod des Elternprozesses übrig bleibt — Kindprozesse in einem Job Object halten
Warum SDK-Helfer eine beendete UI überleben und Kamera oder COM-Port behalten. Kindprozesslebensdauer mit Job Object, KillOnJobClose und ...
Warum Argumente zerbrechen — Die Regeln der Windows-Kommandozeilenargumente
Windows übergibt CreateProcess eine einzige Zeichenfolge, die der Empfänger zerlegt. Behandelt die Regeln von CommandLineToArgvW, CRT und...
Win32-Thread-Pool-API — Nebenläufigkeit ohne eigene Threads, mit CreateThreadpoolWork
Häufen sich in nativem Code die CreateThread-Aufrufe? Dieser Artikel erklärt anhand von Primärquellen die in Vista neu gestaltete Win32-T...
DllMain und die Ladersperre — Der wahre Grund, warum man sagt, in der DLL-Initialisierung nichts zu tun
Warum Sie aus DllMain weder LoadLibrary aufrufen noch mit Threads synchronisieren dürfen. Anhand von Primärquellen erklärt dieser Artikel...
Eine Checkliste für den sicheren Umgang mit Kindprozessen in Windows-Apps
Beim sicheren Umgang mit Kindprozessen in Windows-Apps zählt weniger die Start-API als der Besitz des Prozessbaums und das Design der Bee...
Verwandte Themen
Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.
Technische Windows-Themen
Portal zu Windows-Entwicklung, Fehleranalyse und der Nutzung bestehender Assets.
Leistungen zu diesem Thema
Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.
Windows-App-Entwicklung
Geschäftsanwendungen, Geräteintegration und Kommunikationstools von den Anforderungen bis zur Umsetzung.
Häufige Fragen
Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.
- Wie wähle ich zwischen Named Pipes und TCP (einem Localhost-Socket)?
- Für die Prozesskommunikation auf demselben Rechner ist eine Named Pipe der erste Kandidat. Der Grund ist das Sicherheitsmodell. Eine Pipe steuert mit einem Windows-Sicherheitsdeskriptor (ACL) auf Betriebssystemebene, wer sich verbinden darf, und der Server kann mit ImpersonateNamedPipeClient das Windows-Konto des verbindenden Gegenübers prüfen und ausleihen. Das steht im Gegensatz zu einem Localhost-TCP-Port, an den sich jeder verbinden kann, sodass Sie die Identität des Gegenübers mit einer eigenen Authentifizierung feststellen müssen. TCP-basierte Optionen sind dagegen vorteilhaft, wenn später Remote-Kommunikation wahrscheinlich ist, wenn Sie auch mit Prozessen auf anderen Betriebssystemen sprechen oder wenn Sie bestehende Protokollvermögen wie gRPC nutzen wollen. Diese Einschätzung ist auch im Artikel zur Entscheidungstabelle der Prozesskommunikation ausgearbeitet.
- Soll ich den Byte-Modus oder den Nachrichtenmodus verwenden?
- Wenn Sie einen Schreibvorgang als eine Bedeutungseinheit behandeln wollen, ist der Nachrichtenmodus (PIPE_TYPE_MESSAGE + PIPE_READMODE_MESSAGE) bequem. Der Empfänger kann in den Einheiten lesen, die der Sender geschrieben hat, sodass Sie die Grenzen nicht selbst verwalten müssen. Der Byte-Modus ist ein ununterbrochener Bytestrom wie TCP, und Sie müssen das Framing selbst entwerfen, etwa ein Längenpräfix. Wenn Sie ein Protokoll tragen, das bereits Framing hat (zum Beispiel ein serialisiertes Format mit Längenpräfix), ist der Byte-Modus in Ordnung. Eine Einschränkung: Auch im Nachrichtenmodus entsteht eine geteilte Lesung (ERROR_MORE_DATA), wenn der Empfangspuffer kleiner als die Nachricht ist, die Sie trotzdem behandeln müssen. Außerdem ist der Lesemodus eine Einstellung pro Handle, und CreateNamedPipe setzt ihn nur auf der Serverseite. Der Client muss nach CreateFile mit SetNamedPipeHandleState PIPE_READMODE_MESSAGE angeben. In .NET gibt der Server PipeTransmissionMode.Message an, und der Client setzt NamedPipeClientStream.ReadMode nach dem Verbinden auf Message.
- Wie baue ich einen Server, der gleichzeitig mit mehreren Clients spricht?
- Eine Named Pipe kann mehrere Instanzen unter demselben Namen anlegen, und eine Instanz bedient einen Client. Es gibt zwei Formen. Die eine ist ein synchroner Entwurf, der jedem Client einen Thread zuweist; die Umsetzung ist geradlinig, verbraucht aber einen Thread pro Client. Die andere nutzt asynchrones I/O mit FILE_FLAG_OVERLAPPED und lässt eine kleine Zahl von Threads ConnectNamedPipe, ReadFile und WriteFile für jede Instanz bedienen; Microsofts offizielles Beispiel zeigt auch eine Umsetzung, die mehrere Instanzen auf einem einzigen Thread verarbeitet. In .NET können Sie die asynchrone Form mit NamedPipeServerStream.WaitForConnectionAsync und async/await fast so geradlinig schreiben wie die synchrone. Ohne besonderen Grund empfehle ich für neue Umsetzungen die asynchrone .NET-Form.
- Was ist das Minimum, das ich für die Sicherheit von Named Pipes tun sollte?
- Vier Punkte. Erstens: Wenn Remote-Verbindungen nicht nötig sind, geben Sie PIPE_REJECT_REMOTE_CLIENTS an und lehnen Sie Verbindungen über das Netz ausdrücklich ab. Zweitens: Setzen Sie mit SECURITY_ATTRIBUTES eine passende ACL und engen Sie die Benutzer und Gruppen ein, die sich verbinden dürfen (die Standard-ACL ist für manchen Einsatz zu locker). Drittens: Geben Sie beim Anlegen der ersten Instanz FILE_FLAG_FIRST_PIPE_INSTANCE an, damit Sie Namensentführung erkennen, bei der zuerst eine Pipe desselben Namens angelegt wird (dieses Flag nicht auf die zweite und spätere Instanzen setzen). Viertens: Ein Client, der nur will, dass der Server ihn identifiziert, sollte bei CreateFile SECURITY_SQOS_PRESENT|SECURITY_IDENTIFICATION angeben, sodass ein gefälschter Server seine Rechte nicht ausleihen (impersonieren) kann. In einem Broker-Entwurf, in dem der Server unter den Rechten des Clients echten Zugriff ausführt, muss Identitätswechsel erlaubt sein; diese Einschränkung nutzen Sie also je nachdem, ob der Entwurf dem Server das Ausleihen von Rechten gestattet.
- Gibt es Fallstricke bei ImpersonateNamedPipeClient?
- Am wichtigsten ist die Prüfung des Rückgabewerts. Setzen Sie nach fehlgeschlagenem Identitätswechsel fort, laufen nachfolgende Operationen mit den eigenen (oft hohen) Rechten des Serverprozesses, und Operationen, die dem Client nicht erlaubt gewesen wären, gehen durch. Die offizielle Dokumentation sagt ausdrücklich, dass Sie bei Fehlschlag die Anfrage des Clients nicht ausführen dürfen. Außerdem müssen Sie erst etwas lesen und dann aufrufen, weil Identitätswechsel im Kontext der letzten von der Pipe gelesenen Nachricht geschieht, und nach getaner Arbeit zuverlässig mit RevertToSelf in den ursprünglichen Kontext zurückkehren. Die Mechanik rund um Identitätswechsel (Token, Impersonationsstufen, SeImpersonatePrivilege) ist ausführlich im Artikel zu Identitätswechseltoken behandelt.
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.