This question already has answers here:
Capture console exit C#
(10 answers)
Closed 8 years ago.
I am looking for a way to trigger a piece of code when a console application is manually closed (users closes window). Been trying with:
AppDomain.CurrentDomain.ProcessExit +=
new EventHandler(CurrentDomain_ProcessExit);
but the above doesn't work if manually closed.
Is there any ways to use a .Net call for this or do I need to import the Kernel dll and do it that way?
This code works to catch the user closing the console window:
using System;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
handler = new ConsoleEventDelegate(ConsoleEventCallback);
SetConsoleCtrlHandler(handler, true);
Console.ReadLine();
}
static bool ConsoleEventCallback(int eventType) {
if (eventType == 2) {
Console.WriteLine("Console window closing, death imminent");
}
return false;
}
static ConsoleEventDelegate handler; // Keeps it from getting garbage collected
// Pinvoke
private delegate bool ConsoleEventDelegate(int eventType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
}
Beware of the restrictions. You have to respond quickly to this notification, you've got 5 seconds to complete the task. Take longer and Windows will kill your code unceremoniously. And your method is called asynchronously on a worker thread, the state of the program is entirely unpredictable so locking is likely to be required. Do make absolutely sure that an abort cannot cause trouble. For example, when saving state into a file, do make sure you save to a temporary file first and use File.Replace().
You need to hook to console exit event and not your process.
http://geekswithblogs.net/mrnat/archive/2004/09/23/11594.aspx
Capture console exit C#
Related
I have already read this similar quesiton but the solution accepted in that question is not working for me.
I have a WinForm application (called FormPlusConsoleApp) that works as a console application if some arguments are passed by the calling program.
I need to provide status messages from the program FormPlusConsoleApp to the console therefore, I attach to the console at the very beginning of the program.
PROBLEM: After executing the required job, the program should exit with an exit code 0/1 and should completely detach to the console without any user interaction.
I am already calling the FreeConsole() method as suggested in this similar quesiton but at the end, a blinking cursor appears on the command prompt (as shown in the screenshot) and the user must press a button to completely exit.
Sample Working Program:
using System;
using System.Windows.Forms;
using System.IO;
namespace FormPlusConsoleApp
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
System.Windows.Forms.MessageBox.Show("Attach the debugger now...");
if (args.Length == 0) //form version
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormApp());
}
else //start console version
{
// case args.Length > 0
SampleConsoleExecution();
}
}
static void SampleConsoleExecution()
{
//attach to the console
bool isAttached = AttachConsole(-1);
//Sample Message
Console.WriteLine(Environment.NewLine + "Console process started...");
//do something
Console.WriteLine(Environment.NewLine + "Exit process!");
//Free console
FreeConsole(); //<<<<<<<<<--------blinking cursor appears on the console but the user still have to press the "ENTER" button
}
//To attach to the console (parent)
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
//To free from the attached console
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
}
}
PS: I understand that the best approach is to have separate WinForm and console applications that use the same business logic. Unfortunately, that's not an option for me. The above code is just MWE to test the problem.
As written in this blog post by Raymond Chen and this answer you can't have an app that is both, console and windows app.
There are few workarounds, the approach that worked for me is to add the following code at the end of the main function:
SendKeys.SendWait("{ENTER}");
In order to set the exit code, you could use Environment.Exit:
Environment.Exit(0);
This is not a duplicate of this post. I got my solution from this post but it does not work not to mention my app is a winforms app, not a console app.
So I have a project that is running in the background. Basically, I created a Windows forms application but I am not calling the Form in Program.cs. I created a class that listens to when the application exits:
class ShutDownManager
{
public ShutDownManager()
{
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
}
static bool exitSystem = false;
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig)
{
Console.Writeline("I'M OUT OF HERE");
return true;
}
}
I create an instance of this class in Program.cs's Main function.
However this does not seem to fire. It works like a charm in Console applications but when it comes to my forms application (without the form) it doesn't work. What could be the problem?
My code in Main:
static void Main()
{
ShutDownManager sdm = new ShutDownManager();
StartUpManager.AddApplicationToCurrentUserStartup();
Timers timers = new Timers();
}
Let me break down your situation: An excerpt from learn.microsoft.com on SetConsoleCtrlHandler
Each console process has its own list of application-defined HandlerRoutine functions that handle CTRL+C and CTRL+BREAK signals. The handler functions also handle signals generated by the system when the user closes the console, logs off, or shuts down the system. A console process adds or removes additional handler functions by calling the SetConsoleCtrlHandler function, which does not affect the list of handler functions for other processes.
As you notice, this seems to be referring to Console process only. And it cannot tell you when other application exit, but only about this application. In addition it can notify you of LOGOFF and SHUTDOWN. Also, since there is no console (as you want a background process), there is no CTRL-C or CTRL-BREAK processing involved.
Conclusion: the best case is that you can only be notified about LOGOFF and SHUTDOWN
Relevant information from the same page for winforms app:
This function provides a similar notification for console application and services that WM_QUERYENDSESSION provides for graphical applications with a message pump.
What is the problem with your code?
Well, it is designed to provide a callback when the system has LOGOFF or SHUTDOWN when the application is running. Needless to say, your application has already exited to receive such events. So you have to wait after registering your callback.
What is the best way to do it?
You should write a service for this. And if I am not mistaken, service already gets a notification for LOGOFF and SHUTDOWN
How to write a simple C# service has a good example.
EDIT:
For more comprehensive understanding , you might want to look at the MSDN docs about ServiceBase class. Also, you should look at the OnSessionChange event, which is notified about all session events. It has some useful code excerpt at SessionChangeDescription class doc. Similarly for OnShutdown event
Now if you can't do windows service, you will have to create a Form from your winform application and make the Form invisible. You should then handle the WM_QUERYENDSESSION event or use the SystemEvents.SessionEnding Event to handle the situation
Sample code for that
var frm = new Form();
frm.Activated += (s, e) => { frm.Visible = false; };
SystemEvents.SessionEnded += (s, e) => { /* your code to handle logoff and shutdown */ };
Application.Run(frm);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I show console output/window in a forms application?
Is there a way for a c# winforms program to write to the console window?
There are basically two things that can happen here.
Console output
It is possible for a winforms program to attach itself to the console window that created it (or to a different console window, or indeed to a new console window if desired). Once attached to the console window Console.WriteLine() etc works as expected. One gotcha to this approach is that the program returns control to the console window immediately, and then carries on writing to it, so the user can also type away in the console window. You can use start with the /wait parameter to handle this I think.
Start commsnd syntax
Redirected console output
This is when someone pipes the output from your program somewhere else, eg.
yourapp > file.txt
Attaching to a console window in this case effectively ignores the piping. To make this work you can call Console.OpenStandardOutput() to get a handle to the stream that the output should be piped to. This only works if the output is piped, so if you want to handle both of the scenarios you need to open the standard output and write to it and attach to the console window. This does mean that the output is sent to the console window and to the pipe but its the best solution I could find. Below the code I use to do this.
// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
StreamWriter _stdOutWriter;
// this must be called early in the program
public GUIConsoleWriter()
{
// this needs to happen before attachconsole.
// If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
// I guess it probably does write somewhere, but nowhere I can find out about
var stdout = Console.OpenStandardOutput();
_stdOutWriter = new StreamWriter(stdout);
_stdOutWriter.AutoFlush = true;
AttachConsole(ATTACH_PARENT_PROCESS);
}
public void WriteLine(string line)
{
_stdOutWriter.WriteLine(line);
Console.WriteLine(line);
}
}
What I want to have happen is that the console window just goes away, or better yet that it is hidden, but I want my application to keep running. Is that possible? I want to be able to use Console.WriteLine and have the console serve as an output window. I want to be able to hide and show it, and I don't want the whole app to die just because the console was closed.
EDIT
Code:
internal class SomeClass {
[DllImport("kernel32")]
private static extern bool AllocConsole();
private static void Main() {
AllocConsole();
while(true) continue;
}
}
EDIT 2
I tried the accepted solution here [ Capture console exit C# ], per the suggestion in the comments on this question. The example code is bugged in that the DLLImport needs to be "kernel32.dll" or "kernel32", not "Kernel32". After making that change, I'm getting a message to my handler for CTRL_CLOSE_EVENT when I click the X on the console window. However, calling FreeConsole and/or returning true doesn't prevent the application from terminating.
Ah, yes, this is one of the caveats of using the Windows console subsystem. When the user closes the console window (regardless of how the console was allocated), all of the processes that are attached to the console are terminated. That behavior makes obvious sense for console applications (i.e., those that specifically target the console subsystem, as opposed to standard Windows applications), but it can be a major pain in cases like yours.
The only workaround that I know of is to use the SetConsoleCtrlHandler function, which allows you to register a handler function for Ctrl+C and Ctrl+Break signals, as well as system events like the user closing the console window, the user logging off, or the system shutting down. The documentation says that if you're only interested in ignoring these events, you can pass null for the first argument. For example:
[DllImport("kernel32")]
static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool Add);
delegate bool HandlerRoutine(uint dwControlType);
static void Main()
{
AllocConsole();
SetConsoleCtrlHandler(null, true);
while (true) continue;
}
That works perfectly for Ctrl+C and Ctrl+Break signals (which would have otherwise caused your application to terminate as well), but it doesn't work for the one you're asking about, which is the CTRL_CLOSE_EVENT, generated by the system when the user closes the console window.
Honestly, I don't know how to prevent that. Even the sample in the SDK doesn't actually allow you to ignore the CTRL_CLOSE_EVENT. I tried it in a little test app, and it beeps when you close the window and prints the message, but the process still gets terminated.
Perhaps more worryingly, the documentation makes me think it is not possible to prevent this:
The system generates CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals when the user closes the console, logs off, or shuts down the system so that the process has an opportunity to clean up before termination. Console functions, or any C run-time functions that call console functions, may not work reliably during processing of any of the three signals mentioned previously. The reason is that some or all of the internal console cleanup routines may have been called before executing the process signal handler.
It's that last sentence that catches my eye. If the console subsystem starts cleaning up after itself immediately in response to the user attempting to close the window, it may not be possible to halt it after the fact.
(At least now you understand the problem. Maybe someone else can come along with a solution!)
Unfortunately there's nothing you can do to really alter this behaviour.
Console windows are "special" in that they're hosted by another process and do not allow sub-classing. This limits your ability to modify their behaviour.
From what I know, your two options are:
1. Disable the close button altogether. You can do this with the following code fragment:
HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)
{
HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
if (hMenu != NULL) DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}
2. Stop using consoles altogether, and implement your own text output solution.
Option #2 is the more complicated option but would provide you the greatest control. I found an article on CodeProject that implements a console-like application using a rich edit control to display the text (rich edit controls have the ability to stream text like the console, so they are well suited to this sort of application).
On closing the console window obtained using AllocConsole or AttachConsole, the associated process will exit. There is no escape from that.
Prior to Windows Vista, closing the console window would present a confirmation dialogue to the user asking him whether the process should be terminated or not but Windows Vista and later do not provide any such dialogue and the process gets terminated.
One possible solution to work around this is avoiding AttachConsole altogether and achieving the desired functionality through other means.
For instance in the case described by OP, console window was needed to output some text on Console using Console static class.
This can be achieved very easily using inter-process communication. For example a console application can be developed to act as an echo server
namespace EchoServer
{
public class PipeServer
{
public static void Main()
{
var pipeServer = new NamedPipeServerStream(#"Com.MyDomain.EchoServer.PipeServer", PipeDirection.In);
pipeServer.WaitForConnection();
StreamReader reader = new StreamReader(pipeServer);
try
{
int i = 0;
while (i >= 0)
{
i = reader.Read();
if (i >= 0)
{
Console.Write(Convert.ToChar(i));
}
}
}
catch (IOException)
{
//error handling code here
}
finally
{
pipeServer.Close();
}
}
}
}
and then instead of allocating/attaching a console to the current application, the echo server can be started from within the application and Console's output stream can be redirected to write to the pipe server.
class Program
{
private static NamedPipeClientStream _pipeClient;
static void Main(string[] args)
{
//Current application is a Win32 application without any console window
var processStartInfo = new ProcessStartInfo("echoserver.exe");
Process serverProcess = new Process {StartInfo = processStartInfo};
serverProcess.Start();
_pipeClient = new NamedPipeClientStream(".", #"Com.MyDomain.EchoServer.PipeServer", PipeDirection.Out, PipeOptions.None);
_pipeClient.Connect();
StreamWriter writer = new StreamWriter(_pipeClient) {AutoFlush = true};
Console.SetOut(writer);
Console.WriteLine("Testing");
//Do rest of the work.
//Also detect that the server has terminated (serverProcess.HasExited) and then close the _pipeClient
//Also remember to terminate the server process when current process exits, serverProcess.Kill();
while (true)
continue;
}
}
This is just one of the possible solutions. In essence the work around is to allot the console window to its own process so that it can terminate without affecting the parent process.
You can do this by disabling keyboard mouse input by external program called Keyfreez.
you can use it multiple times in your program where no user input required. And if any user input require u can add a process Takskkill /f /IM .
https://www.sordum.org/7921/bluelife-keyfreeze-v1-4-block-keyboard-and-mouse/
Hope this helps all of you
This question already has answers here:
How and why does QuickEdit mode in Command Prompt freeze applications?
(2 answers)
Closed 6 years ago.
I have a console program that has different components that run like this:
void start() {
while(true){
DoSomething();
Thread.Sleep(1000*5);
}
}
My main entry point looks like [pseudo-ish code]
Thread.Start(Componenet1.Start);
Thread.Start(Componenet2.Start);
while(true){
Console.Writeline("running");
Thread.Sleep(1000*5);
}
There are no Console.Reads anywhere. My problem is SOMETIMES the application will be running great but then stop and if I press any key on the window it will start working again. This happens fairly infrequently but I have this program deployed on 100+ VM's running 24/7 in an automated environment.
Also on the computer I have some AHK scripts and other stuff that manipulate the mouse but not sure if that has anything to do with it.
Also note that sometimes the CPU can really be running at 100% on the machines so maybe thread priority is an issue?
SOLUTION: You need to disable quick edit mode. Here is working C# code to do this:
// http://msdn.microsoft.com/en-us/library/ms686033(VS.85).aspx
[DllImport("kernel32.dll")]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
private const uint ENABLE_EXTENDED_FLAGS = 0x0080;
static void Main(string[] args)
{
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
SetConsoleMode(handle, ENABLE_EXTENDED_FLAGS);
If the user accidentally clicks into the black console window, the cursor changes to a filled white rectangle, and the app hangs at the next Console.Write statement, until another clic is made.
It is a generic feature of the Console window when its "QuickEdit Mode" is enabled.
In order to disable that feature, you should uncheck the "QuickEdit Mode" option of your app's console window at run-time.