为只读命名管道(NamedPipeClientStream类)启用MessageMode时出现C#Unauthorize

本文关键字:MessageMode 启用 C#Unauthorize 只读 管道 NamedPipeClientStream | 更新日期: 2023-09-27 18:14:03

中的NamedPipeClientStream类出现问题。NET,因为您不能使用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


在搜索关于这个问题的信息时,我在其他地方找到了它的参考文献,比如这里:

  • 命名管道问题:系统。UnauthorizedAccessException:拒绝访问路径。

  • WPF-使用Windows管道协议的消息传递

  • 管道传输模式。留言:怎么做。NET命名管道区分消息

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

的确,这确实使它发挥了作用,但它需要从根本上将两端管道的定义更改为全双工,而不是单向,我认为这是一种非常糟糕的方法,除非您能够同时更改客户端和服务器,否则这可能是不可能的。


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

为只读命名管道(NamedPipeClientStream类)启用MessageMode时出现C#Unauthorize

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

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

问题就在这里。

如果我们查看接受PipeDirection设置的NamedPipeClientStream的构造函数,我们会发现它们只为PipeDirection.In请求GENERIC_READ访问,为PipeDirection.Out请求GENERIC_WRITE访问(或为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枚举,您可以在其中指定要为句柄获得的访问权限的特定组合。然后,构造函数从指定访问权限的组合中导出管道的方向(如果指定了ReadData,则为In;如果指定了WriteData,则为Out";如果两者都指定了,则为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);

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