10

I have a common unit that does some logging to GExperts Debugger and/or OutputDebugString. I am going to use it in a console app, so I want it to be able to output to stdout via writeln().
The main executable has {$APPTYPE CONSOLE} already, but I don't think that'll help me here. The logging routine will be called from several places:

  1. the main console app, which will link to a BPL,
  2. from another BPL that "requires" the first bpl, and.....
  3. from a DLL that statically links the unit.

The BPLs and DLL will be built with no visibility to the {$APPTYPE CONSOLE} directive, so I can't use IFDEF conditional compilation. The BPL and DLL need to be able to go either way, depending whether the main app is a regular winapp or console app.

One ugly solution that occurred to me is to use the name of the executable. ex:

if (UpperCase(ExtractFileName(ParamStr(0))) = 'MYCONSOLEAPP.EXE')  then ...

But I hate to do that, as I could have other console apps...

I'd rather just have a magic function AmIAConsoleApp : boolean; Is there anything like that? I'm using Delphi2005 on this project.

Update: I see that I'm kind of a duplicate of this question, but I'd like to survey the Delphi folks to see if there's a better approach.

Community
  • 1
  • 1
Chris Thornton
  • 15,620
  • 5
  • 37
  • 62
  • 1
    There were a couple of well-intended, helpful-looking answers suggesting the use of System.IsConsole. They were deleted by the posters, but I felt that it was valuable to include in the discussion. So here's why it won't work: System.IsConsole returns true, according to the doc, "{ True if compiled as console app }". I'm not compiling as a console app (well the main app is, but the DLL isn't). So I had mixed results, depending on where the logger was being called from. – Chris Thornton Aug 24 '11 at 21:33

2 Answers2

17

Call GetStdHandle(Std_Output_Handle). If it succeeds and returns zero, then there is no console to write to. Other return values indicate that a console is attached to the process, so you can write to it (although the console may not be the most desirable place to log messages in a console program since they'll interfere with the normal output). Something like this:

function IAmAConsoleApp: Boolean;
var
  Stdout: THandle;
begin
  Stdout := GetStdHandle(Std_Output_Handle);
  Win32Check(Stdout <> Invalid_Handle_Value);
  Result := Stdout <> 0;
end;
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • 3
    This answer also covers the case if the application is not of a console type but allocated one. So I think this is the correct answer even though the question is wrong (the ultimate aim is to output to console, not to find out how the executable is linked). – Sertac Akyuz Aug 24 '11 at 22:41
  • Cool - I'll give it a shot tomorrow. Thanks. – Chris Thornton Aug 25 '11 at 03:25
1

Use constructor injection to inject a logger at the time you create the instance. Here's a simple example.

Your proposed solution of testing whether the app is a console app works for only those two scenarios. The constructor injection solution is scarcely any more code and works anywhere you need output.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
  • Do you mean something along the lines of `X := TY.Create(MyLogger)`? and then the console app provides a logger that uses Writeln while a GUI app provides another one, or even nil? A good idea (although the term was new to me) I have used several times in such a situation (e.g. a lexer/parser that requires an ICharProvider: The console test app provides one that gets characters from a set of string constants, while the real app provides one that gets characters from the IDE editor). – Rudy Velthuis Aug 24 '11 at 21:48
  • 1
    @Rudy, yes, that's what I mean. – Craig Stuntz Aug 24 '11 at 23:53