24

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.

I want to avoid this:

alt text

Is it possible?

EDIT I 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.

EDIT 2: From this Old New Thing blog post I learned some good stuff. Here's what I know now...

In Windows, EXE files are marked as either GUI or non-GUI. With csc.exe, this is selected with /target:winexe or /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.

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.

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.


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 window on startup if appropriate. You still get a console window appearing briefly.

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


ANSWERS

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

It is possible to 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) :

  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.

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.

This worked for me.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Cheeso
  • 189,189
  • 101
  • 473
  • 713
  • What would you like it to do instead? – Andrew Hare Oct 06 '09 at 21:21
  • The test to determine if the program was started in its own console is wrong. Entering `cls & program.exe` at the command line will erroneously state that the program was started in its own console. You could do the same thing from a batch file. Likely there are other ways to defeat the test. – Jim Mischel Apr 04 '13 at 18:36

6 Answers6

7

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:

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!

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 no UI, though.

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.

neminem
  • 2,658
  • 5
  • 27
  • 36
  • This is helpful - it lets me create a console app or not at runtime. The key thing is, how to determine whether a console is present. I think that is AttachConsole(-1), but even when that returns 0, I don't know how to suppress the console when it is not present. I'll have to try a few things. thanks – Cheeso Oct 06 '09 at 22:01
6

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?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thinking about this a little more, I don't think this will work. I know how to make an EXE either a console app or a WinForms app at runtime, using pinvoke on AttachConsole(). But I don't know how to determine when to make it a console app or a Winforms app. I don't know how to detect if it was lauched by a double-click in explorer, or launched from a console window. – Cheeso Oct 06 '09 at 21:44
  • Why not use a command line argument to select console display mode? That way when run from Explorer, it won't have one; unless you create a shortcut and add the argument there - allowing you to have both with and without console launches from Explorer. – Clifford Oct 06 '09 at 21:52
  • That doesn't quite do it for me. First because I don't want the behavior to be command-line selectable. I want it to be automatic. Second because using the parent's console isn't the same as being a regular console app. See the blog post from Old New Thing http://blogs.msdn.com/oldnewthing/archive/2009/01/01/9259142.aspx for an explanation of why. – Cheeso Oct 07 '09 at 00:03
  • How about this solution? https://stackoverflow.com/a/66607616/463571 – Anton Norko Mar 12 '21 at 21:47
  • @AntonNorko: That basically *is* the solution I gave in 2009. – Jon Skeet Mar 13 '21 at 07:00
3

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

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

Community
  • 1
  • 1
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
3

You can build Windows App which does not have a console window:

Go to project properties > Application and change output type from Console to Windows Application.

That's it.

Anton Norko
  • 2,166
  • 1
  • 15
  • 20
1

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.

bluish
  • 26,356
  • 27
  • 122
  • 180
Aaron Hoffman
  • 6,604
  • 8
  • 56
  • 61
0

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?

vidit
  • 6,293
  • 3
  • 32
  • 50
Jan
  • 447
  • 6
  • 8