The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 2,731 other followers

pipe – Windows how to redirect file parameter to stdout? (Windows equivalent of `/dev/stdout`) – Super User

Posted by jpluimers on 2022/02/02

TL;DR:

  • Windows has CON: which is an equivalent for /dev/tty
  • Windows has no equivalent for /dev/stdout (the standard output stream)
  • There is a C# PipeServer.cs proof-of-concept that allows to simulate /dev/stdout through a temporary named pipe
  • Windows pipe names start with \\.\pipe\ for names on the local machine
  • The above for /dev/stdout on Windows also holds for /dev/stdin (the standard input stream)

All via [Wayback] pipe – Windows how to redirect file parameter to stdout? (Windows equivalent of /dev/stdout) – Super User.

Windows CON is like /dev/tty

Because of extensive backward compatibility to MS-DOS, Windows keeps these filenames as reserved and for most, including CON has special functionality. The original DOS documentation of this has been taken off-line, but luckily there are archival sites: MS-DOS Device Driver Names Cannot be Used as File Names. That is the link to [Wayback] KB74496 before it was taken off-line. Originally, it was known as [Wayback] Q74496:

Microsoft MS-DOS reserves certain names for system device drivers. If you try to name a file using one of these names, you will receive the following error message:

Write Fault Error Writing Device <FILENAME.EXT>Abort, Retry, Ignore, Fail?

The solution is to change the file name.

Below is a list of default device driver names.

   Name    Function
   ----    --------
   CON     Keyboard and display
   PRN     System list device, usually a parallel port
   AUX     Auxiliary device, usually a serial port
   CLOCK$  System real-time clock
   NUL     Bit-bucket device
   A:-Z:   Drive letters
   COM1    First serial communications port
   LPT1    First parallel printer port
   LPT2    Second parallel printer port
   LPT3    Third parallel printer port
   COM2    Second serial communications port
   COM3    Third serial communications port
   COM4    Fourth serial communications port

Basically MS-DOS CON is like the Unix/Linux /dev/tty Device file – Wikipedia, and Windows CON is like the /dev/tty* Virtual console – Wikipedia (since it is bound to the conhost.exe Windows Console – Wikipedia)

Windows has no equivalent for /dev/stdout

On Linux, processes can read input from the special files /dev/stdin or write output to /dev/stdout, and you can specify those as file parameters. This is very useful when a tool by default does not read from /dev/stdin or write to /dev/stdout.

I wish Windows had an equivalent for /dev/stdout (and /dev/stdin). Since I could not find it, I asked [Wayback] pipe – Windows how to redirect file parameter to stdout?(Windows equivalent of /dev/stdout) – Super User, which confirmed by suspicion (thanks [Wayback] grawity!):

Windows has no direct equivalent to /dev/stdout.

You can simulate /dev/stdout and /dev/stdin on Windows through a temporary named pipe provided through an intermediate program

[Wayback] grawity also wrote a proof-of-concept C# program that can provide access to standard input via a named pipe from a secondary console:

Run:

PipeServer foo in | programtwo

in window one, and:

programone \\.\pipe\foo

in window two.

I’ve copied the source to a gist having an initial PipeServer.cs [Wayback].

Later, [Wayback] ntninja improved the proof-of-concept so a secondary console was not needed any more, which I copied to the second PipeServer.cs [Wayback].

General usage:

PipeServer [in|out] [process name] [argument 1] [argument 2] [...]

The string "{pipe}" is then replaced by the redirection path.

Real-World example:

PipeServer.exe in "C:\Keil\UV4\Uv4.exe" -b "C:\Project\Project.uvproj" -j0 -o "{pipe}"

What happens underneath is that "{pipe}" becomes a value like "\\.\pipe\pipeserver_1" fully automatically (the tool tries from 1 up till 65535).

Windows named pipes and named pipe names

Since named pipes are a relatively unknown feature for many people, a few important links (note they are not limited to Win32, it’s just that I could not find the generic documentation on them yet):

–jeroen


Source code:


class PipeServer
{
static
int
Main(string[] args)
{
if(args.Length < 2
||(System.String.Compare(args[0], "in") != 0
&& System.String.Compare(args[0], "out") != 0)) {
System.Console.WriteLine("Usage: PipeServer <in | out> <process> <args>");
return 1;
}
///////////////////////////////////
// // // Process arguments // // //
///////////////////////////////////
// Convert pipe direction
System.IO.Pipes.PipeDirection pipe_dir = 0;
if(System.String.Compare(args[0], "in") == 0) {
pipe_dir = System.IO.Pipes.PipeDirection.In;
}
if(System.String.Compare(args[0], "out") == 0) {
pipe_dir = System.IO.Pipes.PipeDirection.Out;
}
// Find process name to start
string proc_name = args[1];
// Build commandline argument string
string proc_args = "";
for(System.UInt16 i = 2; i < args.Length; i++) {
if(args[i].IndexOf(" ") > 1) {
proc_args += "\"" + args[i].Replace("\"", "\\\"") + "\" ";
} else {
proc_args += args[i] + " ";
}
}
// Create server
string pipe_name = "";
System.IO.Pipes.NamedPipeServerStream pipe_stream = null;
for(System.UInt16 i = 1; i < 65535; i++) {
// Generate new pipe name
pipe_name = "pipeserver_" + System.Convert.ToString(i);
try {
// Start server
pipe_stream = new System.IO.Pipes.NamedPipeServerStream(pipe_name, pipe_dir, 1);
break;
} catch(System.IO.IOException _) {
continue;
}
}
if(pipe_stream == null) {
System.Console.WriteLine("Could not create pipe");
return 1;
}
// Make sure the process knows about the pipe name
proc_args = proc_args.Replace("{pipe}", "\\\\.\\pipe\\" + pipe_name);
// Run process
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = proc_name;
proc.StartInfo.Arguments = proc_args;
proc.Start();
// Connect pipes and wait until EOF
pipe_stream.WaitForConnection();
try {
if(pipe_dir == System.IO.Pipes.PipeDirection.In) {
pipe_stream.CopyTo(System.Console.OpenStandardOutput());
}
if(pipe_dir == System.IO.Pipes.PipeDirection.Out) {
System.Console.OpenStandardInput().CopyTo(pipe_stream);
}
} catch (System.IO.IOException e) {
System.Console.WriteLine("error: {0}", e.Message);
return 1;
}
// Wait for process termination
while(!proc.HasExited) {
proc.WaitForExit();
}
// Return correct exit code
return proc.ExitCode;
}
}

view raw

PipeServer.cs

hosted with ❤ by GitHub

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

 
%d bloggers like this: