严格来说,你不得不使用命令提示符来启动程序并重定向输出。否则,您需要自己解析命令行,GUI shell 可能不会这样做。
如果您只是想在 时重定向输出Start Debugging
,则取消选中 的复选框Enable the Visual Studio hosting process
,您就完成了。
如果您没有,并且"output.txt"
您在那里看到的,实际上不是由您的应用程序生成的,而是"YourApplication.vshost.exe"
在您开始调试之前由 Visual Studio IDE 生成的。内容永远是空的,不能写;因为它被Hosting Process锁定了。
但是,如果您希望应用程序的行为与您启动它的模式相同,那么事情会更加复杂。
当您开始使用应用程序进行调试时,它从以下内容开始:
“YourApplication.exe” arg1 arg2
因为输出已经被 IDE 重定向了。
当 you 时Start Without Debugging
,它的开头是:
"%comspec%" /c ""YourApplication.exe" arg1 arg2 ^>output.txt & pause"
这是让您的应用程序获取您指定的所有参数的正确方法。
您可能想看看我之前的回答如何检测“按任意键继续......” 会显示?.
在这里,我在下面的代码中使用了类似返祖回归的方法:
申请代码
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
了一个条件编译,它将调用Main
,以保持Main
清晰。
有关调试器的已回答问题也会有所帮助:将 C# 中的调试器附加到另一个进程。