C#为只读命名管道(NamedPipeClientStream类)启用MessageMode时发生UnauthorizedAccessException

C#为只读命名管道(NamedPipeClientStream类)启用MessageMode时发生UnauthorizedAccessException,c#,.net,named-pipes,C#,.net,Named Pipes,.NET中的NamedPipeClientStream类存在问题,因为您无法使用中的PipeDirection.in创建此类的实例,然后成功地将读取模式更改为PipeTransmissionMode.Message 尝试执行此操作将引发未经授权的访问异常。虽然管道通常用于进程之间的通信,但单个进程中的这个简单示例说明了问题: var pipeOut = new NamedPipeServerStream("SomeNamedPipe",

.NET中的
NamedPipeClientStream
类存在问题,因为您无法使用
中的PipeDirection.in
创建此类的实例,然后成功地将
读取模式
更改为
PipeTransmissionMode.Message

尝试执行此操作将引发未经授权的访问异常。虽然管道通常用于进程之间的通信,但单个进程中的这个简单示例说明了问题:

var pipeOut = new NamedPipeServerStream("SomeNamedPipe", 
                                        PipeDirection.Out, 
                                        1, 
                                        PipeTransmissionMode.Message);
var pipeIn = new NamedPipeClientStream(".", 
                                       "SomeNamedPipe", 
                                       PipeDirection.In);
pipeIn.Connect();
pipeIn.ReadMode = PipeTransmissionMode.Message;
试图设置ReadMode属性时,此代码将抛出一个
UnauthorizedAccessException


在搜索有关此问题的信息时,我在其他地方找到了对它的引用,例如:

所有这些帖子都提到这是“奇怪的”、“奇怪的”等等,但没有解释“为什么”它不起作用,并且都给出了相同的解决方法,即“出于某种奇怪的原因”将管道方向设置为InOut使其起作用

<>这确实是可行的,但是它需要从根本上改变管道的定义,两端都是全双工的,而不是单向的,我认为这是一个很差的方法,除非你能够改变客户端和服务器,否则这可能是不可能的。

我的问题是,为什么在入站管道上启用消息模式会导致异常,有没有比将管道更改为双向模式更好的方法来解决此问题?

查看Microsoft参考源,我可以看到,设置
ReadMode
属性只是调用win32
SetNamedPipeHandleState
函数来执行该操作,该调用产生的错误作为异常而引发。根据文档,它声明了要调用此函数所需的管道句柄

句柄必须具有对指定管道的通用写入访问权限 只写或读/写管道,或者它必须具有通用的\u读和 只读管道的文件\写入\属性访问

问题就在这里

如果我们查看接受
PipeDirection
设置的NamedPipeClientStream的构造函数,我们会发现它们只请求
GENERIC\u READ
访问
PipeDirection.in
,和
GENERIC\u WRITE
访问
PipeDirection.Out
(或同时请求
InOut
)。这意味着在
Out
InOut
模式下打开的任何管道都可以工作,因为
GENERIC\u WRITE
访问对于这些情况是足够的,但是对于
NamedPipeClientStream
类从不请求的只读管道,我们需要
GENERIC\u READ
FILE\u WRITE\u属性。这是类中的一个缺陷,应该由Microsoft更正

我已在此处提交了有关Microsoft Connect的错误报告:

如果您自己遇到此问题,请加快投票,这可能有助于加快修复速度


在修复之前(截至2017年3月无),您可以通过为
NamedPipeClientStream
使用不同的构造函数来完全解决此问题

构造函数有一个重载,它接受的不是
PipeDirection
枚举,而是
PipeAccessRights
枚举,您可以指定要为句柄获取的访问权限的特定组合。然后,构造函数从指定的访问权限组合中导出管道的方向(
In
如果指定了
ReadData
Out
“如果指定了
WriteData
InOut
,如果两者都指定了)

这意味着您可以解决此问题,而无需使管道全双工,只需更改以下构造函数行:

var pipeIn = new NamedPipeClientStream("<ServerName>", "<PipeName>", PipeDirection.In);
var pipeIn=newnamedpipeclientstream(“,”,PipeDirection.In);
为此:

var pipeIn = 
   new NamedPipeClientStream("<ServerName>", 
                             "<PipeName>", 
                             PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes, 
                             PipeOptions.None, 
                             System.Security.Principal.TokenImpersonationLevel.None, 
                             System.IO.HandleInheritability.None);
var pipeIn=
新名称PipeClientStream(“,
"", 
PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes,
没有选择,
System.Security.Principal.TokenImpersonationLevel.None,
System.IO.HandleInheritability.None);

如果使用此替代构造函数作为建议的解决方法,则结果将与从第一种构造函数中获得的结果相同且无法区分,唯一的例外是将获得此附加访问权限,以便启用消息模式。

使用
PipeDirecti肯定没有帮助在
on上,枚举的名称完全是向后命名的。您写入单向管道的输入端,数据沿着管道向下传输到另一个进程,然后可以从输出端读取。但是,
PipeDirection。In
表示可读(管道的输出端).
错误CS1729“NamedPipeServerStream”不包含接受6个参数的构造函数
@AlexG您需要对
NamedPipeClientStream
进行这些更改,如图所示。您的错误表明您试图修改
NamedPipeServerStream