NamedPipeClientConnection class
One client-side connection to a named pipe. Produced by NamedPipeClientManager.Connect. Carries the connection-lifecycle events (Connected, Disconnected) and the message events (MessageReceived, MessageSent), plus the AsyncRead / AsyncWrite / AsyncClose methods that trigger them.
The class is tagged [COMCreatable(False)] and its constructor takes a package-private interface — reach instances only through NamedPipeClientManager.Connect.
Important
The package _README.txt states: “you MUST call AsyncClose on the client side, otherwise the connection is left alive when the object goes out of scope”. Either call AsyncClose explicitly before dropping the last reference, or let the object terminate cleanly through its Class_Terminate (which calls AsyncClose automatically). Holding the reference forever — in a module-level Collection, for example — without calling AsyncClose keeps the pipe handle open and the IOCP thread alive.
Private manager As NamedPipeClientManager
Private WithEvents connection As NamedPipeClientConnection
Private Sub Form_Load()
Set manager = New NamedPipeClientManager
Set connection = manager.Connect("MyService")
End Sub
Private Sub connection_Connected()
connection.AsyncWrite StrConv("hello", vbFromUnicode)
End Sub
Private Sub connection_MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)
Debug.Print "reply: " & StrConv(Data, vbUnicode)
End Sub
Private Sub Form_Unload(Cancel As Integer)
connection.AsyncClose
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
CustomData
A per-connection opaque slot the consumer can use to attach arbitrary state to this connection object.
Syntax: connection.CustomData [ = value ]
- value
- Any Variant-compatible value to store with this connection. Default Empty.
The package never reads or writes CustomData. It exists solely as a convenience field so consumer code does not need a separate dictionary or parallel array to correlate a NamedPipeClientConnection reference with application-level state.
Typical uses include a session object, a pending-replies dictionary, a display name, or any other per-connection value the application needs to retrieve when a MessageReceived or Disconnected event fires.
Example
This example attaches a user-defined session object to a connection when it connects, then retrieves it inside the message-received handler.
Private manager As NamedPipeClientManager
Private WithEvents connection As NamedPipeClientConnection
Private Sub OpenConnection(ByVal pipeName As String, ByVal session As ClientSession)
Set manager = New NamedPipeClientManager
Set connection = manager.Connect(pipeName)
Set connection.CustomData = session ' attach session state to this connection
End Sub
Private Sub connection_MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)
Dim session As ClientSession = connection.CustomData
session.HandleReply Data
End Sub
Handle
The underlying Win32 file handle returned by CreateFileW("\\.\pipe\<PipeName>"). LongPtr.
Syntax: connection.Handle
The value is 0 (vbNullPtr) before the asynchronous connection sequence completes and after AsyncClose has been called. Once the Connected event has fired, Handle holds the file handle that the package uses for all subsequent ReadFile and WriteFile calls against this connection.
Warning
Do not call CloseHandle on this value directly. Use AsyncClose instead, so the IOCP loop and the parent manager’s bookkeeping stay consistent. Closing the handle outside the package leaves pending IOCP completions referencing freed memory.
Most consumers do not need this field. It is exposed for low-level diagnostics — for example, passing the handle to GetNamedPipeInfo or other Win32 APIs to inspect pipe state.
PipeName
The leaf pipe name this connection was opened against. String.
Syntax: connection.PipeName
The value is set by the constructor from the serverPipeName argument supplied to NamedPipeClientManager.Connect and is not changed after that. The package uses it internally to compose the Win32 UNC path \\.\pipe\<PipeName> when calling CreateFileW.
Read the field to identify which server a given connection object is bound to — useful when a single form or class manages several NamedPipeClientConnection instances against different pipe names.
Events
Connected
Fires once the asynchronous CreateFileW started by NamedPipeClientManager.Connect has succeeded and the pipe is ready for message exchange.
Syntax: connection_Connected()
Disconnected
Fires once the pipe 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: connection_Disconnected()
MessageReceived
Fires when a complete message has been read from the pipe.
Syntax: connection_MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)
- Cookie
- The opaque correlation value originally passed to the AsyncRead that produced this read — or Empty if the read came from the auto-issued reads triggered by NamedPipeClientManager.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 they are needed later. The recommended capture mechanism is to assign Data to a fresh PropertyBag’s Contents, which deep-copies the bytes and provides typed multi-field access in one step.
MessageSent
Fires when a previously-issued AsyncWrite has completed.
Syntax: connection_MessageSent(ByRef Cookie As Variant)
- Cookie
- The opaque correlation value that was passed to the originating AsyncWrite call.
Methods
AsyncClose
Cancels every outstanding asynchronous I/O against this connection and closes the underlying pipe handle.
Syntax: connection.AsyncClose
Internally, AsyncClose calls CancelIoEx on the pipe handle to abort any pending reads or writes, then calls CloseHandle to release it. Once AsyncClose returns, no further I/O is possible on the connection. The Disconnected event fires asynchronously once every outstanding IOCP completion has been processed and the outstanding-request counter reaches zero.
AsyncClose is called automatically from Class_Terminate when the last reference to the connection object drops, so letting the variable go out of scope is sufficient when the connection is short-lived and no module-level reference is kept.
Important
If the connection object is stored in a long-lived container — a module-level Collection, a class field, or a global variable — Class_Terminate does not run until that container is also released. Call AsyncClose explicitly before dropping the last reference whenever the lifetime is not guaranteed to be short. Failure to do so keeps the underlying pipe handle open and the IOCP worker thread running.
AsyncRead
Manually issues an asynchronous read against this connection.
Syntax: connection.AsyncRead [ Cookie [, OverlappedStruct ] ]
- Cookie
- optional A Variant correlation value, passed back as the Cookie parameter of the matching MessageReceived event. Default Empty.
- OverlappedStruct
- optional A LongPtr to a pre-allocated
OVERLAPPED_CUSTOMstructure. Internal use only — the IOCP machinery passes this when re-issuing a read afterERROR_MORE_DATA. Consumer code should always omit this parameter.
Only needed when the parent manager’s ContinuouslyReadFromPipe is False; otherwise the IOCP loop keeps a read pending automatically and explicit calls are redundant.
AsyncWrite
Sends a message to the server.
Syntax: connection.AsyncWrite Data() [, Cookie ]
- Data
- required A Byte() array containing the bytes to send. An uninitialised or zero-length array is a no-op. For typed multi-field payloads the recommended encoding is PropertyBag — see Recommended payload encoding:
PropertyBagon the package overview. - Cookie
- optional A Variant correlation value, passed back as the Cookie parameter of the matching MessageSent event. Default Empty.
Returns immediately; the actual transmission runs through the IOCP loop. The completion fires MessageSent on this connection.
Warning
The bytes are copied into a per-completion buffer sized at NamedPipeClientManager.MessageBufferSize (default 131072 bytes) without a bounds check. Passing an array longer than that value overruns the buffer. Raise MessageBufferSize above the largest expected message before the first NamedPipeClientManager.Connect call; the value is read once at that point and propagated to every per-connection buffer.
Example
This example connects to a named pipe server, sends a request encoded as a PropertyBag, and prints the reply.
Private manager As NamedPipeClientManager
Private WithEvents connection As NamedPipeClientConnection
Private Sub Form_Load()
Set manager = New NamedPipeClientManager
Set connection = manager.Connect("MyService")
End Sub
Private Sub connection_Connected()
Dim request As New PropertyBag
request.WriteProperty "CommandID", "WHAT_TIME_IS_IT"
connection.AsyncWrite request.Contents, cookie:=42
End Sub
Private Sub connection_MessageSent(ByRef Cookie As Variant)
Debug.Print "request sent (cookie=" & Cookie & ")"
End Sub
Private Sub connection_MessageReceived(ByRef Cookie As Variant, ByRef Data() As Byte)
Dim incoming As New PropertyBag
incoming.Contents = Data ' deep-copies bytes; safe past the handler
Debug.Print "time: " & incoming.ReadProperty("ResponseData")
End Sub
Private Sub Form_Unload(Cancel As Integer)
connection.AsyncClose
End Sub
New
Constructs a connection object in the not-yet-connected state and binds it to the parent manager’s IOCP infrastructure.
Syntax: New NamedPipeClientConnection(server, serverPipeName, serverCompletionPortHandle, serverMessageWindowHandle)
Note
The constructor signature is package-private. The first parameter type (INamedPipeClientManagerInternal) is an internal interface not accessible to consumer code. Do not construct NamedPipeClientConnection directly — obtain instances from NamedPipeClientManager.Connect.
- server
- required An
INamedPipeClientManagerInternalreference to the owning manager. The constructor reads MessageBufferSize, FreeThreadingEvents, and ContinuouslyReadFromPipe from this object, so the values are frozen at construction time. - serverPipeName
- required The leaf pipe name to open, stored as PipeName.
- serverCompletionPortHandle
- required A LongPtr handle to the manager’s I/O Completion Port, used when associating the pipe handle and posting I/O completions.
- serverMessageWindowHandle
- required A LongPtr handle to the manager’s hidden message-only window, used to marshal IOCP-thread events back to the UI thread when FreeThreadingEvents is False.
See Also
- WinNamedPipesLib package – overview, IOCP / event-marshalling architecture, cookie pattern,
Data()lifetime caveat, the AsyncClose rule - Recommended payload encoding:
PropertyBag– the deep-copy capture pattern for transient Data in events - NamedPipeClientManager class – the manager that produced this connection
- NamedPipeServerConnection class – the server-side counterpart