Tuesday, January 29, 2008

TDM Rerun #2: A Better Build Process

I had to live with the problem. Until the day when it occurred to me that I could very probably find the solution to my problem on the internet. So I connected to Deja News and searched.

- A Better Build Process, The Delphi Magazine #49, September 1999

A life without Google? Was that really possible? I can hardly remember those times.

The article was written in Delphi 4 times and deals with building Delphi applications from a command line. More specifically, it solves the problem of dynamically recreating project.res file, which is something that you have to do if you want to increment build number in the version info resource during the build process. (A utility to increment build number was included, too.)

Also on display was a batch file that built my trusty GpProfile profiler and a "very complicated" tail utility.

{$APPTYPE Console}
program tail;
SysUtils, Classes;
str : TStringList;
i : integer;
first: integer;
str := TStringList.Create;
first := str.Count-StrToIntDef(ParamStr(2),20);
if first < 0 then first := 0;
for i := first to str.Count-1 do Writeln(str[i]);

Not much has changed in the Delphi world - I would code it almost identical today.

Links: article (PDF, 70 KB), source code (ZIP, 185 KB)

Monday, January 28, 2008

TDM Rerun #1: Safer Sockets

Instead of fixing the ScktComp unit it is probably better to create a derived class, override the Read method, paste code from ScktComp and make this small modification. Choose your way but beware: CancelIO is not available in Windows 95, only in 98 and NT 4.

- Safer Sockets, The Delphi Magazine #44, April 1999

Those were the times ... Most of computers were still running Windows 95, the actual Delphi version was 4 and we were using TWinSocketStream for data transfer over Windows sockets. Oh, happy days. Not!

I won't comment this article much. It describes how to fix a problem with an obsolete component that should never be used in modern software. I still remember how happy I was switching to ICS ... Still, I promised all my articles and I have to start with this one.

This article represents another important milestone in my 'journalist' career - it was my first article written in English language and a start of very happy long-term relationship with The Delphi Magazine.

Links: article (PDF, 49KB), source code (ZIP, 1 KB)

Thursday, January 24, 2008

Make a better Delphi

Delphi is a good language and Delphi IDE is a good development environment and together they make a good pair, but admit it - Delphi language is missing some modern language constructs (parameterized types, for example) and Delphi IDE can be rough at the edges. We can't do much about the former, but Delphi IDE can be greatly enhanced by installing third party add-ons (and great thanks to Borland/CodeGear for enabling us to do so).

Recently I've noticed that I can't really function well on a bare-bones IDE. I'm heavily dependent on three IDE extensions - GExperts, DDevExtensions, and ModelMaker Code Explorer. I decided to dedicate some blog space to them and describe what they do and why I like them.

It would take too much space to describe every aspect of those three add-ons, so I decided to focus on a navigation in a broad sense - moving inside unit, between units, between code and design view etc. Even more, I won't cover every navigational helper in those three, I'll just focus on the features I'm using regularly.

Delphi IDE

Delphi IDE has some neat navigation tools built in. First, there are markers. You can drop ten markers by pressing <Ctrl><Shift>number, where number is a numeric key from 0 to 9 (you don't have to use numeric keyboard keys, standard keyboard numbers will work just fine). By pressing <Ctrl>number you'll be transferred to the line containing the marker. To delete a marker you just press <Ctrl><Shift>number while the caret is positioned in the line containing the marker.

Another neat feature of the Delphi IDE is the ability to jump from method implementation to its declaration and back. The shortcut for this operation is <Ctrl><Shift><UpArrow> or <Ctrl><Shift><DownArrow> (both work the same).

Delphi: find referencesWhen you want to find where some identifier is used (in current unit and in the rest of the project), you can use find references (<Ctrl><Shift><Enter>) command. I'm mentioning it here because you can also use it to jump to the place where the identifier is used.

I'm also using the unit list (<Ctrl><F12>) and form list (<Shift><F12>) regularly. First shows the list of units in the project and second the list of forms. Just double-click the unit/form or select it and press <Enter> and it will open in the IDE.


GExperts is a set of freeware experts that enhance Delphi IDE in various ways. Only small subset is connected to the navigation.

GExperts: open fileOne of the most useful helpers is open file expert. Press the key (I have it configured to <Ctrl><Shift><Alt>O) and up pops a list of all files that are accessible from the current project. You can select the unit and press <Enter> to open it (alternatively you can double-click that unit) but before that you can - and that's even more important - start typing in the filter box and the expert will remove all non-matching units.

GExperts: procedure list

Another useful tool is procedure list expert. As the name suggests, it presents a list of methods in the current unit, which you can, of course, filter by typing. Double-click or <Enter> will beam you to the selected method.

GExperts: form designer menuGExperts also offers bunch of helpful editor experts. Locate matching delimiter (<Ctrl><Alt><LeftArrow>/<RightArrow>) jumps to a matching delimiter - from left to right bracket, from begin to end, from implementation to interface and so on. Locate previous/next identifier (<Ctrl><Alt><UpArrow>/<DownArrow>) locates previous/next occurrence of the identifier under the caret.

The last navigational help in the GExperts is hidden in the form designer pop-up menu. It is extended with the find component reference command. Right-click on a component or control and select this option and GExperts will bring up the editor, focused on the part where this component/control is declared. Or used for the first time in the code. Sometimes former and sometimes latter and I can never guess which one it will be.


DDevExtensions: unit listDDevExtensions is developed by Andreas Hausladen, author of well-known DelphiSpeedUp. I'm mostly using it because it reconfigures the <Tab> key to indent a block of code and <Shift><Tab> to unindent it, but that's not a navigational extension so you can pretend you didn't hear that from me. It also implements smart home functionality. First time you press the <Home> key, caret will jump to the first column in the row. If you press the <Home> button again, caret will jump to the first non-blank character in that line. Useful.

DDevExtensions also redefines unit list and form list keyboard shortcuts so that display a list that can be filtered - similar to the GExeperts' open file expert.

ModelMaker Code Explorer

MMX: ExplorerModelMaker Code Explorer (or MMX as it is usually called between loving users) is one of the largest Delphi IDE add-ons on the market. Its most important functionality is refactoring - creating classes, fields, properties, and methods becomes lightingly fast with MMX - but it also offers some navigational functionality. MMX is not free like GExperts and DDevExtensions but I can assure you that it's worth every cent.

An important part of MMX interface is the Explorer window. It lists every class and method in the current unit, has movements synchronized with the editor and is filterable.

MMX: indexerSimilar to Delphi's find reference is find in module command (<Ctrl><Shift><Alt>F). It locates all occurrences of  the identifier in the current module and uses tabbed interface to store old searches. Great advantage of MMX over Delphi is that it works in older IDEs and that it works on a non-compilable code.

MMX also offers extended up/down navigation. Delphi's shortcuts <Ctrl><Shift><UpArrow>/<DownArrow> are redefined to jump (in addition to the default behaviour) between propery declaration, its getter and setter, between constructors and destructors and between overloaded methods. Highly useful.

MMX: global historyThe last enhancement on my today's list is global history. MMX will automatically remember any method where you spent more than a few seconds. When you press the keyboard shortcut (I have it set to <Ctrl>` - the latter being the key left to the '0' key), MMX displays pop-up menu with most recently edited methods. Click one and you're instantly transferred there.

Other enhancements

That's all by me, I've exhausted my knowledge. Here's where you, dear reader, can help. If you know a trick that I didn't mention or a helpful expert that I don't have installed, do tell me about it in the comments. You'll help me and other Delphi programmers.

Wednesday, January 23, 2008

Reposting TDM Articles

Like some other authors (Hallvard Vassbotn, Bob Swart) I'll be republishing articles I wrote for The Delphi Magazine. [Chris, thanks for the permission!]

Unlike Dr. Bob and Hallvard I won't have problems with choosing from my TDM articles - I published much less than them so I'll simply republish everything :)

In the next months you can expect online versions of my articles:

  • Safer Sockets [TDM #44]
    Describes an issue with TWinSocketStream and a presents a workaround.
  • A Better Build Process [TDM #49]
    How to build Delphi programs from a command line; introduces some helper tools.
  • Time Is The Simplest Thing [TDM #55]
    The art of timezones. Introduces GpTimezone unit, which is still alive and regularly updated.
  • Let's Cooperate [TDM #68]
    My first take on steampunk process synchronisation - not via OS primitives but by using file system. Introducing GpFileSync unit, which somehow never made it to my web site.
  • SvCom 4.0 [TDM #69]
    A review of SvCom service development package. Still alive and well, currently at version 7.
  • GExperts 1.0 [TDM #72]
    A review of Delphi experts we all love.
  • File Sharing On Linux [TDM #84]
    My only article on Kylix continued the Let's Cooperate theme.
  • A Synchronisation Toolkit [TDM #86]
    Simple multi-process synchronisation and communication primitives. Introduces the GpSync unit, which is sill alive and regularly updated.
  • My Data Is Your Data [TDM #88]
    Describes an easy to use wrapper around Windows' shared memory. Introduces GpSharedMemory unit.
  • Synchronisation Toolkit Revisited [TDM #91]
    An upgrade of the GpSync unit, introducing message queue.
  • Shared Pools [TDM #95]
    Shared memory pools. I never developed this concept any further.
  • Shared Events Part 1: Rationale, Design, Implementation [TDM #97]
    Cross-process event dispatch using shared memory. Introduces GpSharedEvents unit.
  • Shared Events Part 2: Redesign [TDM #102]
    Implementation details and a redesign.
  • A Portable XML [TDM #105]
    A review of the OmniXML library. Still alive and regularly updated.
  • Many Faces Of An Application [TDM #107]
    How to write an application that can be a service, Windows GUI and system tray icon monitor.
  • Thread Pooling, The Practical Way [TDM #112]
    Describes Windows thread pool, various problems with it and some workarounds.
  • Put It In A Tree [TDM #118]
    Discusses various approaches to creating a tree structure from a list of mail messages.
  • To Manage Memory [TDM #126]
    An introduction to memory management algorithms and structures plus a short overview of the (at that time very young) FastMM memory manager.

And that's all folks. I'll probably attack this list in chronological manner. If you have any preferences I may make an exception and change priorities so don't be afraid to ask for article you want to read first.

Monday, January 14, 2008

Disappearing Welcome Page

One of the most annoying bugs in Delphi 2007 must be the disappearing Welcome Page. Sometimes, when you do Close All, IDE decides that Welcome Page contains highly sensitive material that is not intended for your eyes, and deletes the contents. Like this:

Empty Welcome Page 

By trial and error (and much luck) I have found a way to restore it.

Step 1: Click into the edit box next to the 'house' icon. Text (bds:/default.htm) will be selected.

Step 1

Step 2: Press <Backspace> to delete the text.

Step 2

Step 3: Press <Enter>. Nothing will happen, but the trick won't work without this.

Step 4: Click the 'house' icon. Welcome Page should reload.

Working Welcome Page

If that doesn't work, you can always close the welcome page and reopen it via View, Welcome Page menu. Is is, however, faster to try click-backspace-enter-click trick first.

Saturday, January 12, 2008

TDataset Enumerator

You already know that I love all things enumeratorish. Just recently I found a great example that combines three kinds of Delphi magic to allow the programmer to write very simple TDataset enumeration loops. [And for the sake of my life I can't remember where I first saw the link to this code. Apologies to the original author.] [Updated 2008-01-14: Jim McKeeth found my source. Apparently Nick Hodges was the first to blog about this enumerator. Thanks, Nick!]

This TDataset enumerator was written by Uwe Raabe and is freely available in the CodeGear's Code Central as item #25386.

The three trick he uses are:

  1. Data helper is used to push enumerator support into TDataset.
  2. Standard enumerator support to enumerate over dataset and return special descendant of the Variant type for each record.
  3. Custom variant type to automagically access record fields by by adding .RecordFieldName to the variant variable. [I must admit I didn't even know this was possible to do in Delphi.]

All together those tricks allow you to write very simple TDataset iteration loops (code taken from the example in the DN #25386):

Employee: Variant;
S: string;

for Employee in QuEmployee do begin
S := Trim(Format('%s %s', [Employee.First_Name, Employee.Last_Name]));
if Employee.Hire_Date < EncodeDate(1991, 1, 1) then
S := '*' + S;

Excellent job!

Thursday, January 10, 2008

Unicode Delphi

The Oracle at Delphi (aka Allen Bauer) has posted some information on Unicode support in the next Delphi (codenamed Tiburon).

Firstly, let me say that I really appreciate that we'll finally get VCL Unicode and reference-counted wide string support in Delphi. I really really really appreciate that.

Secondly, I must say, and I must say this very loud, that I DON'T LIKE HOW IT WILL BE IMPLEMENTED! (And I apologize for yelling in public.)

Allen wrote

This new data type will be mapped to string which is currently an AnsiString underlying type depending on how it is declared.  Any string declared using the [] operator (string[64]) will still be a ShortString which is a length prefixed AnsiChar array.  The UnicodeString payload will match that of the OS, which is UTF16.


If your code must still operate on Ansi string data, then simply be more explicit about it.  Change the declaration to AnsiString, AnsiChar, and PAnsiChar.  This can be done today and will recompile unchanged in Tiburon.

I wrote my response in comments to the Allen's post and I'll restate it here.

This is very very bad. This will not work for our company and I presume for many of us working with legacy applications with millions of source lines. We have applications using 3rd party components and libraries (all with source code, thankfully). We have applications using libraries that can be compiled for Win32 and for DOS (with BP7 and plenty of IFDEFs, of course). And we have no illusions that they will continue to work if string suddenly becomes an UTF-16-holding type.

We would like to move to Unicode Delphi, but we would like to do it gradually, when starting work on new applications. That way, we can test all units, components and libraries as they are introduced in the new program and we can fix all problems that will occur.

So please, CodeGear, give as an 'Unicode application' option. It should be disabled for old applications and enabled for new ones. That would be a much better plan.

[If you agree with me, make sure to post a comment in Allen's blog stating your opinion. If you don't agree with me, make sure to post a comment in Allen's blog stating your opinion. He needs as much feedback as possible to make decitions that will be good for us.]