繁体   English   中英

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

[英]C# UnauthorizedAccessException when enabling MessageMode for read-only named pipe (NamedPipeClientStream class)

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

尝试这样做会引发UnauthorizedAccessException 虽然管道通常用于在进程之间进行通信,但是在单个进程中的这个简单示例显示了问题:

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函数来执行操作,此调用的错误作为异常引发。 根据文档SetNamedPipeHandleState函数,它说明了管道句柄,以便调用此函数

句柄必须具有对只读或读/写管道的命名管道的GENERIC_WRITE访问权限,或者对于只读管道必须具有GENERIC_READ和FILE_WRITE_ATTRIBUTES访问权限。

这就是问题所在。

如果我们看一下构造函数NamedPipeClientStream是参加一个PipeDirection设置,我们可以看到,他们只要求GENERIC_READ访问PipeDirection.InGENERIC_WRITE访问PipeDirection.Out (或两者InOut )。 这意味着以OutInOut模式打开的任何管道都可以工作,因为GENERIC_WRITE访问对于这些情况就足够了,但我们需要GENERIC_READFILE_WRITE_ATTRIBUTES作为只读管道, NamedPipeClientStream类从不请求。 这是课堂上的缺陷,应由Microsoft更正。

我在这里提交了有关Microsoft Connect的错误报告:

https://connect.microsoft.com/VisualStudio/feedback/details/1825187

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


直到修复(从2017年3月开始没有),人们可以通过为NamedPipeClientStream使用不同的构造函数来完全解决这个问题。

还有,它接受,而不是一个构造函数的一个重载PipeDirection枚举,一个PipeAccessRights枚举相反,在那里你可以指定你想获得手柄的访问权限的特定组合。 构造函数然后导出管的从指定的访问权的组合(该方向In如果ReadData被指定时, Out “如果WriteData被指定时, InOut如果它们都指定)。

这意味着,您可以通过简单地更改构造函数行来解决此问题,而无需使管道全双工:

var pipeIn = new NamedPipeClientStream("<ServerName>", "<PipeName>", PipeDirection.In);

对此:

var pipeIn = 
   new NamedPipeClientStream("<ServerName>", 
                             "<PipeName>", 
                             PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes, 
                             PipeOptions.None, 
                             System.Security.Principal.TokenImpersonationLevel.None, 
                             System.IO.HandleInheritability.None);

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM