When running a console application in Visual Studio, depending on your settings, it will add a prompt after the program exits:
Press any key to continue . . .
I have found how to detect if I am running under the debugger(use Debugger.IsAttached
), but it isn't helpful. Press CTRL-F5 to Start Without Debugging sets this flag to false
, yet still shows the prompt.
I want to detect this because I'd like to display my own message and wait for a keypress, but not double up keypress checks.
I don't want to muck with my general Visual Studio settings. If I can disable it for this project in a way that can be checked into source control, that would also work.
What mechanism is used to append this prompt, and how do I detect it?
Or how do I disable it per-project, and check this change into source control?
Add the following code to the console application:
public static class Extensions {
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);
public static Process GetParentProcess(this Process x) {
return (
from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
where (uint)it["ProcessId"]==x.Id
select Process.GetProcessById((int)(uint)it["ParentProcessId"])
).First();
}
public static IEnumerable<Process> GetChildProcesses(this Process x) {
return (
from it in (new ManagementObjectSearcher("root\\CIMV2", "select * from Win32_Process")).Get().Cast<ManagementObject>()
where (uint)it["ParentProcessId"]==x.Id
select Process.GetProcessById((int)(uint)it["ProcessId"])
);
}
public static void Abort(this ProcessThread x) {
TerminateThread(OpenThread(1, false, (uint)x.Id), 1);
}
}
And then modify your code like this:
class Program {
static void Main(String[] args) {
// ... (your code might goes here)
try {
Process.GetCurrentProcess().GetParentProcess().Threads.Cast<ProcessThread>().Single().Abort();
}
catch(InvalidOperationException) {
}
Console.Write("Press ONLY key to continue . . . ");
Console.ReadKey(true);
}
}
So, everything we are expecting is done now. I consider this as a workaround solution. It works under Windows XP SP3
and I guess that it would work with newer Windows operating systems. Under Visual Studio, applications are always a spawned process. In older Visual C++ 6.0, it spawned by the IDE by calling VCSPAWN.EXE
; in Visual Studio 2010, your application runs with following command line when Start Without Debugging:
"%comspec%" /c ""your application filename" & pause"
So it is impossible to reach the goal in fully managed ways; because it was NOT under the application domain.
Here we use the managed way of WMI
to enumerate the processes, and encapsulate the unmanaged WINAPI
s to terminate the ProcessThread
s, because the ProcessThread
is not supposed to be normally aborted; it's provided like something for read-only.
As mentioned above, the application was spawned with the particular command line; it would have a single thread creates a single process signature, so we used the Single()
method to retrieve that thread and terminate it.
When we start the application under an existing command prompt, it is just the same scenario of Start Without Debugging. Moreover, when Start Debugging, the application process is created by devenv.exe
. It has a lot of threads, we known that and won't abort any thread, just prompt and wait for a key press. This situation is similar to starting application with double-clicking or from context menu. This way, the application process is created by the system shell, usually Explorer.exe
and it also has a lots of threads.
In fact, if we can successfully abort the thread it implies that we have the permissions to kill the parent process. But we do NOT need to. We just need to abort the only thread, the process terminates automatically by system when it has no more threads. Killing the parent process by identifying that the calling process is %comspec%
, is another way to do the same thing, but it's a dangerous procedure. Because the process spawning the application might have other threads which have any number of threads create a process matches %comspec%
. You may kill a critical work of process with carelessness or just growing the complexity of checking whether the process is safe to kill. So I consider a single thread creates a single process as a signature of our parent process which is safe to kill/abort.
WMI
is modern, some of WINAPI
s might become deprecated in the future. But the real reason of this composition is for its simplicity. The old Tool Help Library
is such complicated like the ways to convert ProcessThread
to System.Threading.Thread
. With LINQ and extension methods, we can make the code simpler and more semantical.
Sounds like this prompt is provided by the pause
command. This command is automatically added by Visual Studio.
When you run your project outside of Visual Studio, there is no reason to "detect" this command. You can safely assume that it will not be added to your program. This means you can go ahead and add whatever prompt you want, similar to:
Console.WriteLine("Press any key...");
Console.Read();
See this question.
Here is a piece of code that should do it:
class Program
{
static void Main(string[] args)
{
// do your stuff
if (!WasStartedWithPause())
{
Console.WriteLine("Press any key to continue . . . ");
Console.ReadKey(true);
}
}
}
public static bool WasStartedWithPause()
{
// Here, I reuse my answer at http://stackoverflow.com/questions/394816/how-to-get-parent-process-in-net-in-managed-way
Process parentProcess = ParentProcessUtilities.GetParentProcess();
// are we started by cmd.exe ?
if (string.Compare(parentProcess.MainModule.ModuleName, "cmd.exe", StringComparison.OrdinalIgnoreCase) != 0)
return false;
// get cmd.exe command line
string cmdLine = GetProcessCommandLine(parentProcess);
// was it started with a pause?
return cmdLine != null & cmdLine.EndsWith("& pause\"");
}
public static string GetProcessCommandLine(Process process)
{
if (process == null)
throw new ArgumentNullException("process");
// use WMI to query command line
ManagementObjectCollection moc = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId=" + process.Id).Get();
foreach (ManagementObject mo in moc)
{
return (string)mo.Properties["CommandLine"].Value;
}
return null;
}
That message has nothing to do with your program. You can add to your program anything you like and it will perform in the same way as it would, if you were to run it from the command prompt.
Visual studio displays it for the purpose of showing you that its execution has finished running, so you know it ended correctly. If you wish to skip it you could try to "run without debugging" (or something like that; it's just below "run with debugger").
This prompt is given when the command
system("pause")
is used.
Even I faced this problem. You can either use the function _getch() under conio.h for waiting for key press.
So you can use the following code:
cout<<"your message here"
_getch()
This would wait for the key press and display your own prompt.
This is the output of "pause" command which is added by visual studio. If you see this, the program is ended. Which comes to a question like is it possible for an application to detect that it self is ended. I think this is not logical.