
| 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?