10
$\begingroup$

Tried to open an Out named pipe from Powershell

$pipe = new-object System.IO.Pipes.NamedPipeServerStream 'testpipe','Out'
$pipe.WaitForConnection()
$sw = new-object System.IO.StreamWriter $pipe
$sw.AutoFlush = $true
$sw.WriteLine("Server pid is $pid")
$sw.Dispose()
$pipe.Dispose()

and use Get to read the named pipe.

Get["\\\\.\\pipe\\testpipe"]

The following messages were shown in Powershell and Mathematica

Exception calling "WriteLine" with "1" argument(s): "Pipe is broken."
At line:5 char:1
+ $sw.WriteLine("Server pid is $pid")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : IOException

Exception calling "Dispose" with "0" argument(s): "Pipe is broken."
At line:6 char:1
+ $sw.Dispose()
+ ~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : IOException
Get::noopen: Cannot open \\.\pipe\testpipe.
$\endgroup$

1 Answer 1

16
$\begingroup$

Short Version

On Windows, unlike Unix, named pipes are special and do not behave like regular stdio streams. Programs that use named pipes require custom logic. As a rule the Wolfram Language I/O functions, like most stdio programs, do not have that custom logic.

Details

Reading from a named pipe on Windows is a tricky business. There are all kinds of race conditions that can cause a pipe endpoint to be temporarily busy. In this case, the server is ever-so-briefly busy as it spins up its StreamWriter so it returns a ERROR_PIPE_BUSY error to the client. The client aborts and closes the pipe. When the server gets around to writing some bytes to the pipe, if finds that the Pipe is broken.

On Windows, named pipe client code is required to expect ERROR_PIPE_BUSY and to poll or call WaitNamedPipe in response. Unfortunately, the WL I/O functions, like 99% of stdio programs in the wild, do not have this custom logic. Indeed, even the Windows command line tools lack it:

C:\> type \\.\pipe\testpipe

All pipe instances are busy.

NETLINK Work-around

One way to work around this problem is to use the NamedPipeClientStream class directly through NETLink:

Needs["NETLink`"]
InstallNET[];
LoadNETType["System.IO.Pipes.PipeDirection"];

Then, with the PowerShell server running, evaluate:

NETBlock @
  Internal`WithLocalSettings[
    {pipe = NETNew["System.IO.Pipes.NamedPipeClientStream", ".", "testPipe", PipeDirection`In]}
  , pipe@Connect[]
  ; Internal`WithLocalSettings[
      {stream = NETNew["System.IO.StreamReader", pipe]}
    , stream@ReadLine[]
    , stream@Dispose[]
    ]
  , pipe@Dispose[]
  ]

(* "Server pid is 12345" *)
$\endgroup$
2
  • $\begingroup$ Hi @WReach, thanks for your amazing response. I am using MMA 11.3, but it always needs to LoadNETType twice to get the correct type. It will first throw the message that NET::netexcptn: A .NET exception occurred: System.TypeLoadException: Type System.IO.Pipes.PipeDirection not found. at Wolfram.NETLink.TypeLoader.GetType(String typeName, String assemblyName, Boolean throwOnError) at Wolfram.NETLink.Internal.CallPacketHandler.loadType1(KernelLinkImpl ml). And everything goes fine when I execute it again. Do you know the reason? $\endgroup$ May 13, 2019 at 2:28
  • 1
    $\begingroup$ That is strange. I tried it with 11.3 as well and I did not get the TypeLoadException. Perhaps try calling InstallNET[] explicitly prior to LoadNETType[...]. $\endgroup$
    – WReach
    May 13, 2019 at 16:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.