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

时间:2009-10-06 21:18:46

标签: .net windows console console-application

  

相关:
  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提示符运行)时,我希望程序不显示控制台窗口。

我想避免这种情况:

alt text

有可能吗?

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

我在Windows上使用.NET。

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

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

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

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


此时我认为允许应用程序使用控制台的唯一方法是从cmd.exe调用它,而不是在双击时使用它,就是将exe定义为常规控制台exe(如果合适,/target:exe)和在启动时隐藏窗口。您仍然会暂时显示控制台窗口。

我仍然没有想过如何知道它是从explorer或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应用程序。

这对我有用。

6 个答案:

答案 0 :(得分:5)

所以,我用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。但是,创建控制台或 UI同样容易。

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:1)

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

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

就是这样。

答案 4 :(得分:0)

那么这更像是服务吗?

OR

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

答案 5 :(得分:0)

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

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启动代码将产生一个附加的进程,从commandprompt运行它确实激活了分支来清理我的混乱。 顺便说一句,从资源管理器或其他非控制台应用程序启动的控制台将具有零长度标题?