Monday, 18 July 2016

New: Velthuis.AutoConsole unit

I often (really, very often) write console programs. Most of them are simple test programs to test algorithms, calculations or language features, or stuff copied from StackOverflow.

But console programs, if run from the IDE, or from the Windows Explorer, have one annoying feature: they close immediately if they are finished. So if my program has many Writelns in it, they simply rush by and the console window closes before I can inspect the output. To avoid that, I often put Readln; in the last line of the program. If the program is finished, it pauses and waits for me to press Enter.

But such a Readln; is annoying if I really want to run the program from an existing console window (aka a Windows command line). Then I don't want to have to press Enter, because the output is shown in the current console window already, and that won't close until I tell it to.

To get the best of both worlds, I wrote my simple Velthuis.AutoConsole unit. Put it in the uses clause of your console program and, if the program is a console program and it was started from the command line, or if it is not a console program, it will do nothing. But if it is a console program and it was started any other way (from the Windows Explorer, from the Delphi IDE with or without debugger, from another non-console program), then, before the console window can close, it will display:

Press any key...

Then I really only have to press a key (it does not have to be Enter, it can be any key) and the program will end, closing the console Window.

I placed the new unit in the zip for my Console unit. You can download it from the page I linked to.

Here is the code of this simple unit. I think I may have to add a ConsoleCtrlHandler, so the window is not closed when the user presses Ctrl+C or closes the console window with the  x  close button on the window frame. But I am not sure if that is necessary. If I press those, I usually want to stop the program anyway. Comments on this are welcome.

unit Velthuis.AutoConsole;

interface

implementation

uses
  Velthuis.Console,    // as described in the link above 
  Winapi.Windows;

function GetConsoleWindow: HWnd; stdcall; 
  external 'kernel32.dll' name 'GetConsoleWindow';

function StartedFromConsole: Boolean;
var
  ConsoleHWnd: THandle;
  ProcessId: DWORD;
begin
  ConsoleHwnd := GetConsoleWindow;
  if ConsoleHWnd <> 0 then
  begin
    GetWindowThreadProcessId(ConsoleHWnd, ProcessId);
    Result := GetCurrentProcessId <> ProcessId;
  end
  else 
    Result := False;
end;

procedure Pause;
begin
  if IsConsole and not StartedFromConsole then
  begin
    Write('Press any key... ');
    ReadKey;
  end;
end;

initialization

finalization
  Pause;

end.

Update

I removed the reliance on the Velthuis.Console unit. I added this piece, a simplified version of ReadKey, which does not try to translate the key, making things a lot simpler:

procedure WaitForInput;
var
  InputRec: TInputRecord;
  NumRead: Cardinal;
  OldKeyMode: DWORD;
  StdIn: THandle;
begin
  StdIn := GetStdHandle(STD_INPUT_HANDLE);
  GetConsoleMode(StdIn, OldKeyMode);
  SetConsoleMode(StdIn, 0);
  repeat
    ReadConsoleInput(StdIn, InputRec, 1, NumRead);
  until (InputRec.EventType and KEY_EVENT <> 0) and InputRec.Event.KeyEvent.bKeyDown;
  SetConsoleMode(StdIn, OldKeyMode);
end;

and changed the Pause procedure a little:

procedure Pause;
begin
  if IsConsole and not StartedFromConsole then
  begin
    Write('Press any key... ');
    WaitForInput;
  end;
end;