tyr*_*ion 19 c# visual-studio visual-studio-2012
我试图将C#程序的输出重定向到文件.使用"cmd.exe"时,我可以简单地运行它myprogram.exe arg1 arg2 > out.txt,但我想使用Visual Studio 启动选项完成相同的操作.
我创建了一个C#Empty Project并添加了以下代码:
using System;
class Test
{
public static void Main(string[] args)
{
foreach (var arg in args) Console.WriteLine(arg);
}
}
Run Code Online (Sandbox Code Playgroud)
然后我在项目设置中编辑了命令行参数:

使用Ctrl + F5运行项目不能按预期工作.我得到控制台中打印的命令行参数,而不是输出文件:
arg1
arg2
>
output.txt
Run Code Online (Sandbox Code Playgroud)
如果我将命令行参数更改为:arg1 arg2 "> output.txt"我得到以下输出:
arg1
arg2
^> output.txt
Run Code Online (Sandbox Code Playgroud)
我注意到output.txt在Output文件夹中创建了一个空文件.
这件事有可能完成,还是我被迫继续使用cmd.exe来启动我的程序?
Ken*_*Kin 10
严格来说,您被迫使用命令提示符启动具有重定向输出的程序.否则,您需要自己解析命令行,GUI shell可能不会这样做.
如果你只是想在你的时候重定向输出Start Debugging,那么取消选中复选框Enable the Visual Studio hosting process,就完成了.
"output.txt"实际上,如果你没有,并且你已经在那里看到过,那么你的应用程序不是"YourApplication.vshost.exe"由Visual Studio IDE 生成的,而是在开始调试之前生成的.内容总是空的,不能写; 因为它被托管流程锁定了.

但是,如果您希望应用程序的行为与您启动它的模式相同,则事情会更复杂.
当您开始使用该应用程序进行调试时,它始于:
"YourApplication.exe"arg1 arg2
因为输出已被IDE重定向.
当你Start Without Debugging,它开始于:
"%comspec%"/ c""YourApplication.exe"arg1 arg2 ^> output.txt&pause"
这是让应用程序获取您指定的所有参数的正确方法.
你可能想看看我以前的答案如何检测"按任意键继续...".会显示吗?.
在这里,我在下面的代码中使用像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
}
}
Run Code Online (Sandbox Code Playgroud)我们还需要以下代码,以便它可以工作:
扩展代码方法
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)由于在"devenv"调试时父级将是Visual Studio IDE(当前已命名).父母和祖父母的过程实际上是各种各样的,我们需要一个规则来执行一些检查.
棘手的部分是孙子是真正遇到的Main.每次运行时,代码都会检查祖父进程.如果祖父母是null它产生的,那么产生的进程将是%comspec%,它也是新进程的父进程,它将以当前的相同可执行文件开始.因此,如果祖父母与自己相同,那么它将不会继续产卵,只是碰上了Main.
在静态构造函数中的代码,这是之前就开始使用Main.SO上有一个已回答的问题:静态构造函数如何工作?.
当我们开始调试时,我们正在调试祖父进程(生成).为了使用孙子进程进行调试,我使用Debugger.Launch了一个条件编译来调用Main,以便保持Main清楚.
有关调试器的已回答问题也很有用:将C#中的调试器附加到另一个进程.
我不确定这是否可以在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();
Run Code Online (Sandbox Code Playgroud)
http://msdn.microsoft.com/en-us/library/system.console.setout.aspx
| 归档时间: |
|
| 查看次数: |
10532 次 |
| 最近记录: |