0

Before thinking this is a duplicate Question, then please read this: Yes this question is around in on SO in different languages at least Delphi, C# and C++ but they all have something in common: They talk about handling a clean shut down not preventing it.

So here we go:

Form a VCL application I open a new Console Window using AllocConsole but when closing that window with the cross in the top right corner my application terminates. That I would like to prevent not handle!

Some code:

function Handler(dwCtrlType: DWORD): Boolean; cdecl;
begin
  case dwCtrlType of
    CTRL_CLOSE_EVENT, CTRL_C_EVENT, CTRL_BREAK_EVENT:
      Exit(True);
  else
    Exit(false);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  AllocConsole;
  SetConsoleCtrlHandler(@Handler, True);
end;

I have read the WinAPI documentation but it doesn't say anything about preventing Application termination.

I have tried adding message handlers for WM_ENDSESSION, WM_QUERYENDSESSION, WM_CLOSE and WM_QUIT on my MainForm but none of them gets called. I've also tried to add a FormCloseQuery event but it doesn't get called either.

I have read and tried out the solution found here but SetConsoleCtrlHandler(nil, True); doesn't provide the application for termination

So in short how to prevent termination.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
Jens Borrisholt
  • 6,174
  • 1
  • 33
  • 67
  • @David the SO question you are referring to doesn't provide a solution. – Jens Borrisholt Apr 20 '19 at 07:02
  • 1
    I'm pretty sure that that question is the same question, and it has answers. They explain that what you are trying to achieve, cannot be done, and the answer awarded the bounty explains how to workaround the issue. – David Heffernan Apr 20 '19 at 07:25
  • As stated I have seen that question and I did read the last of his answer "Honestly, I don't know how to prevent that. .... At least now you understand the problem. Maybe someone else can come along with a solution!" So he doesn't provide a solution, his words not mine. So this question hav not been answered here on SO. – Jens Borrisholt Apr 20 '19 at 09:00
  • 3
    Dude, sometimes the answer is that something is not possible. – David Heffernan Apr 20 '19 at 09:35
  • 2
    And various other comments and answers offer different work-around, like disabling the close button, a suggestion to capture the event and call FreeConsole (although unconfirmed if that works), making your own console-like form, or starting a separate console application and feed it through IPC. – GolezTrol Apr 20 '19 at 09:41
  • I was a bit curious so tried out the solution of catching the console events and calling FreeConsole. This doesn't work. I think theoretically it should be possible to not just disable the close button, but to attach your own event handler or hook to it, so that, when clicked, you can call FreeConsole before passing it on the the console window. I tried that, but with a simple hook it doesn't work. Maybe I did it wrong, but I think you need a more complicated hook in a separate DLL, because the console is not really part of the main process. – GolezTrol Apr 20 '19 at 12:01
  • 2
    AFAICT the dupe was an exact dupe, it doesn't talk about a clean shutdown, it just want's the app to continue running. – Sertac Akyuz Apr 20 '19 at 12:08
  • Well, the dupe was a C# question, and hence scoped to .NET. Of course you can call Windows API's from C# as well, but that's not something that every Delphi developer will (or has to) know. Only for that reason, I can't blame OP for asking again. And then there are the facts that the accepted answer is from 2012 (things may have changes), and that its author doesn't seem to be sure if there are no other ways. – GolezTrol Apr 20 '19 at 12:20
  • Way to go Delphi experts!! Awesome reopen from @MartynA. Now we need to find another dupehammer. – David Heffernan Apr 20 '19 at 12:37
  • 1
    BTW, the handler function is stdcall, not cdecl. – Sertac Akyuz Apr 21 '19 at 14:48

1 Answers1

0

Theoretically, this is done by overloading WinProc, and call to FreeConsole after prevent console close event. I am trying to rewrite the WinProc function at that way:

function GetConsoleWindow: HWND; stdcall; external kernel32 name 'GetConsoleWindow';

var
  FConsoleHandle: HWND;

function ConsoleWndProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  if Msg = WM_CLOSE then
  begin
    // Ignore the close message
    Result := 0;
  end
  else
  begin
    // Handle other messages
    Result := DefWindowProc(hWnd, Msg, wParam, lParam);
  end;
end;

procedure TMainForm.cbDebugConsoleClick(Sender: TObject);
var
  m: HMENU; s: string; r: NativeInt;
begin
  if cbDebugConsole.Checked then
  begin
    AllocConsole;
    FConsoleHandle := GetConsoleWindow;
    r := SetWindowLong(FConsoleHandle, GWL_WNDPROC, LongInt(@ConsoleWndProc));
    s := SysErrorMessage(GetLastError);
  end else
    FreeConsole;
end;

So, SetWindowLong returns 0, means "Access is denied". I don't kown how to get access rights to change WinProc of the Console window.

chabapok
  • 921
  • 1
  • 8
  • 14