这篇文章非常长,覆盖了许多方面,但简单的答案在第一段中已经涵盖。
虽然不能保证结果,但测试(稍后展示)表明
type \\.\pipe\testpipe
打开管道
两次,第二次打开失败,因为在
class Program
中创建的实例已经被使用。
CreateFile允许以所需访问级别0(不读取或写入)打开文件(包括管道等设备),以获取某些元数据,包括可能是否存在。如果
type
正在执行此操作,则无法用于访问(单个实例)命名管道。也许
type
的行为从Win7到Win10发生了变化。(有一天我会在Win7上运行以下内容。)
证明(好像是这样的):
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--;
}
}
}
}
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);
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);
}
}
给出以下结果(注意:必须在
cmd.exe
中使用
type
,而 PowerShell 的 "type" 是
Get-Content
的别名,使用
FileStream
对象,无法直接访问设备或管道。请参见底部。)服务器输出由 S#...#S 限定,客户端由 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.
C
Client>type \\.\pipe\testpipe
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.
C
Hallo world from 10!
Client>type \\.\pipe\testpipe
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.
C
Hallo world from 5!
Client>type \\.\pipe\testpipe
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.
C
Hallo world from 6!
Client>type \\.\pipe\testpipe
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.
C
Hallo world from 4!
Client>type \\.\pipe\testpipe
The system cannot find the file specified.
请注意最后一条响应。"
All pipe instances are busy.
"的响应意味着在尝试第二次打开之前,
type
没有关闭第一个打开的管道(或者存在一些非常紧密的进程间定时问题)。 (这看起来就像是
ProcessMon 的工作!)
因此,这解释了为什么
type
表现出它所表现的方式(也许),但OP似乎只是在使用
type
进行测试。声明的意图是
命令行应用程序将从提供管道名称的文件名参数中读取。因此,只要它只打开管道一次,它就应该可以工作。一些测试示例:
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.
C
Client>more < \\.\pipe\testpipe
S
Client connected on thread[5].
Data was written from 5.
Pipe closed in 5.
Server thread[5] finished.
C
Hallo world from 5!
Client>more \\.\pipe\testpipe
S
Client connected on thread[7].
Data was written from 7.
Pipe closed in 7.
Server thread[7] finished.
C
Hallo world from 7!
Client>sort \\.\pipe\testpipe
S
Client connected on thread[6].
Data was written from 6.
Pipe closed in 6.
Server thread[6] finished.
C
Hallo world from 6!
Client>findstr world \\.\pipe\testpipe
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.
C
Client>find "world" \\.\pipe\testpipe
---------- \\.\PIPE\TESTPIPE
S
Client connected on thread[3].
Data was written from 3.
Pipe closed in 3.
Server thread[3] finished.
C
Hallo world from 3!
Unable to read file
Client>copy \\.\pipe\testpipe con:
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.
C
Hallo world from 8!
The pipe has been ended.
0 file(s) copied.
因此,findstr
和copy
都有双重打开。对于copy \\.\pipe\testpipe pipeit
,响应为1 file(s) copied。
(经过4次打开,其中3次失败),但是pipeit为空。 看起来find
可以工作,但无法正确处理管道的写端关闭。请注意,more < \\.\pipe\testpipe
可以正常工作。这意味着(已经测试为真)任何读取StdIn的命令行应用程序都可以连接到命名管道(使用cmd.exe
)。即使findstr world < \\.\pipe\testpipe
也可以工作,并且find "world" < \\.\pipe\testpipe
没有抱怨。在cmd
中,重定向可能也适用于将StdOut和StdErr发送到读取管道服务器(或多个服务器!),但尚未测试(有评论吗?)
最后确认,
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);
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();
}
}
这允许测试管道上的单个打开,打开/关闭/(延迟)/打开和两个同时打开。各种测试显示了预期的结果,其中一个与OP相关的结果如下所示(使用原始的单实例管道)。
S
Server> makepipe
NamedPipeServerStream object created.
Waiting for client connection...
C
Client> oneopen \\.\pipe\testpipe
S
Data was written.
Pipe closed.
C
604 0
Hallo world!
我知道这有点太长了;但我想涵盖许多变体。
最后,与承诺的一样关于PowerShell,下面是获得的测试结果。
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
因此,PowerShell不仅不喜欢从管道获取内容,而且在决定之前会打开(并关闭)它
4 次!!