APPTYPE CONSOLE magic

Giganews Newsgroups
Subject:APPTYPE CONSOLE magic
Posted by: Andrew Fionik (fion…@gmail.com)
Date:Mon, 29 Jun 2009

Hello all.

Preabmle. I have a service application. The service itself is just a host
process for my classes. Unfortunately debugging a service is a nightmare. I
decided to give to my application an ability to run as standalone console
application. Let's say user specify command line switch /console and
application should run in console. The dpr file looks like as below:

program XXXX;
uses
  SysUtils,
  SvcMgr,
  uFWService in 'uFWService.pas' {FWService: TService},
  .... // a long list of *.pas files included in the project
  uConsoleApplication in 'uConsoleApplication.pas',
  uFWConsoleApplication in 'uFWConsoleApplication.pas',
  uFWLogMessages in 'uFWLogMessages.pas',
  uFWServiceExitCodes in 'uFWServiceExitCodes.pas';

{$R *.RES}

function StartAsConsole: Boolean;
begin
  Result := FindCmdLineSwitch('CONSOLE', ['-', '/'], True);
end;

function StartAsService: Boolean;
begin
  Result := not StartAsConsole;
end;

var
  lConsoleApplication: TConsoleApplication;

begin
  if StartAsService then
    begin
      if not SvcMgr.Application.DelayInitialize or
SvcMgr.Application.Installing then
        SvcMgr.Application.Initialize;
      SvcMgr.Application.CreateForm(TFWService, FWService);
      SvcMgr.Application.Run;
    end
  else
  begin
    lConsoleApplication := TFWConsoleApplication.Create;
    try
      lConsoleApplication.Run;
    finally
      FreeAndNil(lConsoleApplication);
    end;
  end;
end.

TConsoleApplication and TFWConsoleApplication are hand made classes.
Somewhere inside of them there are reading and writing operations from
standard input/output streams received by the GetStdHandle windows function.
Please notice there is no APPTYPE CONSOLE compiler directive there.
Somewhere in the initialization code there are calls:

    FConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
    FConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE);
    FConsoleError := GetStdHandle(STD_ERROR_HANDLE);

In order to print something on a console I use windows API function
WriteConsoleW.

Now the problem begins. The problem with console.

If I simply attempt to run application as is it runs. But! If I run it from
Delphi IDE console is not created and no output is visible. If I run it from
command prompt still no output. I tried to create a console by calling
AllocConsole. Something like this:

    AllocConsole;
    FConsoleInput := GetStdHandle(STD_INPUT_HANDLE);
    FConsoleOutput := GetStdHandle(STD_OUTPUT_HANDLE);
    FConsoleError := GetStdHandle(STD_ERROR_HANDLE);

It looks like it works but... when I run it from IDE a new console is
created and output is visible but when I run it from the command prompt a
new console is still created instead of printing on an existing console.

Ok. I tried to to attach to the existing console. I created a sample:

program hw;
uses
  SysUtils,
  Windows;

const
  kGreeting:WideString='Hello world!';
var
  StartupInfo:TStartupInfo;
  hOutput:THandle;
  CharsWritten:Cardinal;
begin
  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
  StartupInfo.cb:=SizeOf(StartupInfo);
  GetStartupInfo(StartupInfo);
  if (StartupInfo.dwFlags and STARTF_USESTDHANDLES)<>0 then
    hOutput:=StartupInfo.hStdOutput
  else
    begin
      AllocConsole;
      hOutput:=GetStdHandle(STD_OUTPUT_HANDLE);
    end;
  try
    CharsWritten:=0;
    WriteConsoleW(hOutput, @kGreeting[1], Length(kGreeting), CharsWritten,
nil);
  finally
    CloseHandle(hOutput);
  end;
end.

When I run this program from the IDE a new console is created. It is okay.
But when I run the program from the command prompt StartupInfo.dwFlags
doesn't include STARTF_USESTDHANDLES and a new console is still created
instead of getting inherited handles. I'm completely confused. What does
APPTYPE CONSOLE do? How do it manage the situation when a program is run
from the IDE or from the command prompt?

Replies