I am writing a standard windows app in Delphi 7.
If I was writing a console app, I can call the following to output to the cmd line or output file.
write
This question is very similar (if not exactly the same) as something I was trying to accomplish. I wanted to detect if my app was executed from a cmd.exe and send output to the parent console, otherwise it would display a gui. The answers here helped me solve my issue. Here is the code I came up with as an experiment:
ParentChecker.dpr
program ParentChecker;
uses
Vcl.Forms,
SysUtils,
PsAPI,
Windows,
TLHelp32,
Main in 'Main.pas' {frmParentChecker};
{$R *.res}
function AttachConsole(dwProcessID: Integer): Boolean; stdcall; external 'kernel32.dll';
function FreeConsole(): Boolean; stdcall; external 'kernel32.dll';
function GetParentProcessName(): String;
const
BufferSize = 4096;
var
HandleSnapShot: THandle;
EntryParentProc: TProcessEntry32;
CurrentProcessId: THandle;
HandleParentProc: THandle;
ParentProcessId: THandle;
ParentProcessFound: Boolean;
ParentProcPath: String;
begin
ParentProcessFound:=False;
HandleSnapShot:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if HandleSnapShot<>INVALID_HANDLE_VALUE then
begin
EntryParentProc.dwSize:=SizeOf(EntryParentProc);
if Process32First(HandleSnapShot,EntryParentProc) then
begin
CurrentProcessId:=GetCurrentProcessId();
repeat
if EntryParentProc.th32ProcessID=CurrentProcessId then
begin
ParentProcessId:=EntryParentProc.th32ParentProcessID;
HandleParentProc:=OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,False,ParentProcessId);
if HandleParentProc<>0 then
begin
ParentProcessFound:=True;
SetLength(ParentProcPath,BufferSize);
GetModuleFileNameEx(HandleParentProc,0,PChar(ParentProcPath),BufferSize);
ParentProcPath:=PChar(ParentProcPath);
CloseHandle(HandleParentProc);
end;
Break;
end;
until not Process32Next(HandleSnapShot,EntryParentProc);
end;
CloseHandle(HandleSnapShot);
end;
if ParentProcessFound then Result:=ParentProcPath
else Result:='';
end;
function IsPrime(n: Integer): Boolean;
var
i: Integer;
begin
Result:=False;
if n<2 then Exit;
Result:=True;
if n=2 then Exit;
i:=2;
while i<(n div i + 1) do
begin
if (n mod i)=0 then
begin
Result:=False;
Exit;
end;
Inc(i);
end;
end;
var
i: Integer;
ParentName: String;
begin
ParentName:=GetParentProcessName().ToLower;
Delete(ParentName,1,ParentName.LastIndexOf('\')+1);
if ParentName='cmd.exe' then
begin
AttachConsole(-1);
Writeln('');
for i:=1 to 100 do if IsPrime(i) then Writeln(IntToStr(i)+' is prime');
FreeConsole();
end
else
begin
Application.Initialize;
Application.MainFormOnTaskbar:=True;
Application.CreateForm(TfrmParentChecker, frmParentChecker);
frmParentChecker.Label1.Caption:='Executed from '+ParentName;
Application.Run;
end;
end.
Main.pas (form with label):
unit Main;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, RzLabel;
type
TfrmParentChecker = class(TForm)
Label1: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmParentChecker: TfrmParentChecker;
implementation
{$R *.dfm}
end.
This allows me to run my GUI app from a command prompt and display output to the same console where my app was launched. Otherwise, it will run the full GUI part of the app.
Example output from console window:
I:\Delphi\Tests and Demos\ParentChecker\Win32\Debug>start /wait ParentChecker.exe
2 is prime
3 is prime
5 is prime
7 is prime
11 is prime
13 is prime
17 is prime
19 is prime
23 is prime
29 is prime
31 is prime
37 is prime
41 is prime
43 is prime
47 is prime
53 is prime
59 is prime
61 is prime
67 is prime
71 is prime
73 is prime
79 is prime
83 is prime
89 is prime
97 is prime
I:\Delphi\Tests and Demos\ParentChecker\Win32\Debug>
I found this very complete article about the whole issue: http://www.boku.ru/2016/02/28/posting-to-console-from-gui-app/
I made a unit to do the AttachConsole, hook the exception handler to mirror messages to console.
To use it, you only need to call ATTACH in your code. It is best to make attaching a commandline option e.g -console
if FindCmdLineSwitch('console',true) then AttachConsole(true,true);
This is for a gui application, and when using this, you must use START /W to launch your program is you expect it to be blocking on the commandline/batch e.g. start /w myprogram.exe -console
One handy benefit is that you can launch it standalone with a console if you want, and get to see all the error messages in the console.
unit ConsoleConnector;
// Connects the/a console to a GUI program
// Can hook exception handler to mirror messages to console.
// To use it, you only need to call ATTACH
// best to make attaching a commandline option e.g -console
// if FindCmdLineSwitch('console',true) then AttachConsole(true,true);
// When using this, you will use START to launch your program e.g.
// start /w myprogram.exe -console
// creates Console var at end in initialise/finalise - you might want to do this explicitly in your own program instead.
// see: http://www.boku.ru/2016/02/28/posting-to-console-from-gui-app/
//sjb 18Nov16
interface
uses sysutils,forms;
type
TConsoleConnector = class
private
OldExceptionEvent:TExceptionEvent;
Hooked:boolean;
BlockApplicationExceptionHandler:boolean; //errors ONLY to console, no error messageboxes blocking program
procedure DetachErrorHandler;
procedure GlobalExceptionHandler(Sender: TObject; E: Exception);
procedure HookExceptionHandler;
public
IsAttached:boolean;
function Attach(
CreateIfNeeded:boolean=true; //Call ALLOCCONSOLE if no console to attach to
HookExceptions:boolean=false; //Hook Application.OnException to echo all unhandled exceptions to console
OnlyToConsole:boolean=false // Suppresses exception popups in gui, errors only go to console
):boolean;
procedure Detach; //detach and unhook
procedure writeln(S:string); //only writes if console is attached
procedure ShowMessage(S:string); //Popup ShowMessage box and mirror to console. Obeys OnlyToConsole
end;
var Console:TConsoleConnector;
implementation
uses Windows,dialogs;
//winapi function
function AttachConsole(dwProcessId: Int32): boolean; stdcall; external kernel32 name 'AttachConsole';
function TConsoleConnector.Attach(CreateIfNeeded:boolean=true;HookExceptions:boolean=false;OnlyToConsole:boolean=false):boolean;
begin
IsAttached:=AttachConsole(-1);
if not IsAttached and CreateIfNeeded
then begin
IsAttached:=AllocConsole;
end;
result:=IsAttached;
if HookExceptions then HookExceptionHandler;
end;
procedure TConsoleConnector.Detach;
begin
FreeConsole;
IsAttached:=false;
DetachErrorHandler;
end;
procedure TConsoleConnector.WriteLn(S:string);
begin
if IsAttached then system.writeln(S);
end;
procedure TConsoleConnector.ShowMessage(S:string);
begin
self.Writeln(S);
if BlockApplicationExceptionHandler then exit;
dialogs.ShowMessage(S);
end;
procedure TConsoleConnector.GlobalExceptionHandler(Sender: TObject; E: Exception);
begin
self.Writeln(E.Message);
if BlockApplicationExceptionHandler then exit;
if assigned(OldExceptionEvent) //i.e there was an old event before we hooked it
then OldExceptionEvent(Sender,E)
else Application.ShowException(E);
end;
procedure TConsoleConnector.HookExceptionHandler;
begin
OldExceptionEvent:=Application.OnException;
Application.OnException:=GlobalExceptionHandler;
Hooked:=true;
end;
procedure TConsoleConnector.DetachErrorHandler;
begin
if Hooked //I have hooked it
then begin
Application.OnException:=OldExceptionEvent;
OldExceptionEvent:=nil;
Hooked:=false;
end;
end;
initialization
Console:=TconsoleConnector.create;
finalization
Console.Detach;
Console.Destroy;
end.