为只读命名管道 (NamedPipeClientStream class) 启用 MessageMode 时出现 C# UnauthorizedAccessException
C# UnauthorizedAccessException when enabling MessageMode for read-only named pipe (NamedPipeClientStream class)
.NET 中的 NamedPipeClientStream
class 存在问题,您无法使用 PipeDirection.In
创建此 class 的实例,然后成功更改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
。
在搜索有关此问题的信息时,我在其他地方找到了对它的引用,例如此处:
Named pipes issue: System.UnauthorizedAccessException: Access to the path is denied.
- PipeTransmissionMode.Message: How do .NET named pipes distinguish between messages?
所有这些帖子都提到这是 "weird"、"odd" 等,但没有解释 "why" 它不起作用,并且都给出了相同的解决方法, "for some strange reason" 将管道方向设置为 InOut
使其工作。
这确实使它起作用,但它需要从根本上改变管道的定义,在两端,全双工而不是单向,我认为这是一种非常糟糕的方法,除非您能够同时更改客户端和服务器,否则这甚至是不可能的。
我的问题是,为什么在入站管道上启用消息模式会导致异常,有没有比将管道更改为双向模式更好的方法来解决这个问题?
查看 Microsoft 参考源,我可以看到设置 ReadMode
属性 只是调用 win32 SetNamedPipeHandleState
函数来执行操作,此调用引发的错误为例外。根据文档 SetNamedPipeHandleState function,它说明了管道句柄,以便调用此函数
the handle must have GENERIC_WRITE access to the named pipe for a
write-only or read/write pipe, or it must have GENERIC_READ and
FILE_WRITE_ATTRIBUTES access for a read-only pipe.
问题就出在这里。
如果我们查看采用 PipeDirection
设置的 NamedPipeClientStream 的构造函数,我们会发现它们只请求 GENERIC_READ
访问 PipeDirection.In
和 GENERIC_WRITE
访问对于 PipeDirection.Out
(或对于 InOut
)。这意味着在 Out
或 InOut
模式下打开的任何管道都可以工作,因为 GENERIC_WRITE
访问权限足以满足这些情况,但我们需要 GENERIC_READ
和 FILE_WRITE_ATTRIBUTES
对于只读管道,NamedPipeClientStream
class 从不请求。这是 class 中的一个缺陷,应由 Microsoft 更正。
我已在此处提交有关 Microsoft Connect 的错误报告:
https://connect.microsoft.com/VisualStudio/feedback/details/1825187
如果您自己遇到这个问题,请给它投票,它可能有助于加快修复速度。
在修复(none 截至 3/2017)之前,可以通过为 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);
如果您使用此备用构造函数作为建议的解决方法,结果将与您从构造函数的第一种形式获得的结果相同且无法区分,但将获得此附加访问权限,因此可以启用消息模式。
.NET 中的 NamedPipeClientStream
class 存在问题,您无法使用 PipeDirection.In
创建此 class 的实例,然后成功更改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
。
在搜索有关此问题的信息时,我在其他地方找到了对它的引用,例如此处:
Named pipes issue: System.UnauthorizedAccessException: Access to the path is denied.
- PipeTransmissionMode.Message: How do .NET named pipes distinguish between messages?
所有这些帖子都提到这是 "weird"、"odd" 等,但没有解释 "why" 它不起作用,并且都给出了相同的解决方法, "for some strange reason" 将管道方向设置为 InOut
使其工作。
这确实使它起作用,但它需要从根本上改变管道的定义,在两端,全双工而不是单向,我认为这是一种非常糟糕的方法,除非您能够同时更改客户端和服务器,否则这甚至是不可能的。
我的问题是,为什么在入站管道上启用消息模式会导致异常,有没有比将管道更改为双向模式更好的方法来解决这个问题?
查看 Microsoft 参考源,我可以看到设置 ReadMode
属性 只是调用 win32 SetNamedPipeHandleState
函数来执行操作,此调用引发的错误为例外。根据文档 SetNamedPipeHandleState function,它说明了管道句柄,以便调用此函数
the handle must have GENERIC_WRITE access to the named pipe for a write-only or read/write pipe, or it must have GENERIC_READ and FILE_WRITE_ATTRIBUTES access for a read-only pipe.
问题就出在这里。
如果我们查看采用 PipeDirection
设置的 NamedPipeClientStream 的构造函数,我们会发现它们只请求 GENERIC_READ
访问 PipeDirection.In
和 GENERIC_WRITE
访问对于 PipeDirection.Out
(或对于 InOut
)。这意味着在 Out
或 InOut
模式下打开的任何管道都可以工作,因为 GENERIC_WRITE
访问权限足以满足这些情况,但我们需要 GENERIC_READ
和 FILE_WRITE_ATTRIBUTES
对于只读管道,NamedPipeClientStream
class 从不请求。这是 class 中的一个缺陷,应由 Microsoft 更正。
我已在此处提交有关 Microsoft Connect 的错误报告:
https://connect.microsoft.com/VisualStudio/feedback/details/1825187
如果您自己遇到这个问题,请给它投票,它可能有助于加快修复速度。
在修复(none 截至 3/2017)之前,可以通过为 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);
如果您使用此备用构造函数作为建议的解决方法,结果将与您从构造函数的第一种形式获得的结果相同且无法区分,但将获得此附加访问权限,因此可以启用消息模式。