C# 如何使用Visual Studio将C“项目的标准输出重定向到文件”;命令行参数";选项
我试图将C#程序的输出重定向到一个文件。使用“cmd.exe”时,我可以简单地使用C# 如何使用Visual Studio将C“项目的标准输出重定向到文件”;命令行参数";选项,c#,visual-studio,visual-studio-2012,C#,Visual Studio,Visual Studio 2012,我试图将C#程序的输出重定向到一个文件。使用“cmd.exe”时,我可以简单地使用myprogram.exe arg1 arg2>out.txt运行它,但我希望使用Visual Studio启动选项完成同样的任务 我创建了一个C#空项目并添加了以下代码: 使用系统; 课堂测试 { 公共静态void Main(字符串[]args) { foreach(args中的var arg)控制台。WriteLine(arg); } } 然后,我在项目设置中编辑了命令行参数: 使用Ctrl+F5运行项目无
myprogram.exe arg1 arg2>out.txt运行它,但我希望使用Visual Studio启动选项完成同样的任务
我创建了一个C#空项目并添加了以下代码:
使用系统;
课堂测试
{
公共静态void Main(字符串[]args)
{
foreach(args中的var arg)控制台。WriteLine(arg);
}
}
然后,我在项目设置中编辑了命令行参数:
使用Ctrl+F5运行项目无法正常工作。我将命令行参数打印在控制台中,而不是输出文件中:
arg1
arg2
>
output.txt
如果我将命令行参数更改为:arg1 arg2>output.txt“
我将获得以下输出:
arg1
arg2
^> output.txt
我注意到在output文件夹中创建了一个空的output.txt
文件
这是可以完成的,还是我被迫继续使用cmd.exe来启动我的程序?我不确定这是否可以在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();
您可以在外部编辑器中打开.csproj.user
,并将StartTargetments
更改为:
<StartArguments>arg1 arg2 > output.txt</StartArguments>
arg1 arg2 output.txt
到
arg1 arg2>output.txt
在“开始选项”部分,以这种方式更改命令行参数文本框
args1 args2 1>output.txt
这将重定向标准输出(1),创建名为output.txt的文件
如果要附加到文件的早期版本,请写入
args1 args2 1>>output.txt
现在,当输出控制台被重定向时,您可以一步一步地调试程序。严格来说,您必须使用命令提示符以重定向的输出启动程序。否则,您将需要自己解析命令行,GUI shell可能不会这样做
如果您只想在开始调试时重定向输出
,则取消选中启用Visual Studio宿主进程
的复选框,即可完成
如果没有,那么您在那里看到的“output.txt”
实际上不是由应用程序生成的,而是由Visual Studio IDE在开始调试之前生成的“YourApplication.vshost.exe”
。内容总是空的,不能写;因为它被门锁上了
但是,如果您希望应用程序在任何启动模式下都保持相同的行为,那么事情就更复杂了
当您开始调试应用程序时,它将从以下内容开始:
“YourApplication.exe”arg1 arg2
因为IDE已经重定向了输出
当您在不调试的情况下启动时,
将从以下内容启动:
%comspec%/c“YourApplication.exe”arg1 arg2^>output.txt暂停(&p)
这是让应用程序获取您指定的所有参数的正确方法
你可能想看看我之前的答案
在这里,我使用的方法如下所示:
- 应用程序代码
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);
}
}
}
使用系统管理;//添加参考是必需的
使用System.Runtime.InteropServices;
使用系统诊断;
使用System.Collections.Generic;
使用System.Linq;
使用制度;
公共静态局部类NativeMethods{
[DllImport(“kernel32.dll”)]
公共静态外部bool TerminateThread(
IntPtr-hThread,uint-dwExitCode);
[DllImport(“kernel32.dll”)]
公共静态外部IntPtr OpenThread(
uint dwDesiredAccess、bool bInheritHandle、uint dwThreadId);
}
公共静态部分类ProcessThreadExtensions/*公共方法*/{
公共静态无效中止(此进程线程t){
NativeMethods.TerminateThread(
OpenThread(1,false,(uint)t.Id),1);
}
公共静态IEnumerable GetChildProcess(此进程p){
返回p.getprocesss(1);
}
公共静态进程GetParentProcess(此进程p){
返回p.GetProcesses(-1).SingleOrDefault();
}
}
部分类ProcessThreadExtensions/*非公共方法*/{
静态IEnumerable进程(
这个过程(p,int方向){
返回
从新[]{
“从Win32_进程中选择{0},其中{1}”
让selectName=direction如果要将输出通过管道传输到文件,则必须继续使用cmd.exe。命令行参数:用于命令行参数,因此您尝试的任何内容都将被转义为命令行参数。管道和重定向器不是命令行参数,因此它们将被转义
我只需要创建一个.bat文件来调用我喜欢的程序。你可以将它固定到任务栏上,然后简单地运行它。我建议一种更好的方法,而不需要编写任何代码
只需将visual studio配置为作为外部程序启动您的程序。简单的方法是:
右键单击项目=>属性=>调试
然后确保在调试模式下运行程序,很遗憾,这在Visual Studio 2012中不起作用。如果从Main()打印args[0],它将在控制台窗口中打印1>output.txt
。我刚刚意识到-只要您至少有一个参数,它就可以工作。如果您只输入>output.txt
它就不工作了…等等{edit}仍然不起作用…嗯,我发现了它是什么-如果按F5在调试器下运行,它会起作用。如果按Ctrl-F5在调试器外运行(但仍从VS启动),则它不起作用。请注意
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);
}
}
}