21

C# プログラムの出力をファイルにリダイレクトしようとしています。「cmd.exe」を使用する場合は、単に実行できますが、 Visual Studio Start Optionsmyprogram.exe arg1 arg2 > out.txtを使用して同じことを達成したいと思います。

C# の空のプロジェクトを作成し、次のコードを追加しました。

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

次に、プロジェクト設定で コマンド ライン引数を編集しました。プロジェクトのプロパティ

Ctrl+F5を使用してプロジェクトを実行しても、意図したとおりに動作しません。出力ファイルではなく、コンソールに出力されたコマンドライン引数を取得します。

arg1
arg2
>
output.txt

コマンドライン引数を次のように変更するとarg1 arg2 "> output.txt"、次の出力が得られます。

arg1
arg2
^> output.txt

output.txt出力フォルダーに空のファイルが作成されることに気付きました。

これを達成することは可能ですか、それとも cmd.exe を使用してプログラムを起動し続ける必要がありますか?

4

7 に答える 7

11

厳密には、リダイレクトされた出力でプログラムを起動するには、コマンド プロンプトを使用する必要があります。そうしないと、自分でコマンド ラインを解析する必要があり、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 & 一時停止"

これは、指定したすべての引数をアプリケーションに取得させる正しい方法です。

「続行するには何かキーを押してください...」かどうかをどのように検出できますかという以前の回答をご覧になることをお勧めします。表示されますか?.

ここでは、以下のコードで先祖返りのようなアプローチを使用しています。

  • アプリケーションコード

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

"devenv"デバッグ中は、親は Visual Studio IDE (現在の名前は) になるためです。実際、親と祖父母のプロセスはさまざまであり、いくつかのチェックを実行するためのルールが必要になります。

トリッキーな部分は、孫が実際に遭遇するものだということMainです。祖父母プロセスが実行されるたびに、そのコードをチェックします。祖父母が生成された場合、生成さnullれたプロセスは になります%comspec%。これは、現在の同じ実行可能ファイルで開始される新しいプロセスの親でもあります。したがって、祖父母がそれ自体と同じである場合、スポーンし続けることはなく、単にMain.

静的コンストラクターは、の前に開始されるコードで使用されますMain。SO に関する回答済みの質問があります:静的コンストラクターはどのように機能しますか? .

デバッグを開始すると、祖父母プロセス (生成されるプロセス) をデバッグしています。孫プロセスでデバッグするために、明確にするために、をDebugger.Launch呼び出す条件付きコンパイルで作成しました。MainMain

デバッガーに関する回答済みの質問も役立ちます: Attach debugger in C# to another process .

于 2013-04-29T22:38:17.200 に答える
9

これが 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

于 2013-04-28T11:43:39.340 に答える
3

コードを書く必要のない、より良い方法をお勧めします。

起動時の設定

Visual Studio を構成して、プログラムを外部プログラムとして開始するだけです。

于 2013-05-07T15:41:18.677 に答える
0

簡単な方法は次のとおりです。 プロジェクトを右クリック=>プロパティ==>デバッグ

ここに画像の説明を入力

そして、必ずプログラムをデバッグ モードで実行してください。

于 2018-03-01T14:31:07.753 に答える