# C/C++/C# Console Redirection with Sockets - Win32



## Oliver_FF (Jun 8, 2008)

Here's another interesting thing that you can do with sockets in C or C++. If any of you are familiar with the concepts of SSH or Telnet you'll get this straight away. Every console application has three IO paths...

1. stdIn is the standard input - aka when you type something into the console and it goes to your program.
2. stdOut is the standard output - aka when your program outputs feedback the data is written to stdOut, then its contents are written to the console you are working in.
3. stdErr is the standard error - aka if your program crashes and burns you can tell the OS roughly what happened (in an ideal world...)

Well with C or C++ in Windows you can redirect stdIn and stdOut of any console application to go through a network socket. To write to stdIn you simply send data over the socket. To read from stdOut you simply read data off of the socket. Pretty cool, eh? The most obvious use of something like this is for remote access to your computer - create a host program that sits around waiting for you to connect to it and redirect output to, say, cmd.exe so you have "complete" control over your PC remotely. "Complete" because cmd.exe sucks 



```
#using <windows.h> //sorry not entirely sure where inside there the specific headers are :(

//The socket to use
Socket thisSocket;
//Information about the process we are redirecting.
PROCESS_INFORMATION pi;
//Information about where the IO goes ;)
STARTUPINFO si;

/* Initialise the socket somewhere here */

//cb is the size of the STARTUPINFO structure
si.cb = sizeof( si );
//Tell it we do not want to see the application we are redirecting to/from
si.wShowWindow = SW_HIDE;
//Tell it we want to be able to use .wShowWinwow, .hStdInput, .hstdOutput, .hstdError members.
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
        
//Tell it we want the standard input to come from thisSocket
si.hStdInput = (HANDLE)thisSocket;
//Tell it we want the standard output to go to thisSocket
si.hStdOutput = (HANDLE)thisSocket;
//Tell it we want the stardard error to appear on thisSocket
si.hStdError = (HANDLE)thisSocket;
        
//Spawn the process we want to redirect using the information we just declared.
CreateProcess( NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi );
        

/* From now everything is set up and ticking over. */


//Wait until the process we started ends - then we can quit.
WaitForSingleObject( pi.hProcess, INFINITE );
        
//Close the process handles before exiting.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
```
MSDN on PROCESS_INFORMATION - http://msdn.microsoft.com/en-us/library/ms684873(VS.85).aspx
MSDN on STARTUPINFO - http://msdn.microsoft.com/en-us/library/ms686331(VS.85).aspx

The same effect can be done using C#. The code is easier to read and follow in C# but also it's trickier because you have to transfer the data between the socket yourself... At least that's what i'm assuming when i attempted it...


```
using System.Diagnostics;

process = new Process();
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
process.Start();

//use process.StandardInput.WriteLine(); to write to the applications stdIn

//use process.StandardOutput.ReadLine(); to read from the applications stdOut
```


----------



## Deleted member 3 (Jun 8, 2008)

I've been looking for simple code to send data over a network to build upon. This seems relatively simple. The examples I found were huge and didn't even work out of the box.

The downside is that I don't use C, I'll be looking into this though.


----------



## Oliver_FF (Jun 8, 2008)

If you just want to send data over the network you might want to check my basic sockets tutorial thing here:
http://forums.techpowerup.com/showthread.php?t=56901

The syntax may vary between programming languages, but the method of using the Windows API is usually fairly similar. It's all pretty well documented over at MSDN too


----------



## Kreij (Jun 8, 2008)

DanTheBanjoman said:


> I've been looking for simple code to send data over a network to build upon. This seems relatively simple. The examples I found were huge and didn't even work out of the box.
> 
> The downside is that I don't use C, I'll be looking into this though.



What language are you using Dan?


----------



## Deleted member 3 (Jun 8, 2008)

Kreij said:


> What language are you using Dan?



vb.net


----------



## Oliver_FF (Jun 8, 2008)

Well I guess it's lucky for you that the structures and function calls are all in VB 


```
Dim pInfo As PROCESS_INFORMATION = New PROCESS_INFORMATION()
Dim sInfo As STARTUPINFO = New STARTUPINFO()
...[options here]...

CreateProcess("cmd.exe", Nothing, IntPtr.Zero, IntPtr.Zero, True, 0, IntPtr.Zero, Nothing, sInfo, pInfo)
```


----------



## lo0ney (Jan 14, 2009)

Could you please explain how i would use the send and recv functions in order to send commands to cmd.exe and how to retrieve output?

Thanks alot!

Lo0ney


----------



## FordGT90Concept (Jan 14, 2009)

If you want to use cmd on the local machine to do something, you're better off just using System.Diagnostics.ProcessStartInfo, System.Diagnostics.Process, and a System.IO.StreamReader.  Simply make ProcessStartInfo point to command.exe on Windows 9x and cmd.exe on Windows NT.  In the arguments, do:

/C "your commands > file to save output in"

Use the StreamReader to parse the file.


Also, there is Process.StandardOutput, Process.StandardInput, and Process.StandardError that you could use for redirection.


----------



## lo0ney (Jan 14, 2009)

Well im using C and im trying to send commands to cmd.exe remotely.  IE: Client/Server

So i would need the client to send commands to the server which would then pass the cmd to cmd.exe then return output to the client


----------



## FordGT90Concept (Jan 14, 2009)

What kind of commands?


----------



## lo0ney (Jan 14, 2009)

Any command that can be executed in cmd.exe.  For example: "dir", "ipconfig", "cd C:\"

I have an edit box on the client where the user will type a command to be executed 'remotely' from the server, which launced cmd.exe, then returns the output of the command back to the client.


----------



## FordGT90Concept (Jan 14, 2009)

I would advise against doing that as there is nothing to stop a user from completely destroying a computer.  Additionally, some commands return a massive stack of data so you'd have to use TCP instead of the preferred UDP.  Have you already got the network layers sorted because that will take the most work.


----------



## lo0ney (Jan 14, 2009)

I can set cmd restrictions and filters later, but for now its just for testing purposes.  And I am using TCP sockets, but could i not just use the source posted  by Oliver FF to do this?

I have the source (first post) basically as my server, and then i have a client which connects to the server.


----------



## portnoid (Aug 22, 2010)

I had the error 87 problem and it turned out to be not using a protocol that supported 
IFS handles. Here is how I created a socket that uses IFS handles:

 // Load version 2.2 of Winsock
   if (WSAStartup(MAKEWORD(2, 2), &wsda))
   {
      fprintf(stderr, (char *)"WSAStartup failed error=%d\n", WSAGetLastError());
      exit(1);
   }
   // Find a protocol where socket descriptors returned by the provider
   // are operating system Installable File System (IFS) handles.
   // First, have WSAEnumProtocols tell you how big a buffer you need.
   bool               bProtocolFound = false;
   LPWSAPROTOCOL_INFO lpProtocolBuf  = NULL;
   DWORD              dwBufLen, dwErr;
   int                nRet = WSAEnumProtocolsA(NULL, lpProtocolBuf, &dwBufLen);
   if (SOCKET_ERROR != nRet)
   {
      die((char *)"WSAEnumProtocols: should not have succeeded");
   }
   else if (WSAENOBUFS != (dwErr = WSAGetLastError()))
   {
      // WSAEnumProtocols failed for some reason not relating to buffer size.
      die((char *)"WSAEnumProtocols");
   }
   else
   {
      // WSAEnumProtocols failed for the "expected" reason. Therefore,
      // you need to allocate a buffer of the appropriate size.
      lpProtocolBuf = (WSAPROTOCOL_INFO *)malloc(dwBufLen);
      if (lpProtocolBuf)
      {
         // Now call WSAEnumProtocols again with the expectation
         // that it will succeed because you have allocated a big enough
         // buffer.
         nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
         if (SOCKET_ERROR == nRet)
         {
            die((char *)"WSAEnumProtocols");
         }
         else
         {
            // Loop through protocols, looking for the first service
            // provider that meets the matching criteria.
            for (i = 0; i < nRet; i++)
            {
               if ((IPPROTO_TCP == lpProtocolBuf_.iProtocol) &&
                   (XP1_IFS_HANDLES == (XP1_IFS_HANDLES &
                                        lpProtocolBuf.dwServiceFlags1)))
               {
                  bProtocolFound = true;

                  // Create socket.
                  sock = WSASocket(
                     FROM_PROTOCOL_INFO,
                     FROM_PROTOCOL_INFO,
                     FROM_PROTOCOL_INFO,
                     &lpProtocolBuf,
                     0,
                     0);
                  if (INVALID_SOCKET == sock)
                  {
                     die((char *)"WSASocket");
                  }
                  break;
               }
            }
         }
      }
   }_


----------



## jackrabbit (Aug 6, 2015)

I found an easy workaround to read the combined output and errors when I launch a process from c#.

Instead of calling your program, just call a batch file that calls your program with error redirection.  In my case, the c# program first creates the temporary batch file, invoke this batch file, then delete it instead of directly calling the script.

Example of a batch file:
   echo off
   C:\Temp\MyProgram Arg1 Arg2 Arg3 2>&1

"2>&1" will redirect all errors of MyProgram into the STDOUT, so the process can now retrieve the combined output by reading the Process.StandardOutput only.

If you wish to invoke MyProgram and log STDOUT and STDERR at the same time, simply use this batch file:
   echo off
   C:\Temp\MyProgram Arg1 Arg2 Arg3 1>C:\Temp\MyProgram.log 2>&1

Or this if you wish to append to the log on every call:
   echo off
   C:\Temp\MyProgram Arg1 Arg2 Arg3 1>>C:\Temp\MyProgram.log 2>&1


----------

