NamedPipeServer class
Hosts one named pipe and accepts an unbounded number of concurrent client connections, each represented by a NamedPipeServerConnection. The class owns a Windows I/O Completion Port and a configurable pool of worker threads that drive every connection’s reads, writes, and connect notifications. Instantiate with New.
Configure the public fields (PipeName is required, the others have sensible defaults), call Start, and respond to the lifecycle events as clients arrive and exchange messages. The package opens the underlying pipe as PIPE_TYPE_MESSAGE / PIPE_READMODE_MESSAGE — message boundaries on the wire match message boundaries seen by the consumer.
Private WithEvents server As NamedPipeServer
Private Sub Form_Load()
Set server = New NamedPipeServer
server.PipeName = "MyService"
server.Start
End Sub
Private Sub server_ClientConnected(Connection As NamedPipeServerConnection)
Debug.Print "client " & Connection.Handle & " arrived"
End Sub
Private Sub server_ClientMessageReceived( _
Connection As NamedPipeServerConnection, _
ByRef Cookie As Variant, _
ByRef Data() As Byte)
Connection.AsyncWrite Data ' echo it back
End Sub
See the package overview for the IOCP / event-marshalling architecture, the cookie correlation pattern, and the transient lifetime of Data() As Byte inside events.
Properties
ContinuouslyReadFromPipe
When True (the default), the server keeps a read pending against every connected client at all times — every ClientMessageReceived is followed by an automatic AsyncRead issued from inside the IOCP thread. Set to False to handle reads one-at-a-time; each ClientMessageReceived handler must then call NamedPipeServerConnection.AsyncRead to receive the next message. Boolean, default True.
FreeThreadingEvents
Controls where the lifecycle and message events are raised. When False (the default), the IOCP worker threads marshal each event to the main UI thread through a hidden message-only window, and the consuming process must be pumping a Win32 message loop. When True, events fire directly on whichever IOCP worker thread received the completion — no message-loop dependency, but the consumer’s event handlers must be thread-safe. Boolean, default False.
Set this before calling Start; it is read once when the worker threads are created and propagated to every NamedPipeServerConnection.
MessageBufferSize
The size, in bytes, of the per-completion ReadFile buffer initially allocated for each connection. Long, default 131072 (128 KiB). Does not cap the maximum message size — on ERROR_MORE_DATA the IOCP loop allocates a larger overflow buffer and re-issues the read — but the initial size affects how often that overflow path runs, and so affects throughput for sustained large-message traffic.
NumThreadsIOCP
The number of IOCP worker threads created by Start. Long, default 1. One thread is enough for most scenarios because every blocking call inside the worker is an overlapped Win32 operation that releases the thread immediately. Raise this to allow multiple ClientMessageReceived handlers to run concurrently under FreeThreadingEvents = True, or to keep up with heavy traffic on multi-core hardware. Set this before calling Start.
PipeName
The name the pipe is published under. String, no default. The Win32 pipe namespace path is \\.\pipe\<PipeName> — the package prepends \\.\pipe\ itself; pass just the leaf name.
Important
PipeName must be set to a non-empty value before Start, or Start raises run-time error 5 (“cannot start without specifying a pipe name”).
Events
ClientConnected
Fires after a client’s ConnectNamedPipe has completed and the connection is ready for message exchange.
Syntax: server_ClientConnected(Connection As NamedPipeServerConnection)
- Connection
- The newly-connected client’s server-side connection object. Hold the reference if you need per-client state across messages — the same instance is passed to every event for this client. Note that Cookie /
Tag-style storage is available through NamedPipeServerConnection.CustomData.
ClientDisconnected
Fires once the client has dropped and every outstanding asynchronous I/O against the connection has returned. The connection object is no longer usable for I/O after this event.
Syntax: server_ClientDisconnected(Connection As NamedPipeServerConnection)
- Connection
- The connection that has just shut down. Its IsConnected is False.
ClientMessageReceived
Fires when a complete message has been read from the pipe.
Syntax: server_ClientMessageReceived(Connection As NamedPipeServerConnection, ByRef Cookie As Variant, ByRef Data() As Byte)
- Connection
- The connection the message came from.
- Cookie
- The opaque correlation value originally passed to the NamedPipeServerConnection.AsyncRead that produced this read — or Empty if the read came from the auto-issued reads driven by ContinuouslyReadFromPipe.
- Data
- The message payload. See Working with
Data() As Bytein events on the package overview for the transient-buffer lifetime caveat — copy the bytes out before the handler returns if you need them.
ClientMessageSent
Fires when a previously-issued NamedPipeServerConnection.AsyncWrite has completed (or when an AsyncBroadcast message reaches each individual client).
Syntax: server_ClientMessageSent(Connection As NamedPipeServerConnection, ByRef Cookie As Variant)
- Connection
- The connection the write went out on.
- Cookie
- The opaque correlation value that was passed to the originating AsyncWrite call.
ServerReady
Fires once, after Start, when every IOCP worker thread has joined the completion-port loop and the first connection listener is published. Use this as the “the server is now accepting connections” signal.
Syntax: server_ServerReady()
Methods
AsyncBroadcast
Issues an AsyncWrite against every currently-connected client.
Syntax: server.AsyncBroadcast Data() [, Cookie ]
- Data
- required The message bytes to send.
- Cookie
- optional A Variant correlation value, attached to each per-client ClientMessageSent event. Default Empty.
The set of recipients is snapshotted under a lock at the start of the call. Clients connecting after the snapshot do not receive this broadcast; clients disconnecting after the snapshot but before their per-client write completes simply fail that individual write silently.
ManualMessageLoopEnter
Runs a Win32 message loop on the calling thread until ManualMessageLoopLeave is called from another thread (or any handler raises a WM_USER_QUITTING posting).
Syntax: server.ManualMessageLoopEnter
Intended for console / service hosts that do not have a Forms-style message pump of their own but want the default (FreeThreadingEvents = False) marshalled-event semantics. UI hosts already pump messages naturally and do not need this method.
ManualMessageLoopLeave
Posts a WM_USER_QUITTING message to the hidden marshalling window, causing the ManualMessageLoopEnter loop on the other thread to break out cleanly. Safe to call from any thread.
Syntax: server.ManualMessageLoopLeave
Start
Creates the I/O Completion Port, spins up NumThreadsIOCP worker threads, and publishes the first connection listener under \\.\pipe\<PipeName>. Fires ServerReady when every worker has joined.
Syntax: server.Start
Raises run-time error 5 “cannot start without specifying a pipe name” if PipeName is empty, or “unable to create an IOCP port” if CreateIoCompletionPort fails.
Idempotent: calling Start while the server is already running is a no-op.
Stop
Cancels every outstanding I/O on every connection, posts the IOCP shutdown sentinel to each worker, waits for the threads to exit, closes every pipe handle, and frees the completion port. Idempotent: calling Stop on a server that has not been started — or has already been stopped — is a no-op. Automatically invoked from Class_Terminate, so a server going out of scope cleans up implicitly.
Syntax: server.Stop
New
Constructs a server in the not-yet-started state. Creates the hidden STATIC-class message window used to marshal IOCP-thread completions back to the UI thread.
Syntax: New NamedPipeServer
See Also
- WinNamedPipesLib package – overview, IOCP / event-marshalling architecture, cookie pattern,
Data()lifetime caveat, known limitations - NamedPipeServerConnection class – the per-client connection passed to every event
- NamedPipeClientManager class – the client-side counterpart