【问题标题】:Is it possible to build a Console app that does not display a console Window when double-clicked?是否可以构建双击时不显示控制台窗口的控制台应用程序?
【发布时间】:2010-12-04 10:19:43
【问题描述】:

相关:
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?

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

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

我想避免这种情况:

有可能吗?

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

我在 Windows 上使用 .NET。

编辑 2:this Old New Thing blog post 我学到了一些好东西。这就是我现在所知道的......

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

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

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


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

我还没有弄清楚如何知道它是从资源管理器还是 cmd.exe 启动的,但我越来越接近了..


答案

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

有可能构建一个控制台应用程序,它可以非常快速地隐藏其窗口,但速度不会很快,就好像窗口从未出现过一样。

现在,要确定控制台应用程序是否从资源管理器启动,一些人建议查看它正在运行的控制台
(来自mgb's answerKB article 99115):

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

这会告诉您该进程是否在其自己的控制台中启动,而不是它是否是资源管理器。在资源管理器中双击可以做到这一点,但应用程序中的 Start.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

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

  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);
  }

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

这对我有用。

【问题讨论】:

  • 您希望它做什么?
  • 判断程序是否在自己的控制台中启动的测试是错误的。在命令行输入cls & program.exe 将错误地表明程序是在其自己的控制台中启动的。您可以从批处理文件中执行相同的操作。可能还有其他方法可以击败测试。

标签: .net windows console console-application


【解决方案1】:

所以,我编写了带有 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;
}

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

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

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

【讨论】:

  • 这很有帮助 - 它让我可以在运行时创建或不创建控制台应用程序。关键是,如何确定控制台是否存在。我认为那是 AttachConsole(-1),但即使返回 0,我也不知道在控制台不存在时如何抑制它。我将不得不尝试一些事情。谢谢
【解决方案2】:

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

【讨论】:

  • 再想一想,我认为这行不通。我知道如何在运行时使用 AttachConsole() 上的 pinvoke 将 EXE 制作为控制台应用程序或 WinForms 应用程序。但我不知道如何确定何时将其设为控制台应用程序或 Winforms 应用程序。我不知道如何检测它是通过在资源管理器中双击启动的,还是从控制台窗口启动的。
  • 为什么不使用命令行参数来选择控制台显示模式?这样从资源管理器运行时,它不会有一个;除非您创建快捷方式并在其中添加参数 - 允许您从资源管理器启动和不启动控制台。
  • 这对我来说不太合适。首先是因为我不希望行为是命令行可选择的。我希望它是自动的。其次,因为使用父母的控制台与普通的控制台应用程序不同。请参阅 Old New Thing blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx 的博客文章了解原因。
  • 这个解决方案怎么样? stackoverflow.com/a/66607616/463571
  • @AntonNorko:这基本上我在 2009 年给出的解决方案。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    您可以构建没有控制台窗口的 Windows 应用程序:

    转到项目属性 > 应用程序并将输出类型从 Console 更改为 Windows Application

    就是这样。

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        这会更像是一项服务吗?

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

        【讨论】:

          猜你喜欢
          • 2011-04-04
          • 2019-01-18
          • 1970-01-01
          • 2017-09-09
          • 2015-04-02
          • 1970-01-01
          • 2012-06-21
          • 2020-05-05
          • 1970-01-01
          相关资源
          最近更新 更多