How do I know if a Delphi application supports its console?

You can launch the Delphi console application from the command line of an existing console window, and you can launch it by double-clicking its icon. In the latter case, he will create his own console window and close it as soon as the application is completed.

How do I know if my console application has created its own window?

I want to detect this so that I can display a message like "Press Enter to close the window" so that the user can read what was displayed before the window was closed. Obviously, it would be impractical to do this if the application is launched from the command line.

I use Delphi 2010 if it is important.

+6
delphi console-application
source share
6 answers

You have basically two things to test:

  • Is the application console common to processes? If you use cmd.exe to launch the console application, it will share the console by default, so you won’t need to display the message “Press Enter to close the window”.

  • Is the output redirected to a file? If so, you do not need to show the message either.

For the first, there is a simple solution in the form of the GetConsoleProcessList() function of the Windows API. Unfortunately, it is only available for Windows XP and later, but it may be good enough for you. This is not in Delphi 2009 Windows , so you have to import it yourself:

 function GetConsoleProcessList(lpdwProcessList: PDWORD; dwProcessCount: DWORD): DWORD; stdcall; external 'kernel32.dll'; 

Of course, if your software might otherwise work on earlier versions of Windows, you should use LoadLibrary() and GetProcAddress() instead.

Since you are only interested in whether the number of process handlers is above 1, you can call it a very small buffer for descriptors, for example:

 var HandleCount: DWORD; ProcessHandle: DWORD; begin HandleCount := GetConsoleProcessList(@ProcessHandle, 1); // ... end; 

If the number of your descriptors is more than 1, you have other processes that support the console, so you can skip the message.

You can use the GetFileInformationByHandle() function of the Windows API to check if your console output descriptor references a real file or not:

 var StdOutHandle: THandle; IsNotRedirected: boolean; FileInfo: TByHandleFileInformation; begin StdOutHandle := GetStdHandle(STD_OUTPUT_HANDLE); IsNotRedirected := not GetFileInformationByHandle(StdOutHandle, FileInfo) and (GetLastError = ERROR_INVALID_HANDLE); // ... end; 

This code is only for you, I am sure that some corner cases are not handled properly.

+7
source share

In the past, I used something like below:

 program ConsoleTest; {$APPTYPE CONSOLE} uses Windows; function GetConsoleWindow: HWND; stdcall; external kernel32 name 'GetConsoleWindow'; function IsOwnConsoleWindow: Boolean; //ONLY POSSIBLE FOR CONSOLE APPS!!! //If False, we're being called from the console; //If True, we have our own console (we weren't called from console) var pPID: DWORD; begin GetWindowThreadProcessId (GetConsoleWindow,pPID); Result:= (pPID = GetCurrentProcessId); end; 

begin writeln ('Hello '); if IsOwnConsoleWindow then begin writeln ('Press enter to close console'); readln; end; end.

+4
source share

I know this is an old thread, but I have a good solution.

You do not need to bother with batch files. Focus in the exe type is an attribute of the subsystem. After compiling exe as a GUI application (without the {$ APPTYPE CONSOLE} directive, you must change the attribute of the subsystem IMAGE_SUBSYSTEM_WINDOWS_GUI to IMAGE_SUBSYSTEM_WINDOWS_CUI. The good thing is that when you start the console application from the console, it doesn’t display an additional console window and you don’t need to “Press Enter to close the window.” EDIT: if you run another console application in a console application, as in my project)

When you start it from Explorer, etc., by clicking on it or at startup, Windows will automatically open a console window when the subsystem attribute is IMAGE_SUBSYSTEM_WINDOWS_CUI. You do not need to specify the {$ APPTYPE CONSOLE} directive, all about the subsystem attribute.

The RRUZ solution is a solution that is also used, but with one important difference. I check the parent process subsystem to show "Press Enter to close this window." RUZZ this solution works only in two cases, when it is cmd or explorer. Just check if the parent process is NOT IMAGE_SUBSYSTEM_WINDOWS_CUI attribute, you can display a message.

But how to check the exe subsystem? I found a solution at toric tips ( http://www.swissdelphicenter.ch/torry/showcode.php?id=1302 ) to get PE header information and change it to two functions: setExeSubSys () and getExeSubSys (). Using setExeSubSys (), I made a small console application, so after compilation I can change the attribute of the exe subsystem (it's only 50 kb!).

Once you have the file name of the parent / potential process, you can simply do something like this:

  //In the very beginning in the app determine the parent process (as fast as is possible). // later on you can do: if( getExeSubSys( parentFilename ) <> IMAGE_SUBSYSTEM_WINDOWS_CUI ) then begin writeln( 'Press Enter to close the window' ); readln; end; 

Here are two functions that I did, but it does not work with streams (for example, torry example), I use my own simple block for files for it without stupid exeption things. But basically, I think you are considering an idea.

Install (and also get when you did not point to a pointer to longint (nil)):

 type PLongInt = ^LongInt; function setExeSubSys( fileName : string; pSubSystemId : PLongInt = nil ) : LongInt; var signature: DWORD; dos_header: IMAGE_DOS_HEADER; pe_header: IMAGE_FILE_HEADER; opt_header: IMAGE_OPTIONAL_HEADER; f : TFile; begin Result:=-1; FillChar( f, sizeOf( f ), 0 ); if( fOpenEx( f, fileName, fomReadWrite )) and ( fRead( f, dos_header, SizeOf(dos_header))) and ( dos_header.e_magic = IMAGE_DOS_SIGNATURE ) then begin if( fSeek( f, dos_header._lfanew )) and ( fRead( f, signature, SizeOf(signature))) and ( signature = IMAGE_NT_SIGNATURE ) then begin if( fRead( f, pe_header, SizeOf(pe_header))) and ( pe_header.SizeOfOptionalHeader > 0 ) then begin if( fRead( f, opt_header, SizeOf(opt_header))) then begin if( Assigned( pSubSystemId )) then begin opt_header.Subsystem:=pSubSystemId^; if( fSeek( f, fPos( f )-SizeOf(opt_header) )) then begin if( fWrite( f, opt_header, SizeOf(opt_header)) ) then Result:=opt_header.Subsystem; end; end else Result:=opt_header.Subsystem; end; end; end; end; fClose( f ); end; 

To obtain:

 function GetExeSubSystem( fileName : string ) : LongInt; var f : TFile; signature : DWORD; dos_header: IMAGE_DOS_HEADER; pe_header : IMAGE_FILE_HEADER; opt_header: IMAGE_OPTIONAL_HEADER; begin Result:=IMAGE_SUBSYSTEM_WINDOWS_CUI; // Result default is console app FillChar( f, sizeOf( f ), 0 ); if( fOpenEx( f, fileName, fomRead )) and ( fRead( f, dos_header, SizeOf(dos_header))) and ( dos_header.e_magic = IMAGE_DOS_SIGNATURE ) then begin if( fSeek( f, dos_header._lfanew )) and ( fRead( f, signature, SizeOf(signature))) and ( signature = IMAGE_NT_SIGNATURE ) then begin if( fRead( f, pe_header, SizeOf(pe_header))) and ( pe_header.SizeOfOptionalHeader > 0 ) then begin if( fRead( f, opt_header, SizeOf(opt_header))) then Result:=opt_header.Subsystem; end; end; end; fClose( f ); end; 

If you want more information in the subsystem, just go to Google or go to the MSDN website. Hope this was helpful to everyone.

Greetz, Erwin Haantes

+2
source share

I use (I can't remember where I found it):

 function WasRanFromConsole() : Boolean; var SI: TStartupInfo; begin SI.cb := SizeOf(TStartupInfo); GetStartupInfo(SI); Result := ((SI.dwFlags and STARTF_USESHOWWINDOW) = 0); end; 

And then use it as such:

  if (not WasRanFromConsole()) then begin Writeln(''); Writeln('Press ENTER to continue'); Readln; end; 
+2
source share

Wow, this is really impressive! I tested your solution and worked great.

So you can do something like this:

 function isOutputRedirected() : boolean; var StdOutHandle : THandle; bIsNotRedirected : boolean; FileInfo : TByHandleFileInformation; begin StdOutHandle:= GetStdHandle(STD_OUTPUT_HANDLE); bIsNotRedirected:=( NOT GetFileInformationByHandle(StdOutHandle, FileInfo) and (GetLastError = ERROR_INVALID_HANDLE)); Result:=( NOT bIsNotRedirected ); end; function isStartedFromConsole() : boolean; var SI: TStartupInfo; begin SI.cb := SizeOf(TStartupInfo); GetStartupInfo(SI); Result := ((SI.dwFlags and STARTF_USESHOWWINDOW) = 0); end; function GetConsoleSize() : _COORD; var BufferInfo: TConsoleScreenBufferInfo; begin GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), BufferInfo); Result.x:=BufferInfo.srWindow.Right - BufferInfo.srWindow.Left + 1; Result.y:=BufferInfo.srWindow.Bottom - BufferInfo.srWindow.Top + 1; end; 

And finally:

 var cKey : Char; fCursorPos : _COORD; if( NOT isOutputRedirected() ) and( NOT isStartedFromConsole() ) then begin // Windows app starts console. // Show message in yellow (highlight) and at the bottom of the window writeln; fCursorPos:=getConsoleSize(); Dec( fCursorPos.y ); Dec( fCursorPos.x, 40 ); SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), 14 ); SetConsoleCursorPosition( GetStdHandle(STD_OUTPUT_HANDLE), fCursorPos ); write( '<< Press ENTER to close this window >>' ); read(cKey); end; 

Greetings!

Erwin Haantes

+2
source share

For the foo.exe program, create a batch file named foo_runner.bat. Do not document this command, because it is not intended to be used by anyone, but uses it as the target of any shortcuts that your installer makes. Its contents will be simple:

 @echo off %~dp0\foo.exe %* pause 

This part of %~dp0 provides the directory where the batch file is located, so you need to run foo.exe in the batch file directory, and not grab it from another location on the search path.

+1
source share

All Articles