Hello @Simon Huntington ,
I read the pages you listed plus the two Old New Thing posts that apply, and ran a native test with your exact flags (byte mode, inbound, overlapped, one instance, one outstanding read, sync client writes) on Windows 11 build 26200.
For your first question, the split is supported. The Named Pipe Server Using Overlapped I/O page says in prose, not just in the sample: "If the operation has already finished when ReadFile ... returns, the function's return value indicates the result. For read and write operations, the number of bytes transferred is also returned. If the operation is still pending ... use the GetOverlappedResult function." The NULL advice on ReadFile is explained in the Old New Thing post you could not open: it is a race when the same variable receives both ReadFile's count and the completion's count. Raymond Chen: "it's okay to pass a non-null lpNumberOfBytesRead ... provided that you do so into a different variable from the one that the completion routine uses." Your separate storage removes exactly that hazard; on ERROR_IO_PENDING just ignore the initiating variable (ReadFile "sets this value to zero before doing any work"). If you prefer NULL, this 2014 post confirms a synchronously completed overlapped I/O still signals hEvent and GetOverlappedResult "will merely return immediately", so ReadFile(NULL) TRUE followed by GetOverlappedResult(h, &ov, &n, FALSE) works. Note the ReadFile page says "Windows 7: This parameter can not be NULL." My test matched all of this: on immediate TRUE the variable, GetOverlappedResult and InternalHigh agreed; on the pending path the variable was zeroed and never written again.
For your second question, the documented terminal signal is on the same server page: "the signal is the error generated by trying to read from the pipe after the pipe client closes its handle." Named Pipe Operations gives the last-reference rule, "The pipe exists as long as a server or client process has an open handle to the pipe", and, with DisconnectNamedPipe, states that "any unread data in the pipe is discarded" on disconnect. The ERROR_BROKEN_PIPE sentence on the ReadFile page is written for anonymous pipes only: "If an anonymous pipe is being used and the write handle has been closed ... GetLastError returns ERROR_BROKEN_PIPE."
I want to be direct: I found no Microsoft page that guarantees a named-pipe reader receives every byte written before the last client handle closed and only then gets ERROR_BROKEN_PIPE. The pieces above point that way, but none is phrased as a contract for byte-mode named pipes. If your spec needs a normative citation for that sentence, it does not exist today; the behavior below is observed, not guaranteed.
What I observed on build 26200: 1 MB streamed through a 4 KB buffer, writer closes, server gets every byte (checksum verified) then ERROR_BROKEN_PIPE (109), from GetOverlappedResult if a read was pending or directly from ReadFile if not, 10 of 10 runs. 3 KB written and closed before any read gave the same result, so buffered data is not lost on CloseHandle. A duplicated writer handle left open kept the read pending forever with PeekNamedPipe showing 0 bytes, indistinguishable from idle, until the duplicate closed; no server-side result detects a leaked writer reference. A zero-byte WriteFile on a byte-mode pipe was a no-op, so a 0-byte read does not occur here. DisconnectNamedPipe with 500 unread bytes gave ERROR_PIPE_NOT_CONNECTED (233) and lost them, as documented. CancelIoEx gave ERROR_OPERATION_ABORTED (995) and later data was still readable.
So the predicate is a read on the original handle failing with ERROR_BROKEN_PIPE after earlier reads returned all queued bytes, in a path that never disconnected, cancelled or closed. It separates genuine close from cancel (995) and from server disconnect (233). It cannot separate "all writer references closed" from "a writer still open but idle"; that rests on your handle accounting, not on any I/O result.
Hope these information help. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide, as it would also help others with the same question.
Thank you.