Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to redirect stdout of a C# project to file using the Visual Studio "command line arguments" option

Tags:

I am trying to redirect the output of a C# program to a file. When using "cmd.exe" I can simply run it with myprogram.exe arg1 arg2 > out.txt, but I'd like to accomplish the same thing using Visual Studio Start Options.

I created a C# Empty Project and added this code:

using System; class Test {     public static void Main(string[] args)     {         foreach (var arg in args) Console.WriteLine(arg);     } } 

Then I edited the command line arguments in the Project Settings: Project Properties

Running the project with Ctrl+F5 does not work as intended. I get the command line arguments printed in the console and not in the output file:

arg1 arg2 > output.txt 

If I change the command line arguments to: arg1 arg2 "> output.txt" I get the following output:

arg1 arg2 ^> output.txt 

I noticed that an empty output.txt file gets created in the Output folder.

Is this thing possible to accomplish or am I forced to keep using cmd.exe to launch my program?

like image 265
tyrion Avatar asked Apr 28 '13 11:04

tyrion


People also ask

What is output redirection in C?

Before the C shell executes a command, it scans the command line for redirection characters. These special notations direct the shell to redirect input and output. You can redirect the standard input and output of a command with the following syntax statements: Item.

How do I redirect standard output to a file?

Redirecting stdout and stderr to a file: The I/O streams can be redirected by putting the n> operator in use, where n is the file descriptor number. For redirecting stdout, we use “1>” and for stderr, “2>” is added as an operator.

What is stdout in C programming?

stdout stands for standard output stream and it is a stream which is available to your program by the operating system itself. It is already available to your program from the beginning together with stdin and stderr .

Why do we use dup2?

The dup2() system function is used to create a copy of an existing file descriptor. In Linux, there are 3 standard file descriptors. They are: stdin: This is the standard input file descriptor.


1 Answers

In the strict sense, you are forced to use the command prompt to start the program with redirected output. Otherwise, you would need to parse the command line yourself, the GUI shell might not do that.

If you just want to redirect the output when you Start Debugging, then uncheck the check box of Enable the Visual Studio hosting process, you are done.

If you did not, and the "output.txt" you've seen there, in fact, is not generated by your application, but "YourApplication.vshost.exe" which is spawned before you started to debug, by Visual Studio IDE. The content would always be empty, and cannot be written; because it's locked by the Hosting Process.

fo1e8.png

However, if you want the application behaves as the same whatever the mode you start it, things are more complicated.

When you start debugging with the application, it's started with:

"YourApplication.exe" arg1 arg2

because the output is already redirected by the IDE.

And when you Start Without Debugging, it's started with:

"%comspec%" /c ""YourApplication.exe" arg1 arg2 ^>output.txt & pause"

This is the correct way to let your application gets all the arguments which you specified.

You might want to have a look at my previous answer of How can I detect if "Press any key to continue . . ." will be displayed?.

Here I'm using an approach like atavistic throwback in the code below:

  • Code of application

    using System.Diagnostics; using System.Linq; using System;  class Test {     public static void Main(string[] args) {         foreach(var arg in args)             Console.WriteLine(arg);     }      static Test() {         var current=Process.GetCurrentProcess();         var parent=current.GetParentProcess();         var grand=parent.GetParentProcess();          if(null==grand             ||grand.MainModule.FileName!=current.MainModule.FileName)             using(var child=Process.Start(                 new ProcessStartInfo {                     FileName=Environment.GetEnvironmentVariable("comspec"),                     Arguments="/c\x20"+Environment.CommandLine,                     RedirectStandardOutput=true,                     UseShellExecute=false                 })) {                 Console.Write(child.StandardOutput.ReadToEnd());                 child.WaitForExit();                 Environment.Exit(child.ExitCode);             } #if false // change to true if child process debugging is needed          else {             if(!Debugger.IsAttached)                 Debugger.Launch();              Main(Environment.GetCommandLineArgs().Skip(1).ToArray());             current.Kill(); // or Environment.Exit(0);          } #endif     } } 

We also need the following code so that it can work:

  • Code of extension methods

    using System.Management; // add reference is required  using System.Runtime.InteropServices; using System.Diagnostics;  using System.Collections.Generic; using System.Linq; using System;  public static partial class NativeMethods {     [DllImport("kernel32.dll")]     public static extern bool TerminateThread(         IntPtr hThread, uint dwExitCode);      [DllImport("kernel32.dll")]     public static extern IntPtr OpenThread(         uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); }  public static partial class ProcessThreadExtensions /* public methods */ {     public static void Abort(this ProcessThread t) {         NativeMethods.TerminateThread(             NativeMethods.OpenThread(1, false, (uint)t.Id), 1);     }      public static IEnumerable<Process> GetChildProcesses(this Process p) {         return p.GetProcesses(1);     }      public static Process GetParentProcess(this Process p) {         return p.GetProcesses(-1).SingleOrDefault();     } }  partial class ProcessThreadExtensions /* non-public methods */ {     static IEnumerable<Process> GetProcesses(         this Process p, int direction) {         return             from format in new[] {                  "select {0} from Win32_Process where {1}" }             let selectName=direction<0?"ParentProcessId":"ProcessId"             let filterName=direction<0?"ProcessId":"ParentProcessId"             let filter=String.Format("{0} = {1}", p.Id, filterName)             let query=String.Format(format, selectName, filter)             let searcher=new ManagementObjectSearcher("root\\CIMV2", query)             from ManagementObject x in searcher.Get()             let process=                 ProcessThreadExtensions.GetProcessById(x[selectName])             where null!=process             select process;     }      // not a good practice to use generics like this;      // but for the convenience ..      static Process GetProcessById<T>(T processId) {         try {             var id=(int)Convert.ChangeType(processId, typeof(int));             return Process.GetProcessById(id);         }         catch(ArgumentException) {             return default(Process);         }     } } 

Since the parent would be Visual Studio IDE(currently named "devenv") when we are debugging. The parent and grandparent process in fact are various, and we would need a rule to perform some checking.

The tricky part is that the grandchild is the one really runs into Main. The code check for the grandparent process each time it runs. If the grandparent was null then it spawns, but the spawned process would be %comspec%, which is also the parent of the new process it going to start with the same executable of current. Thus, if the grandparent are the same as itself then it won't continue to spawn, just runs into Main.

The Static Constructor is used in the code, which is started before Main. There is an answered question on SO: How does a static constructor work?.

When we start debugging, we are debugging the grandparent process(which spawns). For debugging with the grandchild process, I made Debugger.Launch with a conditional compilation which will invoke Main, for keeping Main clear.

An answered question about the debugger would also be helpful: Attach debugger in C# to another process.

like image 186
Ken Kin Avatar answered Oct 01 '22 08:10

Ken Kin