我想创建一个可以作为CLI或GUI应用程序运行的C#程序,具体取决于传递给它的标志.可以这样做吗?
我找到了这些相关的问题,但它们并不完全涵盖我的情况:
如何在GUI应用程序中写入控制台
如何使用Windows程序在C++中获得控制台输出?
Rob Kennedy.. 87
Jdigital的答案指向Raymond Chen的博客,该博客解释了为什么你不能拥有一个既是控制台程序又是非控制台*
程序的应用程序:操作系统需要在程序开始运行之前知道要使用哪个子系统.程序开始运行后,返回并请求其他模式为时已晚.
Cade的回答指向一篇关于使用控制台运行.Net WinForms应用程序的文章.它使用AttachConsole
程序开始运行后调用的技术.这具有允许程序写回启动程序的命令提示符的控制台窗口的效果.但是那篇文章中的评论指出了我认为是一个致命的缺陷:子进程并没有真正控制控制台.控制台继续代表父进程接受输入,并且父进程在将控制台用于其他事情之前不知道它应该等待子进程完成运行.
陈的文章指出张俊峰的一篇文章解释了其他几种技巧.
第一个是devenv使用的.它实际上有两个程序.一个是devenv.exe,它是主要的GUI程序,另一个是devenv.com,它处理控制台模式任务,但如果它以非控制台方式使用,它会将其任务转发到devenv.exe和退出.该技术依赖于Win32规则,当您键入没有文件扩展名的命令时,com文件会在exe文件之前被选中.
Windows Script Host有一个更简单的变体.它提供了两个完全独立的二进制文件,wscript.exe和cscript.exe.同样,Java 为控制台程序提供java.exe,为非控制台程序提供javaw.exe.
Junfeng的第二种技术是ildasm使用的技术.他引用了ildasm的作者在两种模式下运行时所经历的过程.最终,这就是它的作用:
该程序被标记为控制台模式二进制文件,因此它始终以控制台开始.这允许输入和输出重定向正常工作.
如果程序没有控制台模式命令行参数,则会重新启动.
仅仅调用FreeConsole
使第一个实例不再是控制台程序是不够的.那是因为启动程序的进程cmd.exe "知道"它启动了一个控制台模式程序并且正在等待程序停止运行.调用FreeConsole
会使ildasm停止使用控制台,但它不会使父进程开始使用控制台.
所以第一个实例重新启动(我猜想有一个额外的命令行参数).当您调用时CreateProcess
,有两个不同的标志要尝试,DETACHED_PROCESS
并且CREATE_NEW_CONSOLE
,其中任何一个都将确保第二个实例不会附加到父控制台.之后,第一个实例可以终止并允许命令提示符恢复处理命令.
这种技术的副作用是,当您从GUI界面启动程序时,仍然会有一个控制台.它会在屏幕上瞬间闪烁然后消失.
Junfeng的文章中关于使用editbin更改程序控制台模式标志的部分是一个红色的鲱鱼,我想.您的编译器或开发环境应提供设置或选项来控制它创建的二进制类型.之后不需要修改任何东西.
那么,底线是你可以有两个二进制文件,或者你可以暂时忽略控制台窗口.一旦你决定哪个是较小的邪恶,你有你选择的实现.
*
我说非控制台而不是GUI,因为否则它是错误的二分法.仅仅因为程序没有控制台并不意味着它有一个GUI.服务应用程序就是一个很好的例子.此外,程序可以有一个控制台和窗口.
Jdigital的答案指向Raymond Chen的博客,该博客解释了为什么你不能拥有一个既是控制台程序又是非控制台*
程序的应用程序:操作系统需要在程序开始运行之前知道要使用哪个子系统.程序开始运行后,返回并请求其他模式为时已晚.
Cade的回答指向一篇关于使用控制台运行.Net WinForms应用程序的文章.它使用AttachConsole
程序开始运行后调用的技术.这具有允许程序写回启动程序的命令提示符的控制台窗口的效果.但是那篇文章中的评论指出了我认为是一个致命的缺陷:子进程并没有真正控制控制台.控制台继续代表父进程接受输入,并且父进程在将控制台用于其他事情之前不知道它应该等待子进程完成运行.
陈的文章指出张俊峰的一篇文章解释了其他几种技巧.
第一个是devenv使用的.它实际上有两个程序.一个是devenv.exe,它是主要的GUI程序,另一个是devenv.com,它处理控制台模式任务,但如果它以非控制台方式使用,它会将其任务转发到devenv.exe和退出.该技术依赖于Win32规则,当您键入没有文件扩展名的命令时,com文件会在exe文件之前被选中.
Windows Script Host有一个更简单的变体.它提供了两个完全独立的二进制文件,wscript.exe和cscript.exe.同样,Java 为控制台程序提供java.exe,为非控制台程序提供javaw.exe.
Junfeng的第二种技术是ildasm使用的技术.他引用了ildasm的作者在两种模式下运行时所经历的过程.最终,这就是它的作用:
该程序被标记为控制台模式二进制文件,因此它始终以控制台开始.这允许输入和输出重定向正常工作.
如果程序没有控制台模式命令行参数,则会重新启动.
仅仅调用FreeConsole
使第一个实例不再是控制台程序是不够的.那是因为启动程序的进程cmd.exe "知道"它启动了一个控制台模式程序并且正在等待程序停止运行.调用FreeConsole
会使ildasm停止使用控制台,但它不会使父进程开始使用控制台.
所以第一个实例重新启动(我猜想有一个额外的命令行参数).当您调用时CreateProcess
,有两个不同的标志要尝试,DETACHED_PROCESS
并且CREATE_NEW_CONSOLE
,其中任何一个都将确保第二个实例不会附加到父控制台.之后,第一个实例可以终止并允许命令提示符恢复处理命令.
这种技术的副作用是,当您从GUI界面启动程序时,仍然会有一个控制台.它会在屏幕上瞬间闪烁然后消失.
Junfeng的文章中关于使用editbin更改程序控制台模式标志的部分是一个红色的鲱鱼,我想.您的编译器或开发环境应提供设置或选项来控制它创建的二进制类型.之后不需要修改任何东西.
那么,底线是你可以有两个二进制文件,或者你可以暂时忽略控制台窗口.一旦你决定哪个是较小的邪恶,你有你选择的实现.
*
我说非控制台而不是GUI,因为否则它是错误的二分法.仅仅因为程序没有控制台并不意味着它有一个GUI.服务应用程序就是一个很好的例子.此外,程序可以有一个控制台和窗口.
查看Raymond关于此主题的博客:
http://blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx
他的第一句话是:"你不能,但你可以试着伪造它."
http://www.csharp411.com/console-output-from-winforms-application/
只需检查WinForms之前的命令行参数Application.
.
我应该补充一点,在.NET中,简单地在同一个解决方案中创建一个控制台和GUI项目非常容易,这些解决方案共享除main之外的所有程序集.在这种情况下,如果没有参数启动,您可以使命令行版本只启动GUI版本.你会得到一个闪烁的控制台.
有一种简单的方法可以做你想要的.在编写应该同时具有CLI和GUI的应用程序时,我总是使用它.您必须将"OutputType"设置为"ConsoleApplication"才能生效.
class Program { [DllImport("kernel32.dll", EntryPoint = "GetConsoleWindow")] private static extern IntPtr _GetConsoleWindow(); ////// The main entry point for the application. /// [STAThread] static void Main(string[] args) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); /* * This works as following: * First we look for command line parameters and if there are any of them present, we run the CLI version. * If there are no parameters, we try to find out if we are run inside a console and if so, we spawn a new copy of ourselves without a console. * If there is no console at all, we show the GUI. * We make an exception if we find out, that we're running inside visual studio to allow for easier debugging the GUI part. * This way we're both a CLI and a GUI. */ if (args != null && args.Length > 0) { // execute CLI - at least this is what I call, passing the given args. // Change this call to match your program. CLI.ParseCommandLineArguments(args); } else { var consoleHandle = _GetConsoleWindow(); // run GUI if (consoleHandle == IntPtr.Zero || AppDomain.CurrentDomain.FriendlyName.Contains(".vshost")) // we either have no console window or we're started from within visual studio // This is the form I usually run. Change it to match your code. Application.Run(new MainForm()); else { // we found a console attached to us, so restart ourselves without one Process.Start(new ProcessStartInfo(Assembly.GetEntryAssembly().Location) { CreateNoWindow = true, UseShellExecute = false }); } } }