为只读命名管道启用MessageMode时的C#UnauthorizedAccessException(NamedPipeClientStream类)
.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
。
在搜索有关此问题的信息时,我在其他地方找到了对它的引用,例如:
-
命名管道问题:System.UnauthorizedAccessException:拒绝访问路径。
-
WPF – 使用Windows管道协议的消息传递
- PipeTransmissionMode.Message:.NET命名管道如何区分消息?
所有这些post都提到这是“奇怪的”,“奇怪的”等,但不解释“为什么”它不起作用,并且都给出相同的解决方法,“出于一些奇怪的原因”设置管道方向到InOut
使它工作。
确实,这确实使它工作,但它需要从根本上改变管道的两端定义到全双工,而不是单向,我认为这是一个非常糟糕的方法,除非你是能够更改客户端和服务器,这甚至可能是不可能的。
我的问题是,为什么在入站管道上启用消息模式会导致exception,是否有更好的方法来解决此问题,而不是将管道更改为双向模式?
查看Microsoft参考源,我可以看到设置ReadMode
属性只是调用win32 SetNamedPipeHandleState
函数来执行操作,此调用的错误作为exception引发。 根据文档SetNamedPipeHandleState函数,它说明了管道句柄,以便调用此函数
句柄必须具有对只写或读/写管道的命名管道的GENERIC_WRITE访问权限,或者对于只读管道必须具有GENERIC_READ和FILE_WRITE_ATTRIBUTES访问权限。
这就是问题所在。
如果我们查看接受PipeDirection
设置的NamedPipeClientStream的构造函数,我们会看到它们只为PipeDirection.In
请求GENERIC_READ
访问,并为PipeDirection.In
请求GENERIC_READ
访问(或者对InOut
都请求)。 这意味着以Out
或InOut
模式打开的任何管道都可以工作,因为GENERIC_WRITE
访问对于这些情况就足够了,但我们需要GENERIC_READ
和FILE_WRITE_ATTRIBUTES
作为只读管道, NamedPipeClientStream
类从不请求。 这是课堂上的缺陷,应由Microsoft更正。
我在这里提交了有关Microsoft Connect的错误报告:
https://connect.microsoft.com/VisualStudio/feedback/details/1825187
如果您自己遇到此问题,请向上投票,这可能有助于加快修复速度。
直到修复(从2017年3月开始没有),人们可以通过为NamedPipeClientStream
使用不同的构造函数来完全解决这个问题。
构造函数的一个重载代替了PipeDirection
枚举,而不是PipeAccessRights
枚举,您可以在其中指定要为句柄获取的特定访问权限组合。 然后构造函数从指定的访问权限的组合中导出管道的方向(如果指定了ReadData
则为“如果指定了WriteData
则为Out
”,如果指定了它们,则为InOut
)。
这意味着,您可以通过简单地更改如下所示的构造函数线来解决此问题,而无需使管道全双工:
var pipeIn = new NamedPipeClientStream("", "", PipeDirection.In);
对此:
var pipeIn = new NamedPipeClientStream("", "", PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes, PipeOptions.None, System.Security.Principal.TokenImpersonationLevel.None, System.IO.HandleInheritability.None);
如果您使用此备用构造函数作为建议的解决方法,则结果将与您从构造函数的第一种forms获得的结果相同且无法区分,除了将获得此附加访问权限,以便消息模式可以是启用。