为只读命名管道(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
使其起作用。
的确,这确实使它发挥了作用,但它需要从根本上将两端管道的定义更改为全双工,而不是单向,我认为这是一种非常糟糕的方法,除非您能够同时更改客户端和服务器,否则这可能是不可能的。
我的问题是,为什么在入站管道上启用消息模式会导致异常,有没有比将管道更改为双向模式更好的方法来解决这个问题?
查看Microsoft参考源,我可以看到设置ReadMode
属性只是调用win32 SetNamedPipeHandleState
函数来执行操作,而此调用中的错误则作为异常引发。根据文档SetNamedPipeHandleState函数,它说明了为了调用此函数而使用的管道句柄
句柄必须具有对的命名管道的GENERIC_WRITE访问权限只读或读/写管道,或者它必须具有GENERIC_read和FILE_WRITE_ATTRIBUTES访问只读管道。
问题就在这里。
如果我们查看接受PipeDirection
设置的NamedPipeClientStream的构造函数,我们会发现它们只为PipeDirection.In
请求GENERIC_READ
访问,为PipeDirection.Out
请求GENERIC_WRITE
访问(或为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
,则为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);
如果使用此备用构造函数作为建议的解决方法,则结果将与从第一种形式的构造函数中获得的结果相同且无法区分,但将获得此附加访问权限,以便启用消息模式。