(This post is VERY long to cover many aspects but the simple answer is covered in the first paragraph.)
Not a guaranteed answer but tests (shown later) indicate that
type \\.\pipe\testpipe
is opening the pipe twice and failing on the second open since the instance created in class Program
has now been used. CreateFile allows opening a file (inc. devices such as pipes) with a desired access of 0 (not read or write) to obtain certain metadata, including perhaps if it even exists. If type
is doing this then it cannot be used to access a (single instance) named pipe. Perhaps the behaviour of type
changed from Win7 to Win10. (I'll run the following on Win7 one day.)
Proof (well seems like it):
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
class makepipes
{
const int numserv = 8;
static void Main(string[] args)
{
int i;
Thread[] servers = new Thread[numserv];
Console.Write("Waiting for client connection... ");
for (i = 0; i < numserv; i++)
{
servers[i] = new Thread(ServerThread);
servers[i].Start();
}
Thread.Sleep(250);
while (i > 0)
{
for (int j = 0; j < numserv; j++)
{
if (servers[j] != null)
{
if (servers[j].Join(250))
{
Console.WriteLine("Server thread[{0}] finished.", servers[j].ManagedThreadId);
servers[j] = null;
i--; // decrement the thread watch count
}
}
}
}
Console.WriteLine("\nServer threads exhausted, exiting.");
}
private static void ServerThread(object data)
{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.Out, numserv);
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("NamedPipeServerStream object created in {0}.", threadId);
// Wait for a client to connect
pipeServer.WaitForConnection();
Console.WriteLine("Client connected on thread[{0}].", threadId);
try
{
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
sw.WriteLine("Hallo world from {0}!", threadId);
Console.WriteLine("Data was written from {0}.", threadId);
}
}
catch (IOException e)
{
Console.WriteLine("Thread {2} failed {0}: {1}", e.GetType().Name, e.Message, threadId);
}
Console.WriteLine("Pipe closed in {0}.", threadId);
}
}
Giving the following results (note: must use type
in cmd.exe
as PowerShell "type", alias for Get-Content
, uses a FileStream
object which cannot directly access devices or pipes. see very bottom.) Server output bounded by S#...#S, client by C#...#C
S#######
Servers> makepipes
Waiting for client connection... NamedPipeServerStream object created in 3.
NamedPipeServerStream object created in 6.
NamedPipeServerStream object created in 8.
NamedPipeServerStream object created in 10.
NamedPipeServerStream object created in 7.
NamedPipeServerStream object created in 5.
NamedPipeServerStream object created in 9.
NamedPipeServerStream object created in 4.
#######S
C#######
Client>type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[3].
Client connected on thread[10].
Data was written from 10.
Pipe closed in 10.
Server thread[10] finished.
Thread 3 failed IOException: Pipe is broken.
Pipe closed in 3.
Server thread[3] finished.
#######S
C#######
Hallo world from 10!
Client>type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[8].
Client connected on thread[5].
Data was written from 5.
Pipe closed in 5.
Thread 8 failed IOException: Pipe is broken.
Pipe closed in 8.
Server thread[5] finished.
Server thread[8] finished.
#######S
C#######
Hallo world from 5!
Client>type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[9].
Client connected on thread[6].
Data was written from 6.
Pipe closed in 6.
Server thread[6] finished.
Thread 9 failed IOException: Pipe is broken.
Pipe closed in 9.
Server thread[9] finished.
#######S
C#######
Hallo world from 6!
Client>type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[7].
Client connected on thread[4].
Data was written from 4.
Pipe closed in 4.
Thread 7 failed IOException: Pipe is broken.
Pipe closed in 7.
Server thread[7] finished.
Server thread[4] finished.
Server threads exhausted, exiting.
#######S
C#######
Hallo world from 4!
Client>type \\.\pipe\testpipe
The system cannot find the file specified.
#######C
Note the last response. The All pipe instances are busy.
response implies that type
is not closing the first open before attempting the second (or there are some really tight interprocess timing issues). (This looks like a job for ProcessMon!)
So this explains why type
behaves as it does (maybe) but the OP seems to have only been using type
for testing. The stated intention was that a command line application would read from a file name argument which is supplied the name of the pipe. So, as long as it only opens the pipe once, it should work. Some test examples,
S#######
Servers> makepipes
Waiting for client connection... NamedPipeServerStream object created in 5.
NamedPipeServerStream object created in 6.
NamedPipeServerStream object created in 4.
NamedPipeServerStream object created in 7.
NamedPipeServerStream object created in 10.
NamedPipeServerStream object created in 3.
NamedPipeServerStream object created in 8.
NamedPipeServerStream object created in 9.
#######S
C#######
Client>more < \\.\pipe\testpipe
#######C
S#######
Client connected on thread[5].
Data was written from 5.
Pipe closed in 5.
Server thread[5] finished.
#######S
C#######
Hallo world from 5!
Client>more \\.\pipe\testpipe
#######C
S#######
Client connected on thread[7].
Data was written from 7.
Pipe closed in 7.
Server thread[7] finished.
#######S
C#######
Hallo world from 7!
Client>sort \\.\pipe\testpipe
#######C
S#######
Client connected on thread[6].
Data was written from 6.
Pipe closed in 6.
Server thread[6] finished.
#######S
C#######
Hallo world from 6!
Client>findstr world \\.\pipe\testpipe
#######C
S#######
Client connected on thread[4].
Client connected on thread[10].
Thread 10 failed IOException: Pipe is broken.
Pipe closed in 10.
Thread 4 failed IOException: Pipe is broken.
Pipe closed in 4.
Server thread[10] finished.
Server thread[4] finished.
#######S
C####### (no output from findstr)
Client>find "world" \\.\pipe\testpipe
---------- \\.\PIPE\TESTPIPE
#######C
S#######
Client connected on thread[3].
Data was written from 3.
Pipe closed in 3.
Server thread[3] finished.
#######S
C#######
Hallo world from 3!
Unable to read file
Client>copy \\.\pipe\testpipe con:
#######C
S#######
Client connected on thread[9].
Thread 9 failed IOException: Pipe is broken.
Pipe closed in 9.
Server thread[9] finished.
Client connected on thread[8].
Data was written from 8.
Pipe closed in 8.
Server thread[8] finished.
Server threads exhausted, exiting.
#######S
C#######
Hallo world from 8!
The pipe has been ended.
0 file(s) copied.
#######C
So, both findstr
and copy
double open. Also, for copy \\.\pipe\testpipe pipeit
the response is 1 file(s) copied.
(after 4 opens with 3 fails) but pipeit is empty. find
appears to work but does not correctly process the closing of the write end of the pipe. Note how more < \\.\pipe\testpipe
works correctly. This would imply (and has been tested as true) that any command line application that reads StdIn can be connected to a named pipe (using cmd.exe
). Even findstr world < \\.\pipe\testpipe
works and find "world" < \\.\pipe\testpipe
doesn't complain. Redirection in cmd
probably also works for StdOut and StdErr to a reading pipe server (or servers!) but haven't tested this (comments anyone?)
Final confirmation,
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
public class oneopen {
[DllImport("Kernel32.dll",SetLastError=true)]
extern static IntPtr CreateFile(
string lpFileName,
int dwDesiredAccess,
int dwShareMode,
IntPtr lpSecurityAttributes,
int dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile
);
[DllImport("Kernel32.dll",SetLastError=true)]
static extern bool ReadFile(
IntPtr handle,
byte[] bytes,
int numBytesToRead,
out int numBytesRead,
IntPtr overlapped_MustBeZero
);
[DllImport("Kernel32.dll",SetLastError=true)]
static extern bool CloseHandle(IntPtr handle);
public static int Main(string[] args)
{
IntPtr fh, fh2;
byte[] bytes = new byte[1024];
int red;
if (args.Length > 0)
{
if (args.Length > 1)
{
fh2 = CreateFile(args[0], 1, 0, IntPtr.Zero, 3, 0, IntPtr.Zero);
Console.WriteLine("{0} {1}\n", fh2.ToInt64(), Marshal.GetLastWin32Error());
if (args.Length > 2)
{
CloseHandle(fh2); // Don't care if fh == INVALID_HANDLE_VALUE
Thread.Sleep(Int32.Parse(args[2]));
}
}
fh = CreateFile(args[0], 1, 3, IntPtr.Zero, 3, 0, IntPtr.Zero);
Console.WriteLine("{0} {1}\n", fh.ToInt64(), Marshal.GetLastWin32Error());
while (ReadFile(fh, bytes, bytes.Length, out red, IntPtr.Zero) && red > 0)
Console.WriteLine(Encoding.ASCII.GetString(bytes, 0, red));
}
return Marshal.GetLastWin32Error();
}
}
This allows testing of a single open on the pipe, an open/close/(delay)/open and two simultaneous opens. Various testing showed the anticipated results with the one relevant to the OP shown (using original single instance pipe)
S#######
Server> makepipe
NamedPipeServerStream object created.
Waiting for client connection...#######S
C#######
Client> oneopen \\.\pipe\testpipe
#######C
S#######Client connected.
Data was written.
Pipe closed.
#######S
C#######
604 0
Hallo world!
#######C
I know this is a bit TLDR; but I wanted to cover many variations.
Finally, regarding PowerShell as promised, the following test results were obtained
S#######
PS Server> .\makepipes
Waiting for client connection... NamedPipeServerStream object created in 6.
NamedPipeServerStream object created in 7.
NamedPipeServerStream object created in 5.
NamedPipeServerStream object created in 8.
NamedPipeServerStream object created in 9.
NamedPipeServerStream object created in 10.
NamedPipeServerStream object created in 4.
NamedPipeServerStream object created in 3.
#######S
C#######
PS Client> type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[6].
Client connected on thread[7].
Client connected on thread[10].
Client connected on thread[8].
Thread 8 failed IOException: Pipe is broken.
Pipe closed in 8.
Thread 10 failed IOException: Pipe is broken.
Pipe closed in 10.
Thread 6 failed IOException: Pipe is broken.
Pipe closed in 6.
Thread 7 failed IOException: Pipe is broken.
Pipe closed in 7.
Server thread[10] finished.
Server thread[6] finished.
Server thread[7] finished.
Server thread[8] finished.
#######S
C#######
type : FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:',
call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr.
At line:1 char:1
+ type \\.\pipe\testpipe
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Content], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.GetContentCommand
PS Client> type \\.\pipe\testpipe
#######C
S#######
Client connected on thread[9].
Client connected on thread[3].
Thread 9 failed IOException: Pipe is broken.
Pipe closed in 9.
Server thread[9] finished.
Client connected on thread[4].
Thread 3 failed IOException: Pipe is broken.
Pipe closed in 3.
Client connected on thread[5].
Thread 5 failed IOException: Pipe is broken.
Pipe closed in 5.
Server thread[3] finished.
Thread 4 failed IOException: Pipe is broken.
Pipe closed in 4.
Server thread[4] finished.
Server thread[5] finished.
Server threads exhausted, exiting.
#######S
C#######
type : FileStream was asked to open a device that was not a file. For support for devices like 'com1:' or 'lpt1:',
call CreateFile, then use the FileStream constructors that take an OS handle as an IntPtr.
At line:1 char:1
+ type \\.\pipe\testpipe
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Content], NotSupportedException
+ FullyQualifiedErrorId : System.NotSupportedException,Microsoft.PowerShell.Commands.GetContentCommand
PS Client> type \\.\pipe\testpipe
type : Cannot find path '\\.\pipe\testpipe' because it does not exist.
At line:1 char:1
+ type \\.\pipe\testpipe
+ ~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\.\pipe\testpipe:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
#######C
So, not only does PowerShell not like getting content from pipes but it opens (and closes) it 4 times before deciding this!!