.net 为只读命名管道(NamedPipeClientStream类)启用MessageMode时出现C# UnauthorizedAccessException异常

vfhzx4xs  于 2023-03-04  发布在  .NET
关注(0)|答案(1)|浏览(157)

.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使它工作。
这确实使它工作,但它需要从根本上改变管道的定义,在两端,全双工,而不是在一个方向上,我认为这是一个非常糟糕的方法,除非你能够改变客户端和服务器,这甚至可能是不可能的。
我的问题是,为什么在入站管道上启用消息模式会导致异常,是否有比将管道更改为双向模式更好的方法来解决这个问题?

8ehkhllq

8ehkhllq1#

查看Microsoft参考源代码,我可以看到设置ReadMode属性只是调用win32 SetNamedPipeHandleState函数来执行操作,此调用的错误作为异常引发。
对于只写管道或读/写管道,句柄必须具有对命名管道的GENERIC_WRITE访问权限;对于只读管道,句柄必须具有GENERIC_READ和FILE_WRITE_ATTRIBUTES访问权限。
问题就在这里。
如果我们查看接受PipeDirection设置的NamedPipeClientStream的构造函数,我们会发现它们只请求PipeDirection.InGENERIC_READ访问权限,以及PipeDirection.OutGENERIC_WRITE访问权限这意味着以OutInOut模式打开的任何管道都将工作,因为GENERIC_WRITE访问对于那些情况是足够的,但是我们需要GENERIC_READFILE_WRITE_ATTRIBUTES作为只读管道,而NamedPipeClientStream类从不请求。这是类中的一个缺陷,应该由Microsoft更正。
我在这里提交了一份关于Microsoft Connect的错误报告:
https://connect.microsoft.com/VisualStudio/feedback/details/1825187
如果您自己遇到这个问题,请投赞成票,这可能有助于加快修复速度。
在修复之前(截至2017年3月没有),可以通过对NamedPipeClientStream使用不同的构造函数来完全解决这个问题。
构造函数有一个重载,它接收PipeAccessRights枚举而不是PipeDirection枚举,在这里你可以指定你想要为句柄获得的访问权限的特定组合。构造函数然后从指定的访问权限组合中导出管道的方向(如果指定了ReadData,则为In;如果指定了WriteData,则为Out ";如果同时指定了InOutWriteData,则为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);

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

相关问题