Skip to content

[API Proposal]: Add PipeOptions.FirstPipeInstance enum value #76984

@JamesNK

Description

@JamesNK

Background and motivation

It's not possible for an app hosting multiple named pipe connections to verify that no one else is using the pipe name when it starts.

For example, Kestrel is looking at adding a named pipe transport, and when we start Kestrel we want to verify that no one else is using the configured pipe name:

  • We don't want to share the pipe between Kestrel instances. It's common for devs to accidentally launch Kestrel when it's already running. A socket errors if you bind to it when it's already open. We want the same protection with a named pipe.
  • A pipe gets many of its settings when it is first opened. Want to verify that Kestrel's settings are applied to the pipe.

Named pipes have a built-in option for checking this - FILE_FLAG_FIRST_PIPE_INSTANCE. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea

.NET doesn't expose this option. While it is automatically set if the max number of pipe instances is 1, there is no way to explicitly set this option.

int openMode = ((int)direction) |
(maxNumberOfServerInstances == 1 ? Interop.Kernel32.FileOperations.FILE_FLAG_FIRST_PIPE_INSTANCE : 0) |
(int)options |
(int)additionalAccessRights;
.

If the PipeOptions.FirstPipeInstance options was available, then Kestrel would create the first NamedPipeServerStream with it the option to verify that there is no existing pipe with that name and create subsequent NamedPipeServerStream instances without the option.

Note that validation on the NamedPipeServerStream ctor currently prevents the enum value having FILE_FLAG_FIRST_PIPE_INSTANCE value added to it. e.g.

// NamedPipeServerStream errors when passed this pipe option
pipeOptions |= (PipeOptions)0x00080000;

API Proposal

namespace System.IO.Pipes
{
    [Flags]
    public enum PipeOptions
    {
        FirstPipeInstance = unchecked((int)0x00080000)
    }
}

API Usage

private NamedPipeServerStream CreateServerStream(bool firstStream)
{
    var pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
    if (_options.CurrentUserOnly)
    {
        pipeOptions |= PipeOptions.CurrentUserOnly;
    }
    if (firstStream)
    {
        pipeOptions |= PipeOptions.FirstPipeInstance;
    }

    return new NamedPipeServerStream(
        _endpoint.PipeName,
        PipeDirection.InOut,
        NamedPipeServerStream.MaxAllowedServerInstances,
        PipeTransmissionMode.Byte,
        pipeOptions,
        inBufferSize: 0,
        outBufferSize: 0);
}

Alternative Designs

[Flags]
public enum PipeOptions
{
    EnsureFirstPipeInstance = unchecked((int)0x00080000)
}

It still alludes at the name of the Windows API flag and conveys "throw if already exists".

Risks

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions