6

I am trying to open the console from my main program (Win32). I found some code, and it works, but I don't understand it. The problem I'm happening is that when I click X on the console, it closes the program as well.

Roughly I have this:

int APIENTRY WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) {

  // create the main program window, classes registered, etc...
  hwnd = CreateWindowEx(0, csClassName, "theNewTimer", WS_POPUP | WS_CLIPCHILDREN, 300, 0, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, NULL, hThisInstance, NULL);
  ShowWindow (hwnd, nFunsterStil);

  // and now the console
  AllocConsole();
  HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
  int hCrt = _open_osfhandle((long) handle_out, _O_TEXT);
  FILE* hf_out = _fdopen(hCrt, "w");
  setvbuf(hf_out, NULL, _IONBF, 1);
  *stdout = *hf_out;

  HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
  hCrt = _open_osfhandle((long) handle_in, _O_TEXT);
  FILE* hf_in = _fdopen(hCrt, "r");
  setvbuf(hf_in, NULL, _IONBF, 128);
  *stdin = *hf_in;


// and then the message loop concluding

I googled some of it, but had no idea of what I was reading.

Evan Carslake
  • 2,267
  • 15
  • 38
  • 56
  • Perhaps the answer [here](http://stackoverflow.com/q/13954299/62576) can help. – Ken White Nov 27 '13 at 02:19
  • This is similar, but not the same. I read up on MSDN and see that I can close the process id to the console, instead the parent app. Problem there is I dont know how to get the consoles process. BOOL ConsoleCtrlHandler(DWORD fdwCtrlType) { //ExitProcess(); // couldnt figure out the process id switch (fdwCtrlType) { case CTRL_CLOSE_EVENT: return TRUE; case CTRL_LOGOFF_EVENT: return FALSE; case CTRL_SHUTDOWN_EVENT: return FALSE; } return FALSE; } – Evan Carslake Nov 27 '13 at 03:19
  • ^ Could not figure out how to format. Thats slightly condensed but I have no idea of where to go from there. It is attached successfully though. – Evan Carslake Nov 27 '13 at 03:24

2 Answers2

8

One thing you can do is disable the close button on the console window:

HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)
{
   HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
   if (hMenu != NULL) DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}
Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • That works great, prevents accidentally closing the entire program, and can use FreeConsole() when I do want just the console closed. – Evan Carslake Nov 27 '13 at 16:46
5

I do not think this is possible. From the MSDN documentation for a HandlerRoutine, there's this sentence.

The CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals give the process an opportunity to clean up before termination.

I read this as saying that CTRL_CLOSE_EVENT is advisory, and that the process is going to exit regardless. My guess is that when the system sends CTRL_CLOSE_EVENT, it starts a timer. The process is allowed to keep running for a little bit of time, but eventually, the OS will just kill the process unilaterally.

Here's the handler that I registered

BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType) {
   switch (dwCtrlType) {
       case CTRL_C_EVENT:
       case CTRL_CLOSE_EVENT:
           return TRUE; // breakpoint set here

       default:
           return FALSE;
   }
}

Here's how I registered the handler, after my call to AllocConsole:

BOOL result = SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE /* Add */);
assert(result);

I set a breakpoint on the line marked //breakpoint set here. Then, I ran the process under the Visual Studio debugger. When the console window was focused, I pressed Ctrl+C. My breakpoint got hit, and I was able to step through my handler and back into KernelBase.dll!CtrlRoutine and so on. The process kept running when I let the process resume normal execution.

However, when I closed the console window, my handler did get called, but I was unable to trace its execution very far. I was able to single step execution a few times, but then the process simply exited. Visual Studio reported "The program '[10644] Win32Project.exe' has exited with code -1073741510 (0xc000013a)."

0xc000013a is STATUS_CONTROL_C_EXIT:

c:\Tools\ERR>Err.exe 0xc000013a
# for hex 0xc000013a / decimal -1073741510 :
  STATUS_CONTROL_C_EXIT                                         ntstatus.h
# {Application Exit by CTRL+C}
# The application terminated as a result of a CTRL+C.
# 1 matches found for "0xc000013a"
chwarr
  • 6,777
  • 1
  • 30
  • 57
  • Nice, my code now looks just like that. So when the CTRL_CLOSE_EVENT is caught, what is it I am supposed to do? That part is working. Testing just returning TRUE, still closes the program, same as FALSE. I googled the ExitProcess() and wasn't able to find what parameter to pass to it. I tried the 255, 0, and -1, all same results. I also tested in the Console Handler calling FreeConsole() before returning TRUE; same result. Any ideas? Thanks. – Evan Carslake Nov 27 '13 at 07:01
  • See the most recent version of the answer. I don't think this can be used to prevent the process from exiting. By the way, there's no special value that can be passed to `ExitProcess` to stop it from exiting. If you don't want to exit the process via `ExitProcess`, just don't call `ExitProcess`. – chwarr Nov 27 '13 at 07:03
  • Thanks for the help, I think that you're right. Even thinking about it now... that kind of makes sense. – Evan Carslake Nov 27 '13 at 07:37
  • 1
    It is not possible to prevent OS from terminating your proccess. As soon as you return it will either inmediately end your proccess, if TRUE is returned, or keep calling handlers until one returns TRUE (ending the proccess) or it reachs the default one (ExitProcess) if FALSE is returned. However you can get some time to clean up by setting a global "we_are_closing" variable and Sleep for some seconds before returning. Your running threads should detect the variable change and clean up as quickly as possible, as you only have 5-10 seconds before OS kills the proccess if handler doesn't return. – gusitoguay Apr 03 '19 at 08:36
  • Warning here: `assert` statements are verified only in debug, when compiling in release they are purely removed – 56ka Mar 29 '22 at 09:59