Determine the Delphi installation directory from a batch file

 Delphi  Comments Off on Determine the Delphi installation directory from a batch file
Apr 172014

Since I work on several projects that require multiple Delphi installations, I needed a way to start the correct Delphi IDE and call the correct command line compiler for each of these projects. There apparently is no way to automatically detect the Delphi version from the .dof,.dpr or .dproj file (or at least this is limited), so I added batch files to these projects that call the correct Delphi version.

One of the things that irked me was that I had to adapt these batch files whenever I was working on a different computer and Delphi was not installed in the default installation directory. Finally I found a solution for this: The required information is stored in the registry at


And the place where this is stored does not vary. I set out to find a way to read the registry from a batch file and found this answer on stackoverflow.

So, here is the result. A batch file that, when called with a Delphi version as parameter, returns the directory it is installed in:

@rem set the DelphiPath variable for various Delphi versions


set DelphiVersion=%1

rem Support for Windows 7/8, 64 Bit
set ProgFiles=%ProgramFiles(x86)%
if not "%ProgFiles%"=="" goto Win64Bit
set ProgFiles=%ProgramFiles%

set DelphiPath=

rem this is equivalent to a case/switch statement
rem call :Delphi%DelphiVersion% resolves into call :Delphi6 etc.
call :Delphi%DelphiVersion%
goto DelphiEndCase
  call :ReadReg Borland\Delphi\6.0
  goto :eof
  call :ReadReg Borland\Delphi\7.0
  goto :eof
  call :ReadReg Borland\BDS\3.0
  goto :eof
  call :ReadReg Borland\BDS\4.0
  goto :eof
  call :ReadReg Borland\BDS\5.0
  goto :eof
  call :ReadReg CodeGear\BDS\6.0
  goto :eof
  call :ReadReg CodeGear\BDS\7.0
  goto :eof
  call :ReadReg Embarcadero\BDS\8.0
  goto :eof
  call :ReadReg Embarcadero\BDS\9.0
  goto :eof
  call :ReadReg Embarcadero\BDS\10.0
  goto :eof
  call :ReadReg Embarcadero\BDS\11.0
  goto :eof
  call :ReadReg Embarcadero\BDS\12.0
  goto :eof
  call :ReadReg Embarcadero\BDS\14.0
  goto :eof

echo DelphiPath: "%DelphiPath%"
if exist "%DelphiPath%" goto allok
echo *** Error: Directory "%DelphiPath%" does not exist. ***
goto :eof

endlocal & set DelphiPath=%DelphiPath%
rem echo DelphiPath: "%DelphiPath%"
goto :eof

rem read the registry entry
set DelphiPath=
FOR /F "usebackq skip=2 tokens=3,*" %%A IN (`REG QUERY HKCU\Software\%1 /v RootDir 2^>nul`) DO (
  set DelphiPath=%%A %%B
rem remove one trailing space which might have been added because %%B was empty
rem remove any quotes
set DelphiPath=%DelphiPath:"=%
rem add quotes
set DelphiPath="%DelphiPath%"
rem remove space before the closing quote
set DelphiPath=%DelphiPath: "="%
rem remove any quotes
set DelphiPath=%DelphiPath:"=%
goto :eof

On this particular computer …

call delphipath.cmd XE6
echo %DelphiPath%

… writes …


… and this …

call delphipath.cmd XE4
echo %DelphiPath%

… writes …

C:\Program Files (x86)\Embarcadero\RAD Studio\11.0\

This batch file is now part of my collection of build tools.

 Posted by on 2014-04-17 at 21:30

Experimental GExperts Version 1.37-2014-04-16 released

 Delphi, GExperts  Comments Off on Experimental GExperts Version 1.37-2014-04-16 released
Apr 162014

Following up on Erik’s overnight surprise release of GExperts for Delphi XE6, here is a new release of my experimental version with the code formatter.

The DLLs for all Delphi versions supported by the official GExperts are included.

BE WARNED: This version has only tested in so far, that it can be installed into Delphi XE6. Nothing else!

Head over to the Experimental GExperts page to download the new version.

 Posted by on 2014-04-16 at 22:20

Windows advanced Batch programming call :bla%var%

 Windows  Comments Off on Windows advanced Batch programming call :bla%var%
Apr 132014

While looking for the equivalent of a switch / case statement for Windows batch files, I came across this answer on Stackoverflow.

Basically, what it does is using environment variable substitution for simulating:

case %var% of
  1: rem do something
  2: rem do something else
  3: rem do something completely different

The code looks like this:

call :SomePrefix%var%
goto aftercase
  rem do something
  goto :eof
  rem do something else
  goto :eof
  rem do something completely different
  goto :eof

If “do something” is some complex code that’s pretty nifty and possibly a bit faster than the naive approach:

if "%var%"=="1" rem do something
if "%var%"=="2" rem do something else
if "%var%"=="3" rem do something completely different

But still: The readability suffers quite a lot. And if speed was an issue, why would you use a batch file in the first place? Anyway, I had a use case for it so I used it.

 Posted by on 2014-04-13 at 14:58

Experimental GExperts Version 1.37-2014-04-12 released

 Delphi, GExperts  Comments Off on Experimental GExperts Version 1.37-2014-04-12 released
Apr 122014

The latest version is the first version that supports Delphi XE5 and also officially XE4.

There is nothing really new about the formatter code. But the new release can be installed even if you don’t have the official GExperts installer (yet). This is an extract from the readme file:

** Installing without an official installer **
With two Delphi Releases per year and Erik Berry being busy otherwise,
new GExperts releases have been lagging behind. So in case there is no
official GExperts installer yet, these are the steps to install the
experimental version by hand:

1. Create a suitable GExperts directory. It doesn't have to be a
   subdirectory of %ProgramFiles% but may be anywhere, even on a network
   share if you are sure this share will always be available.
2. Extract all files from the ZIP somewhere
3. Copy all files from the extracted directory to the GExperts directory.
   (Do *NOT* copy the subdirectories!)
4. Copy the appropriate GExperts DLL from one of the subdirectories
   EditorExpert or RegularExpert to the GExperts directory.
5. Copy the files from the subdirectory FromRegularRelease to the
   GExperts directory.
6. Copy the appropriate cmd from the install subdirectory. To the GExperts
7. Make sure that the Delphi IDE is not running
8. Run the cmd file. It will register the GExperts dll with the Delphi IDE.
9. Start Delphi and check that the new GExperts DLL has been loaded by
   opening the GExperts About dialog.

In theory it is possible to install GExperts for all Delphi versions into
the same GExperts directory. But be warned: This has not been tested

Head over to the Experimental GExperts page to download the new version.

 Posted by on 2014-04-12 at 18:49

Delphi Console Applications

 Delphi  Comments Off on Delphi Console Applications
Apr 102014

Delphi allows to write Console applications as well as GUI applications, while the latter is the default.

But “Console” application is not clearly defined in the documentation as I found out only today (and I have been programming in Delphi for about 20 years).

There is the {$AppType Console} compiler directive which corresponds to the /cc compiler switch and which has no corresponding setting in the compiler options dialog. The File -> New -> Console Application menu generates a project containing this directive. As documented in the Delphi online help this compiler directive opens the INPUT and OUTPUT files which are associated with the stdin and stdout handles. In addition, at least since Delphi 2007 it also opens the ErrOutput file which is associated with the stderr handle. (The latter doesn’t seem to be documented.) Also, the IsConsole function returns true for programs compiled with this directive. The conditional symbol CONSOLE is not defined for these programs unless you also set the linker option "Generate console application". (Which is odd. Why should a linker option have any effect on a conditional symbol for the compiler? But that’s how it is.)

Then, there is the linker option "Generate console application". This option is not automatically set if you use the File -> New -> Console application menu. If you set it, the conditional symbol CONSOLE is defined. You can also set this option for a GUI program. In this case, CONSOLE is also defined and in addition to your program’s form, you will also get a console window. Also IsConsole returns true and the files INPUT, OUTPUT and ErrOutput are opened.

So there are four possible combinations:

  1. AppType CONSOLE + "Generate console application" is set -> CONSOLE is defined and IsConsole returns true
  2. AppType GUI + "Generate console application" is set -> CONSOLE is defined and IsConsole returns true
  3. AppType CONSOLE + "Generate console application" is not set -> CONSOLE is not defined and IsConsole returns true
  4. AppType GUI + "Generate console application" is not set -> CONSOLE is not defined and IsConsole returns false

I could not find any difference between the cases 1 and 2. Maybe some additional startup code is generated in one of these cases? But the executable size doesn’t seem to change either. So, what is the actual point of the compiler directive?

So the important lesson here is:

The files INPUT, OUTPUT and ErrOutput are opened automatically if IsConsole returns true, regardless of the conditional symbol CONSOLE. I don’t really see the use of the latter.

For the curious here is the program code I used to find out the above:

program ConsoleTest;



  WriteLn('CONSOLE is defined');
  if IsConsole then
    WriteLn('CONSOLE is not defined')
    Windows.MessageBox(0, 'CONSOLE is not defined', nil, MB_OK or MB_ICONINFORMATION);
  if IsConsole then begin
    WriteLn('IsConsole returns true');
    WriteLn('Press Enter');
  end else begin
    Windows.MessageBox(0, 'IsConsole returns false', nil, MB_OK or MB_ICONINFORMATION);

 Posted by on 2014-04-10 at 10:55

Creating DLLs with Delphi

 Delphi  Comments Off on Creating DLLs with Delphi
Apr 102014

I just stumbled upon an article on The Delphi Wiki that I wrote years ago:

Creating DLLs

It’s still mostly valid. There have been some improvements in newer Delphi versions, in particular since Delphi 2006 you no longer need the borlndmm.dll if you want to use ShareMem. (But still: Using ShareMem means that your DLL can only be used by Delphi programs, so don’t do that, if you can avoid it.)

That’s not the only article I wrote for that Wiki. It’s interesting to see how much time I apparently had on my hands back then. Sometimes I wish those “good times” came back.

 Posted by on 2014-04-10 at 09:27

Updating Windows Defender signatures (only)

 Windows 8.1  Comments Off on Updating Windows Defender signatures (only)
Apr 082014

One of my problems with Windows 8 is that Microsoft recommends to keep Automatic Updates on the setting “Install updates automatically” while I prefer the setting “Download updates but let me choose whether to install them”. (Actually I don’t really want to choose whether to install them but rather when to install them.)

It’s possible to change this setting, but Windows Defender then won’t download its signature updates because that’s bound to the same mechanism as Windows Update. So every day I get a message on my boot screen, that there are important updates available and that I should start Windows Update to install them. This is annoying like hell.

Thankfully with a little bit of googling I found a solution. There is a tool that that checks for signature updates (an supposedly downloads them, I couldn’t try it yet because I just updated them using Windows update). It must be called like this:

"C:\Program Files\Windows Defender\MpCmdRun.exe" -SignatureUpdate -MMPC

So adding this command to e.g. task scheduler should solve yet another Windows 8 annoyance for good.

 Posted by on 2014-04-08 at 09:54

Translating file filters

 Delphi, dzLib  Comments Off on Translating file filters
Apr 012014

You know these innocuous file filters used by Open / Save dialogs? They usually look like this:

First Filetype (*.ex1)|*.ex1|Second Filetype (*.ex2)|*.ex2|All Files (*.*)|*.*

And since typing them is so difficult because you must remember to type all these pipe symbols, Delphi provides a property editor for it.

Is there anybody who actually likes this property editor? I think it’s even more annoying than directly typing these strings. But why, oh why do I have to type the file filter twice? Once for the description and once for the actual filter?

It gets worse: Just imagine you localize an application that uses many such file filters. So you have lots of strings that contain the ‘All Files (*.*)|*.*’ part, but are different because they support different, but partly overlapping file types. You end up translating the same text over and over again:

First Filetype (*.ex1)|*.ex1|All Files (*.*)|*.*
Second Filetype (*.ex2)|*.ex2|All Files (*.*)|*.*
First Filetype (*.ex1)|*.ex1|Second Filetype (*.ex2)|*.ex2|All Files (*.*)|*.*

Maybe you even spelled ‘All Files’ inconsistently as ‘all files’, ‘all Files’ or ‘All files’. Which confuses the translator and looks bad in your program.

And now add human error: The translator overlooks the part with the actual filter because he just doesn’t know that the string contains two different kinds of text: The first part is human readable and must be translated, the second part is used by the computer to filter files, and it must not be translated, it must not even be changed. Your translator might create translations like this:

Erster Dateityp (*.ex1)|All Files|*.*
Zweiter Dateityp *.ex2|All Files (*.*)|*.*
Erster Dateityp (*.ex1)|*.ex1|Zweiter Dateityp  (*.ex2)|*.*e|All Files (*.*)|*.*

Did you notice the errors? Nobody really does and the program gets shipped. Now you have not only a translation error but also a bug in your program, because these strings are no longer valid file filters.

After this happened to me the gazillionth time, I had enough, so I created the following helper code:

  IdzFileFilterBuilder = interface ['{1EEE52D6-EA31-4C4D-8454-32B2C2BE1814}']
    function Add(const _Description: string; const _Mask: string; _AddMaskToDesc: Boolean = True): IdzFileFilterBuilder;
    function AddAvi: IdzFileFilterBuilder;
    function AddBmp: IdzFileFilterBuilder;
    // ... some more ...
    function Filter: string;

function FileFilterBuilder(_IncludeAllFiles: Boolean = True; const _AllSupported: string = ''): IdzFileFilterBuilder;

The idea is to call the FileFilterBuilder function once and add any filters you need to the returned interface by chaining the calls like this:

  od.Filter := FileFilterBuilder.AddBmp.AddJpg.AddGif.Value;

Or, if you like this formatting better:

  od.Filter := FileFilterBuilder

No unwieldy strings that mix human readable and computer interpreted text, just a simple list of easily readable file extensions. And even if you want to do something more unusual like having one entry for all supported file types and adding a file type that is not predefined by the interface like this:

  od.Filter := FileFilterBuilder(true, 'All supported types')
    .Add('Some other graphics format', '*.blub')

It is still quite readable. Of course the above strings must be translated, so we add _() function calls for dxgettext:

  od.Filter := FileFilterBuilder(true, _('All supported types'))
    .Add(_('Some other graphics format'), '*.blub')

Notice that only the part that actually needs translation is inside the _() translation marker? There is no more way for the translator to change essential strings of your programs. He might create wrong translations but your program will still run.

Of course that was only the interface. I guess, implementing it won’t be too hard for most of my readers. But since I am in a generous mood, I give it to you for free (no, that’s not an April fools joke). Just download it from my dzlib utility library on sourceforge. The file is u_dzDialogUtils and it contains a few more of the predefined .AddXxx methods. Also, the library will soon contain the German translations for the filter strings, if you are interested. You will also need the pseudo templates in the templates subdirectory.

Oh, did I mention this little gem:

function TdzFileFilterBuilder.AddPicture: IdzFileFilterBuilder;
  Result := Add(_('Picture files'), '*.bmp;*.jpg;*.jpeg').AddBmp.AddJpg;

It adds three entries:

  • Picture Files (*.bmp;*.jpg;*.jpeg)
  • Bitmap Files (*.bmp)
  • JPEG files (*.jpg;*.jpeg)

Resulting in the following file filter string:

'Picture Files (*.bmp;*.jpg;*.jpeg)|*.bmp;*.jpg;*.jpeg|Bitmap Files (*.bmp)|*.bmp|JPEG files (*.jpg;*.jpeg)|*.jpg;*.jpeg'

(with the ‘All files’ entry added, if you passed true to the FileFilterBuilder function.)

 Posted by on 2014-04-01 at 21:29