windows 是否可以构建一个双击时不显示控制台窗口的控制台应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1528152/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 13:14:50  来源:igfitidea点击:

Is it possible to build a Console app that does not display a console Window when double-clicked?

.netwindowsconsoleconsole-application

提问by Cheeso

Related:
Should I include a command line mode in my applications?
How to grab parent process standard output?
Can a console application detect if it has been run from Explorer?

相关:
我应该在我的应用程序中包含命令行模式吗?
如何抓取父进程的标准输出?
控制台应用程序是否可以检测它是否已从资源管理器中运行?

I want to build a console app, that is normally run from the command line.

我想构建一个控制台应用程序,通常从命令行运行。

But, when it is double clicked from within Explorer (as opposed to being run from a cmd.exe prompt) then I'd like the program to NOT display a console window.

但是,当它从资源管理器中双击时(而不是从 cmd.exe 提示符运行),那么我希望程序不显示控制台窗口。

I want to avoid this:

我想避免这种情况:

alt text

替代文字

Is it possible?

是否可以?

EDITI guess another way to ask it is, is it possible for a program to know how it was invoked - whether by double-click or by command line?

编辑我想另一种问法是,程序是否有可能知道它是如何被调用的——无论是通过双击还是通过命令行

I'm working in .NET, on Windows.

我在 .NET 上工作,在 Windows 上。

EDIT 2:From this Old New Thingblog postI learned some good stuff. Here's what I know now...

编辑 2:这篇旧的新事物博客文章中,我学到了一些好东西。这是我现在所知道的......

In Windows, EXE files are marked as either GUI or non-GUI. With csc.exe, this is selected with /target:winexeor /target:exe. Before the first instruction in the process executes, the Windows kernel sets up the execution environment. At that moment, if the EXE is marked GUI, the kernel sets the stdin/stdout for the process to NULL, and if non-GUI (command-line) the kernel creates a console and sets the stdin/stdout for the process to that console.

在 Windows 中,EXE 文件被标记为 GUI 或非 GUI。对于 csc.exe,使用/target:winexe或选择/target:exe。在进程中的第一条指令执行之前,Windows 内核设置了执行环境。此时,如果 EXE 标记为 GUI,则内核将进程的 stdin/stdout 设置为 NULL,如果非 GUI(命令行),内核将创建一个控制台并将进程的 stdin/stdout 设置为安慰。

When launching the process, if there is no stdin/stdout (== /target:winexe), then the call immediately returns. So, launching a gui app from a cmd.exe, you will immediately get your cmd prompt back. If there is a stdin/stdout, and if run from cmd.exe, then the parent cmd.exe waits for process exit.

启动进程时,如果没有 stdin/stdout (== /target:winexe),则调用立即返回。因此,从 cmd.exe 启动 gui 应用程序,您将立即返回 cmd 提示符。如果存在标准输入/标准输出,并且从 cmd.exe 运行,则父 cmd.exe 等待进程退出。

The "immediate return" is important because if you code a GUI app to attach to its parent's console, you will be able to do console.writeline, etc. But the cmd.exe prompt is active. The user can type new commands, start a new process, and so on. In other words, from a winexe, simply attaching to the parent console with AttachConsole(-1)will not "turn it into" a console app.

“立即返回”很重要,因为如果您编写一个 GUI 应用程序以附加到其父级的控制台,您将能够执行 console.writeline 等操作。但 cmd.exe 提示处于活动状态。用户可以键入新命令、启动新进程等。换句话说,从 winexe 中,简单地附加到父控制台AttachConsole(-1)不会将其“变成”控制台应用程序。



At this point I think the only way to allow an app to use the console if it is invoked from cmd.exe, and NOT use it if it is double-clicked, is to define the exe as a regular console exe (/target:exe), and hide the windowon startup if appropriate. You still get a console window appearing briefly.

在这一点上,我认为允许应用程序在从 cmd.exe 调用时使用控制台的唯一方法是将 exe 定义为常规控制台 exe ( /target:exe),并且如果合适,在启动时隐藏窗口。您仍然会看到一个短暂出现的控制台窗口。

I still haven't figured how to know whether it was launched from explorer or cmd.exe, but I'm getting closer..

我仍然不知道如何知道它是从资源管理器还是 cmd.exe 启动的,但我越来越接近了..



ANSWERS

答案

It is not possible to build a console app that does not display a console window.

无法构建不显示控制台窗口的控制台应用程序。

It is possibleto build a console app that hides its window very quickly, but not so quickly that it is as if the window never appears.

是可能的构建非常迅速隐藏其窗口控制台应用程序,但不能如此之快,这是因为如果从来没有出现的窗口。

Now, to determine whether a console app was launched from explorer, some have suggested to look at the console it is running in
(from mgb's answer, and KB article 99115) :

现在,要确定控制台应用程序是否是从资源管理器启动的,有些人建议查看它正在运行的控制台
(来自mgb 的回答知识库文章 99115):

  int left = Console.CursorLeft;
  int top = Console.CursorTop;
  bool ProcessWasRunFromExplorer = (left==0 && top==0);

This tells you if the process was launched in its own console, but not whether it was explorer. A double click in explorer would do this, but also a Start.Process() from within an app would do the same thing.

这会告诉您该进程是否在其自己的控制台中启动,而不是它是否是资源管理器。在资源管理器中双击可以执行此操作,但应用程序中的 Start.Process() 也可以执行相同的操作。

If you want to treat those situations differently, use this to learn the name of the parent process:

如果您想区别对待这些情况,请使用它来了解父进程的名称:

  System.Console.WriteLine("Process id: {0}", Process.GetCurrentProcess().Id);
  string name = Process.GetCurrentProcess().ProcessName ;
  System.Console.WriteLine("Process name: {0}", name);
  PerformanceCounter pc = new PerformanceCounter("Process", "Creating Process Id", name);
  Process p = Process.GetProcessById((int)pc.RawValue);
  System.Console.WriteLine("Parent Process id: {0}", p.Id);
  System.Console.WriteLine("Parent Process name: {0}", p.ProcessName);

  // p.ProcessName == "cmd" or "Explorer" etc

To hide the window quickly after the process is launched, use this:

要在进程启动后快速隐藏窗口,请使用以下命令:

  private static readonly int SW_HIDE= 0;

  [System.Runtime.InteropServices.DllImport("user32.dll")]
  private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

  ....
  {
    IntPtr myHandle = Process.GetCurrentProcess().MainWindowHandle;
    ShowWindow(myHandle, SW_HIDE);
  }

If you produce a winexe(a WinForms app), and optionally attach to the parent console when appropriate with AttachConsole(-1), you do not get the equivalent of a regular console app. For a winexe, the parent process (like cmd.exe) will return to the command prompt immediately after starting a GUI application. In other words, the command prompt is active and ready for input while the just-launched process may be emitting output. This is confusing and is probably useful only for debugging winforms apps.

如果您生成一个winexe(一个 WinForms 应用程序),并在适当的时候使用 可选地附加到父控制台AttachConsole(-1),则您不会获得与常规控制台应用程序等效的内容。对于 winexe,父进程(如 cmd.exe)将在启动 GUI 应用程序后立即返回命令提示符。换句话说,命令提示符处于活动状态并准备好输入,而刚刚启动的进程可能正在发出输出。这令人困惑,并且可能仅对调试 winforms 应用程序有用。

This worked for me.

这对我有用。

采纳答案by Martin Beckett

See Can a Win32 console application detect if it has been run from the explorer or not?

请参阅Win32 控制台应用程序能否检测到它是否已从资源管理器中运行?

Or I think the official way is to check the parent process is cmd.exe or explorer.exe

或者我认为官方的方法是检查父进程是cmd.exe还是explorer.exe

回答by neminem

So, I've written tools with both a GUI and a CLI. The hard part was figuring out which one to open - in our case, though, the CLI version had required parameters, so I just opened the GUI if there weren't any parameters. Then, if they did want a console, call a function that looks something like:

所以,我已经用 GUI 和 CLI 编写了工具。困难的部分是弄清楚要打开哪个 - 但是,在我们的例子中,CLI 版本需要参数,所以如果没有任何参数,我只是打开 GUI。然后,如果他们确实想要一个控制台,请调用一个类似于以下内容的函数:

private const int ATTACH_PARENT_PROCESS = -1;
private const int ERROR_INVALID_HANDLE = 6;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll")]
static extern bool AllocConsole();
[DllImport("kernel32.dll")]
static extern bool FreeConsole();

private static bool StartConsole()
{
  if (!AttachConsole(ATTACH_PARENT_PROCESS)) // try connecting to an existing console  
  {  
      if (Marshal.GetLastWin32Error() == ERROR_INVALID_HANDLE) // we don't have a console yet  
      {  
          if (!AllocConsole()) // couldn't create a new console, either  
              return false;  
      }
      else
          return false; // some other error
  }
  return true;
}

Returns whether the console was created. Don't forget to FreeConsole() when you're done!

返回控制台是否已创建。完成后不要忘记 FreeConsole() !

In our case, of course, if we don't create a console, we create a GUI. It'd be just as easy to create either a console or noUI, though.

在我们的例子中,当然,如果我们不创建控制台,我们会创建一个 GUI。不过,创建控制台或创建UI都一样容易。

EDIT: That totally didn't answer the question in the edit that wasn't there when I started writing that, of course. Other than that our hack was just checking whether it was called with command-line parameters or not.

编辑:当然,这完全没有回答我开始编写时不存在的编辑中的问题。除此之外,我们的 hack 只是检查是否使用命令行参数调用它。

回答by Jon Skeet

Just build it as a Windows Forms app, but don't give it a GUI. Unfortunately then you won't get any console output when it's run from the command line either... is that a problem?

只需将其构建为 Windows 窗体应用程序,但不要为其提供 GUI。不幸的是,当它从命令行运行时,你也不会得到任何控制台输出......这是一个问题吗?

回答by Aaron Hoffman

Would this be more like a Service then?

这会更像是一项服务吗?

OR

或者

What About a Windows Forms application that doesn't have a visible form? It would still show up in the Task Manager Processes list.

没有可见窗体的 Windows 窗体应用程序怎么办?它仍会显示在任务管理器进程列表中。

回答by Jan

I haven't read everything thru, but did this (a little while ago, more testing needed):

我还没有通读所有内容,但这样做了(不久前,需要更多测试):

DWORD proc[2],procsfound=GetConsoleProcessList(proc,ELEMS(proc));
if (procsfound>1)
// I'm started as a command in cmd.exe
else
// started from explorer or other non-console parent

IFF there's more than one proc attached, I need to restore the console which I manipulated, otherwise not. May be useful, at least it's simplicity itself. Start the code from VS will yield a single attached proces, running it from commandprompt did activate the branch to clean up my mess. Btw, a console launched from explorer or other non-console app will have a zero-length title?

IFF 附加了不止一个 proc,我需要恢复我操纵的控制台,否则不会。可能有用,至少它本身很简单。从 VS 启动代码将产生一个附加的过程,从命令提示符运行它确实激活了分支以清理我的烂摊子。顺便说一句,从资源管理器或其他非控制台应用程序启动的控制台的标题长度为零?