2013-04-28 125 views
16

我正在嘗試將C#程序的輸出重定向到文件。當使用「cmd.exe」時,我可以簡單地使用myprogram.exe arg1 arg2 > out.txt來運行它,但是我想用Visual Studio 啓動選項完成同樣的操作。如何使用Visual Studio「命令行參數」選項將C#項目的stdout重定向到文件

我創建了一個C#空項目並添加以下代碼:

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

然後我編輯的命令行參數在項目設置: Project Properties

運行項目,按Ctrl + F5不按預期工作。我在控制檯中,而不是在輸出文件打印的命令行參數:

arg1 
arg2 
> 
output.txt 

如果我改變了命令行參數:arg1 arg2 "> output.txt"我得到以下輸出:

arg1 
arg2 
^> output.txt 

我注意到,在輸出文件夾中創建一個空的output.txt文件。

這件事情是可能完成的,還是我不得不繼續使用cmd.exe來啓動我的程序?

+0

這不能爲C#項目完成,但是(正如您可能知道的那樣)完成此操作對於C++項目來說可行。我不知道VS爲什麼不一致。我想這只是微軟普遍缺乏團隊之間的波蘭和協調。 – bames53 2014-08-26 19:00:12

回答

8

我不確定這是否可以在Visual Studio中完成。我的解決方案是爲控制檯設置一個新的輸出。這個例子是從MSDN:

Console.WriteLine("Hello World"); 
FileStream fs = new FileStream("Test.txt", FileMode.Create); 
// First, save the standard output. 
TextWriter tmp = Console.Out; 
StreamWriter sw = new StreamWriter(fs); 
Console.SetOut(sw); 
Console.WriteLine("Hello file"); 
Console.SetOut(tmp); 
Console.WriteLine("Hello World"); 
sw.Close(); 

http://msdn.microsoft.com/en-us/library/system.console.setout.aspx

2

您可以在外部編輯器中打開.csproj.user和改變StartArguments來自:

<StartArguments>arg1 arg2 &gt; output.txt</StartArguments> 

<StartArguments>arg1 arg2 > output.txt</StartArguments> 
+2

鑑於它是XML,我不希望這會改變任何東西。 – 2013-04-30 17:46:18

+1

嗯,我沒有想到它:) – 2013-04-30 18:49:45

4

在開始選項部分,更改您的命令l INE參數文本框以這種方式

args1 args2 1>output.txt 

這重定向標準輸出(1)創建一個名爲Output.txt
如果要追加到以前版本的文件的文件寫

args1 args2 1>>output.txt 

現在,您可以在輸出控制檯重定向時一步一步地調試程序。

+0

不幸的是,在Visual Studio 2012中不起作用。如果您從Main()打印args [0],它將在控制檯窗口中打印'1> output.txt' 。 – 2013-04-28 12:12:31

+0

我剛剛在VS2012中試過這個。奇怪的 – Steve 2013-04-28 12:13:17

+0

我剛剛意識到 - 只要您至少有一個參數,它就會工作。如果你只是''output.txt'它*不*工作...等等{編輯}仍然不起作用...嗯 – 2013-04-28 12:18:54

9

嚴格意義上說,您不得不使用命令提示符以重定向輸出啓動程序。否則,你需要自己解析命令行,GUI外殼可能不會那樣做。

如果您只是想要在輸出Start Debugging時重定向輸出,請取消選中Enable the Visual Studio hosting process複選框即可。

如果你沒有,和"output.txt"你見過沒有,其實是應用程序生成的,但"YourApplication.vshost.exe"你開始調試之前,由Visual Studio IDE被催生。內容總是空的,不能寫;因爲它被Hosting Process鎖定。

fo1e8.png

不過,如果你希望應用程序表現爲無論什麼模式,你啓動它一樣,事情更復雜。

當你開始調試應用程序,它的開始:

「YourApplication.exe」 ARG1 ARG2

因爲輸出已由IDE重定向。

當你Start Without Debugging,它的開始:

「%COMSPEC%」/ C 「」 YourApplication.exe 「ARG1 ARG2 ^> output.txt的&暫停」

這是正確的方式讓你的應用程序獲取你指定的所有參數。

你可能想看看我以前的回答How can I detect if "Press any key to continue . . ." will be displayed?

在這裏,我在下面的代碼中使用的方法類似atavistic throwback

  • 應用程序的代碼

    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 
        } 
    } 
    

我們還需要下面的代碼,以便它可以工作:

  • 的擴展方法的代碼

    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); 
         } 
        } 
    } 
    

由於家長會的Visual Studio IDE(目前名爲"devenv"),當我們正在調試。事實上,父母和祖父母的過程是多種多樣的,我們需要一個規則來執行一些檢查。

棘手的部分是孫子是真正遇到Main。代碼檢查每次運行時的祖父進程。如果祖父母是null那麼它會產生,但產生的過程將是%comspec%,這也是新過程的父母,它將以相同的可執行文件開始。因此,如果祖父母和自己一樣,那麼它就不會繼續產卵,只會碰到Main

Static Constructor用於代碼中,該代碼在Main之前啓動。有關於SO的回答問題:How does a static constructor work?

當我們開始調試時,我們正在調試祖父進程(產生)。對於使用孫子進程進行調試,我使用條件編譯Debugger.Launch來調用Main,以保持清除Main

有關調試器的回答問題也將有所幫助:Attach debugger in C# to another process

+0

此設置現在已經消失了''>就地相同的語法。我正在使用它,但現在更新後,它缺少。我在VS2017 v15.5.2現在 – mbudnik 2018-01-15 10:03:11

+0

@mbudnik:你不必那麼現在要做的,只是做的OP沒有 - 指定重定向將獲得工作的事情。 – 2018-01-15 12:15:32

+0

@mbudnik:遇到的問題只發生在vs2017的「無需調試」開始時。我剛剛做了測試(再次)與我的答案,這仍然沒有涉及不存在對的,現在選擇工作在兩種情況下(調試/非調試)。只要應用它,你就會感受到魔力。 – 2018-01-15 13:14:16

2

如果您想將輸出管道傳輸到文件,您將不得不繼續使用cmd.exe。 命令行參數:用於命令行參數,因此您嘗試的任何內容都將被轉義爲命令行參數。管道和重定向器不是命令行參數,因此它們正在越獄。

我只是創建一個.bat文件,調用該程序,我喜歡。你可以將它固定到任務欄並簡單地運行它。

2

我會建議一個更好的方式,而無需編寫任何代碼段!

The start up settings

只是配置Visual Studio來開始你的程序作爲一個外部程序。

+0

好。但是,如果您處於調試模式,則此方法會遇到一些問題;並且在VS(不調試)和直接在控制檯下運行時也會產生不同的行爲。我建議使用像[cmdow](http://www.commandline.co.uk/cmdow/)這樣的外部程序。無論如何Upvote。 – 2013-05-07 16:25:28

+0

當然,調試選項仍然可用,但我們不能使用Visual Studio託管過程! – 2013-05-07 16:28:41

+0

我做**不**說這是不可用的,但.. – 2013-05-07 16:32:51

0

簡單的方法是: 右鍵單擊項目 =>屬性 ==>調試

enter image description here

並確保在調試模式運行您的程序。

相關問題