Archive for July, 2009

Debunking the Delphi 7 Myths

In my last post on why you really should upgrade from Delphi 7, I seem to have upset a number of people. Some were very angry, some were moderate. Some were eloquent. Most of them were dead wrong! A few of the comments received I really should react to, but I think the volume justifies a follow-up post. This post done, I’ll take my head out of the hornet’s nest and leave Delphi 7 developers alone. For a while at least.

Update: I really meant to leave this topic alone, but only days after I posted this, the Delphi 2010 hype started in all earnest. I didn’t take part in the beta this time, so I was unaware of some of the new things coming out. Some pertained to this post, so I’ve added some extra comments. Also, I needed to correct/clarify a few thngs. The blue stuff will be my last update to this post.

First thing that I should clarify, is that I don’t work for Embarcadero/CodeGear/Borland and never have. In fact, if you google me, you’re bound to come across a number of instances where I have had a real go at them. Sometimes over documentation, sometimes over Quality Central reports with all the required info that stay open, sometimes about I-don’t-remember-what.

I wrote the post of my own volition, because I thought it needed to be said. Onward then…

The one gripe that some commenters have raised, is that of documentation. Here I have to say, they have a point. For quite a while I actually had the Delphi 7 help files on hand on my development machine and there is an IDE expert that you can download that will allow you to use the Delphi 7 help files directly from the newer IDE. The new help files are also available online though, and I find Google is often better at finding things that any table of contents is.

Speaking of the IDE, I noticed a pattern in the comments. Those who don’t use the new IDE, hates it. It reminds me of a story my father-in-law once told me. His grandfather only ever rode in a car once. And he endured it for about five minutes before he demanded to be let out. The reason? The car was noisy and walking is less bumpy on uneven roads. So long distances he covered with the bicycle and shorter distances he walked.

If you really can’t stand the new IDE, there are three things you need:

  1. The IDE expert for the old help files
  2. DDevExtentions has a lot of cool things in it. The only uncool feature it has is the old-style component palette. If you hate the new one, use that. Update: And Delphi 2010 includes a new, significantly upgraded version of the palette. So when that ships, you finally have a choice of which one to use. That should lay the debate to rest, although I have a feeling the new and very shiny IDE insight feature may overtake both the palette and the toolbox. At least for me.
  3. Undock the IDE windows, so you can have the old chaotic layout that you love.

These seem to be the major things people hate about the new IDE, and they are all perfectly solvable (breakable?). And don’t even try to tell me how slow the new IDE is thanks to .NET – it starts up a helluvalot faster than Delphi 7 ever did if you launch the Delphi personality only.

The next big gripe is that third-party tools can give you a lot of the same functionality. A few slight issues with that.

Here is a smorgasbord of the third-party tools that were mentioned – I don’t think I missed any, but you’re welcome to check that I didn’t cheat:

  1. GExperts is fantastic but has not been updated at all since October 30, 2008. That is almost a year, folks. And those where compatibility and the first new expert for a while. A quick glance at the change log shows that the last significant version (1.3) came out almost four years ago. You’re right, absolutely the kind of thing you should depend on – a tool with very little support or progress in four years. Update: I don’t personally use GExperts anymore, in part because a lot of its best features are included in the newer IDE. But a lot of people still get a lot of value from it and it is of course available for Delphi 2009. The point here was that “I use GExperts” make a lousy excuse for not upgrading. Also, I’m told the support is really quite outstanding.
  2. But it gets worse. Someone actually mentioned Bold. Note that I didn’t link the URL, because BoldSoft doesn’t seem to even have it listed on their site anymore. In fact the latest relevant link I could find was this one, where it is explained that development stopped back in 2002 already! I know it may have been valuable back when you still lived with your mother (or had milk teeth), but your continued dependence on an extinct product is very dangerous indeed.
  3. Code Rush is fabulous, as is almost anything from DevExpress. As with almost anything from DevExpress it is also fabulously expensive. And it only improves the editor. And did I mention the Delphi version is extinct? Yeah. Or perhaps it is a typo on the website, but it is described as a “powerful Visual Studio® .NET add-on that…” blah blah blah.
  4. I have honestly never heard of cnPack before. It has actually been updated in the last couple of months and it may very well be enormously capable – I don’t know. I suspect it may be a tad dated though, given that they still link you through to bdn.borland.com. Surprisingly, that link still works although perhaps someone should notify them Borland no longer has anything to do with Delphi and may in fact now have a preference for Cobol. On second thought, just leave it. Perhaps they’re happy that way.
  5. JCL. OK, first: I don’t like JCL, even though lots of other people do. But you do know it is available for Delphi 2009, don’t you?
  6. ModelMaker Code Explorer. Also available for Delphi 2009.

Third issue is about the new features.

Apparently, “Class Helpers are not fully documented because THEY ARE NOT DESIGNED TO BE USED IN YOUR APPLICATIONS!” And also “The documentation that DOES exist for them specifically states that they are not intended for general purpose use.” Uh-huh? Let’s double-check the documentation, shall we? The initial intent of the language feature was to provide compatibility between Delphi Win32 and Delphi.NET. They did this by providing language support for the Decorator pattern. The notion that class helpers must not be used have died out along with other such relics, like the belief that overloaded functions are too dangerous to be allowed in the language. Update: Yes I am aware that the link I supplied still mentions that it was meant for platform-RTL binding and should not be used in general designs. Three points here: First, it is documented just fine. Second, I never said it is the perfect way to extend classes that you can modify. You simply wouldn’ use it in the place of inheritance or simply adding what you want. It is a technique to extend classes that you cannot modify. I’ll post a more about that once the Delphi 2010 hype machine has quieted down a little. Third, the help entry text dates from at least Delphi 2005 and was superceded by this famous post which shows that even within CodeGear/Embarcadero the opinion has shifted. Delphi 2007’s GlassFrame property is in fact a fantastic example of one common use case for class helpers. And its use had nothing to do with .NET runtime compatibility.

And some complaints about how badly generics work. Funny, I use them whenever they make sense and have had only one real issue with them and that is a compiler bug unrelated to generics. Perhaps someone would like to check if the bug exists in Delphi 7?

Lastly, some people complain about things like strings in case-statements. Which of course you had in Delphi 7. Errr… wait, there is something wrong with that last sentence. Ditto for complaints about Delphi 2009 features that are not as well implemented as in Visual Studio. If Delphi 7 didn’t have them, the argument is flawed.

Lots of positive comments also, the best of which highlighted several big benefits that I didn’t even think of.

Give the newer Delphi versions a try. Not for a day, but for the next real project you tackle. I bet you’ll learn to like it very quickly.

Update: Changed the ending, because one of the posters (quite rightly) took issue with it.

Advertisement

Comments (50)

Why you really should upgrade from Delphi 7

Delphi 7 is probably the most popular version of Delphi that has ever shipped. So much so that several developers staunchly stand by their favourite of many years and refuse to move up.

But Delphi has really grown with some truly fabulous things getting added in the time since the old stalwart shipped. I have compiled a list of five reasons why I believe no Delphi developer should remain on Delphi 7. Unlike most of these “top 5” lists, mine is not a list of five magic bullet features, but rather five areas of importance.

So here is my list – in random order – of the top five reasons to upgrade from Delphi 7:

1. The new IDE. Yes, I know it is different from what you’ve grown used to in Delphi 7 and yes, I know it looks a lot like Visual Studio. But is a whole lot faster than Visual Studio 2008 and much more productive than Delphi 7. Stability is not a problem on Delphi 2007 and Delphi 2009 and several specific improvements make a fantastic difference:

  • The new component toolbox. If you really want to, you can use the legendary Andreas Hausladen’s component palette replacement. But you really shouldn’t. The old, horizontal layout is hugely wasteful and gets more and more inefficient as the number of components grow. The new one is more compact, reads easier, can be filtered, works great with the mouse wheel, works great with the keyboard and requires less mouse movement when exploring.
  • The editor has learnt some interesting new tricks, including code templates. You really want that. And Syncedit. And regions. And XML comments.
  • The CPU window can now show only the disassembly view if that is all you need. How sweet is that?
  • Build events can copy compiled files where they’ll be needed, append location info, run unit tests or do whatever you need them to. And multiple build configurations can be configured or stacked to create almost any system that makes sense to you. That is a huge upgrade right there.

2. New toys! The language has had a lot of new features added. A lot of those were added because they were needed by the .NET CLS. But some of what were added were completely novel. Like class helpers, which is a language construct to implement the Decorator pattern with. This feature is so cool that Microsoft aped it in C# 3.0 with extension methods. Add to that support for generics and anonymous methods and you end up with more expressive constructs that can lead to better – and less – code.

3. A lot of modernisation in the VCL. Unfortunately, the DBGrid still looks like a fugitive from Windows 3.1, but we now have support for a whole lot of modern features, like:

  • Vista glass and task dialogs.
  • Ribbon controls, AKA Office Fluent Interface.
  • Customisable hints that can provide any level of info, right up to the thumbnail previews that you see in Office 2007.
  • Lots of little UI touches, like text hints and balloon tips on edit controls.
  • TStringList can now manage the lifetime of the objects you stored (OwnsObjects property, like TObjectList).
  • Much improved, generics-savvy data structures. Dictionaries, stacks, queues, lists – all type-safe and all with object-aware versions that can manage the lifetime of the objects they store. I love ‘em.
  • Unicode! If you only need to develop for a single language this won’t be such a big benefit, but to thousands of applications this feature is a godsend.

4. It’s a good deal. Back when I used Delphi 7, C++Builder was separate and cost the same. I work at a company with a Software Assurance subscription and we get Delphi, C++Builder and Prism all for one single price. And I’m sure you’ve seen the Buy One Get One offer? I recon RAD Studio+Change Manager looks like an absolute winner.

5. Time marches on. The longer you put it off, the harder it gets to upgrade. If you took every version from Delphi 2005 onward, every upgrade would be small enough to be manageable. Even the Unicode port was relatively painless once our third-party controls became available. But jumping from Delphi 7 to Delphi 2009 is already quite a large amount of work. That is only going to keep growing as you continue to procrastinate.

And how about changes in the underlying operating system? Sure you can keep writing your own libraries to use the new features or keep ignoring them, but neither is a very good idea. And the rules keep changing – you don’t store setting files in Program Files anymore, when the mouse hovers over your application’s task button you should now show a preview and so on. Name and shame time: I love MediaMonkey, but their Delphi 7 app is starting to reek like an old-age home. My PC at home runs Windows 7 and MediaMonkey is a decade behind how my other apps behave.

Lastly, the community moves on too. The Delphi community is one of Delphi’s great strengths and through the years I have picked up probably thousands of code snippets that have made my work better in some way – even improve my way of thinking as a programmer. The longer you stay behind, the greater the percentage of advice, solutions, tips and humour that will be irrelevant to you.

To me, that is probably the scariest thought of all.

Comments (21)

Hellishly Horrible Hack #1: Changing an object’s functionality without recompiling

Yup, you read that right. This one comes with a big disclaimer. It’s been tested and works but don’t read on if you are squeamish.

The set-up is like this:

We have an application that is deployed via XCopy and a second team in another city who is watching this with a hawk-eye. They have a stable system and don’t want changes that they are not absolutely certain they want. Lots to complain about in this kind of scenario, but that’s what we have to work with.

Now a requirement landed on my desk to change a piece of functionality without sending them a new version of the package (which is used everywhere) that it resides in. Fortunately, this unit (call it One) is only called from one other unit (call it Two).

Unit Two in turn is being used from the main executable – a very tiny little thing that basically loads all the runtime packages that do the actual work. So our hawkish friends are happy to take a change to the main executable, but not to this core package.

My first thought was to add the new functionality to a copy of One, which can be called directly from the executable and be folded back into the core package for the next major version of the application.

This seemed fine, until I realised that the object in unit Two is globally accessible and referenced from all over. Lots and lots of callers that cannot be recompiled. Yikes.

After the laughter gave way to despair, I finally decided that this calls for a hack. You know, the kind of thing you always sagely warn others against. After all, exactly how else do you change the functionality of Two without recompiling it?

Let me rephrase that. How do I call my new code in One, while leaving the internal state of Two identical to how it would have been if I’d used it to call One?

So what is an object anyway?

In Delphi, as you certainly know, object variables are really pointers to the actual objects, so MyList: TList creates a new pointer variable to which you can assign the memory address of a TList object. Some programmers prefer the word reference simply because Delphi handles the dereferencing of the pointer on your behalf, but a pointer is a pointer no matter what you call it.

So what does the object pointer point to exactly?

Well, it points to a record structure. Seriously. The first field of this record structure is a pointer to your virtual method table. Incidentally, if you declare a variable of a class reference type (TClass, TPersistClass…) that is also actually a pointer to the VMT. And if you call ClassType on any object variable, you also get a pointer to the VMT (typecast to a TClass – a pointer is a pointer, right?)

The second field in your record structure is the first field of your class. So, the hypothetical

TMyObject = class
private
  ID: Integer;
  Age: Integer;
  Income: Double;
end;

is identical to

TMyObject = ^TMyRec;
TMyRec = record
  VMTPtr: TClass;
  ID: Integer;
  Age: Integer;
  Income: Double;
end;

Before the flame war starts, let me point out that these two declarations are different in semantics, purpose and the warm fuzzy feelings they give to programmers, but they are identical in memory.

I can get more into this in a future post, but the thing to note here is that our object variable is in the end just another block of memory with a predictable layout. So if I needed to change the internal state of my object, I could do it without paying attention to scope identifiers, methods, implemented interfaces, or the weather.

So I could create a second class with my new functionality as long as the in-memory position of the fields I want to manipulate are the same.

TMyNewObject = class
private
  FID: Integer;
  FAge: Integer;
  FIncome: Double;
  FMonthlyWage: Boolean;
public
  procedure CalculateIncome(ID: Integer; IsMonthly: Integer);
end;

Notice that I have added a field to my new class as well as a method that can implement my new functionality. I’ve kept the other fields the same but could have changed them in any way that would leave the positions the same for those I use in TMyNewObject.

So now my executable can create an instance of TMyNewObject, call CalculateIncome and… then what? Well, remember the whole thing about the TMyObject instance being globally accessible? I need to find a way to assign the values stored in TMyNewObject back to TMyObject without being hindered by the private scope of those fields. But an object is just another block of memory, right?

To copy memory from the one to the other, I need three things:

  1. Two object instances, namely Obj: TMyObject and NewObj: TMyNewObject.
  2. The in-memory size of the data used by the TMyObject instance.
  3. A function to copy from NewObj^ to Obj^.

Requirement 1 is easily met, requirement 2 is met by calling Obj.InstanceSize and requirement 3 is met by using the following:

Move(Pointer(NewObj)^, Pointer(Obj)^, Obj.InstanceSize);

And that’s it! Well, sort of.

This will work just dandy as long as you use only simple types like Integer, Char, Double or Boolean. It does not, however work for managed types like strings or referenced types like objects. And it has a little type confusion issue as well.

String, interface and dynamic array types are managed types and if we just copy NewObj’s data over Obj’s data, we lose Obj’s pointer to this managed memory which means we leak memory. Also, and possibly worse, the memory manager has the impression that only one reference to your managed object exists, so it will free that object as soon as you free either Obj or NewObj. If you then free the other one, you’ll likely receive the dreaded “Invalid pointer operation” message.

A similar issue exists with any pointer or object types, because you lose the pointer to Obj’s data.

The solution here is to not copy the memory from NewObj to Obj, but rather to swap it. You know, like switching the values of two integer variables:

C := A;
A := B;
B := C;

We’ll put this in a reusable function. We’ll also let this new function automatically ensure that it only copies the memory the two objects have in common – so the lesser of Obj.InstanceSize and NewObj.InstanceSize.

The type confusion issue I mentioned relates to the VMT pointer. Remember I said that is actually used for the class type? So if you copy the entire memory block from NewObj to Obj, you are actually changing the runtime type. Certain constructs will still work as expected (confusingly, the is-operator is just fine) but others return type info from TMyNewObject (like ClassInfo or ClassName). Worse, your virtual methods could be all messed up.

So instead of casting like I did in the Move-call above, I’ll use this inline function to skip the VMT pointer:

function GetObjData(Obj: TObject): Pointer; inline;
begin
  Result := Pointer(Integer(Obj) + SizeOf(TClass));
end;

And then we get a perfectly generic function that doesn’t leak memory or cause pointer errors:

procedure SwitchObjects(Object1, Object2: TObject);
var
  Buffer: Pointer;
  BufSize: Integer;
  BuffOffset: Integer;
begin
  BuffOffset := SizeOf(TClass);
  BufSize := Min(Object1.InstanceSize, Object2.InstanceSize) - BuffOffset;
  GetMem(Buffer, BufSize);
  try
    Move(GetObjData(Object1)^, Buffer^, BufSize);
    Move(GetObjData(Object2)^, GetObjData(Object1)^, BufSize);
    Move(Buffer^, GetObjData(Object2)^, BufSize);
  finally
    FreeMem(Buffer);
  end;
end;

So for my nightmare scenario that I described at the start of this post, the solution turned out to be simple. Instead of directly calling the needed method on my global object, I passed it as parameter to a function that:

  1. Creates my new functionality object.
  2. Calls the new functions.
  3. Switches content with the global object.
  4. And returns.

This is about as clean as the hack can get, I’d say. Obviously, the new functionality will be folded back into the core package for the next major release of our software, but for now everyone is happy.

What truly ugly hacks have you had to apply?

Comments (3)

Translating flags in API calls, part 2

As promised, this is part 2 of my article on translating flags in API calls. If you haven’t read part 1 yet, please do so now.

Sometimes, you get API calls that make real hefty use of flags. One such example is DrawFrameControl, which has one flag to specify the type of control to draw as well as a second flag to specify the style of that control. The values are all declared as DFC_ or DFCS_ constants and they may be freely combined. Only, not all combinations work together and a lot of developers combine the flags using the plus-operator instead of or, which certainly doesn’t help.

Following on from the last post, we can create several overloads, each of which contain only the list of values relevant to the type of control:

function DrawFrameControl(DC: HDC; const Rect: TRect;
  uType: TDrawFrameControlType; uState: TDrawFrameControlCaptionStyles): BOOL; stdcall; overload; external user32 name 'DrawFrameControl';
function DrawFrameControl(DC: HDC; const Rect: TRect;
  uType: TDrawFrameControlType; uState: TDrawFrameControlMenuStyles): BOOL; stdcall; overload; external user32 name 'DrawFrameControl';
function DrawFrameControl(DC: HDC; const Rect: TRect; uType: TDrawFrameControlType; uState: TDrawFrameControlScrollStyles): BOOL; stdcall; overload; external user32 name 'DrawFrameControl';
function DrawFrameControl(DC: HDC; const Rect: TRect; uType: TDrawFrameControlType; uState: TDrawFrameControlButtonStyles): BOOL; stdcall; overload; external user32 name 'DrawFrameControl';

Once you then specify your control in uType, the values you pass to uStyle will select the correct overloaded version of this function.

Of course, if it was that simple I wouldn’t have done a separate post about this. In the case of this function (and several others), the Windows API conspires against us.

First, we have the shared states. All your control types can have the following states:

DFCS_INACTIVE = $100;
DFCS_PUSHED = $200;
DFCS_CHECKED = $400;
DFCS_TRANSPARENT = $800;
DFCS_HOT = $1000;
DFCS_ADJUSTRECT = $2000;
DFCS_FLAT = $4000;
DFCS_MONO = $8000;

There is no really sensible way of combining these with your other control-specific states, other than to add these to each of your types. So the State type for button controls will look like this:

TDfcButtonStyle = (DFCSBUTTONRADIOIMAGE, DFCSBUTTONRADIOMASK,
  DFCSBUTTONRADIO, DFCSBUTTON3STATE, DFCSBUTTONPUSH,
  DFCSINACTIVE = 8, DFCSPUSHED,
  DFCSCHECKED, DFCSTRANSPARENT, DFCSHOT,
  DFCSADJUSTRECT, DFCSFLAT, DFCSMONO,
  DFCSBUTTONRANGE = 31);
TDfcButtonStyles = set of TDfcButtonStyle;

And you’d have something similar for all four types (buttons, captions, menus/popup menus and scroll bars). Once again, notice that DFCSINACTIVE gets a value of 8 instead of $100, because we are interested in the bit position.

The issue then of course becomes on of naming the values. You see, if you have DFCSFLAT in both TDfcButtonStyle and TDfcCaptionStyle the compiler will see them both and complain about the identifier being re-declared.

My favourite solution to this, is using the {$SCOPEDENUMS} compiler directive. This basically makes the Delphi compiler treat enumerated types in a way that is very similar to that of C# – the actual enumerated values are only in scope when you specify the type. I don’t like needing to think up unique two-character prefixes for each type anyhow, but in this case I find scoped enumerations to be invaluable.

Not only is TDfcButtonStyle.DFCSHOT now a distinct value from TDfcCaptionStyle.DFCSHOT, but I can in fact clean the names up to look more like it was thought up with mortals in mind:

TDfcButtonStyle = (ButtonRadioImage, ButtonRadioMask,
  ButtonRadio, Button3State, ButtonPush,
  Inactive = 8, Pushed, Checked, Transparent, Hot,
  AdjustRect, Flat, Mono, Range = 31);
TDfcButtonStyles = set of TDfcButtonStyle;

No doubt this technique will have lots of traditionalists up in arms, but I think it looks much cleaner and more readable.

The second issue is a little worse and in a twist of irony, is even worse when scoped enumerations are used.

You’ll notice there is no DFCBUTTONCHECKED or ButtonChecked in the examples above. That’s because this Windows constant has a value of zero. And Delphi set types don’t handle zero at all. You could pass [] for that parameter, but then it is no longer obvious that you are trying to draw a checkbox. Also, the compiler will have a torrid time distinguishing between the DrawFrameControl overloads.

I found two ways of getting this to work, but they both have the word hack scribbled all over them.

Firstly, you could add ButtonCheck to the above enumeration at some unused position, say 6. This works and is transparent to the client code, but the success of the technique depends entirely on the API function you’re calling. If it has an undocumented feature, or it changes in the next Windows version or it keels over when an unknown flag is passed in, you’re toast. Experience shows it to be mostly reliable, but it is still not to be fully trusted.

The second solution – the one that doesn’t like scoped enumerations – is to declare the missing value as a typed constant:

DFCSBUTTONCHECK: TDrawFrameControlButtonStyles = [];

This is future proof but has another issue – inconsistent use. Compare the calls to draw a checked radio button and a checked check box:

DrawFrameControlTS(PaintBox.Canvas.Handle, PaintRect,
  DFCBUTTON, DFCSBUTTONCHECK + [DFCSCHECKED]);
DrawFrameControlTS(PaintBox.Canvas.Handle, PaintRect,
  DFCBUTTON, [DFCSBUTTONRADIO, DFCSCHECKED]);

And no, I don’t have a magic trick that will make this work better.

So the take-home for this two-part article is this:

It is possible to create type-safe imports for Windows API functions that are every bit as natural to use as native Delphi functions. Some functions make strange use of flags and these can either use some of the described techniques to work around the hardship, or could be imported using good ol’ Cardinal.

As always, the situation and your own better judgement should serve as a guide.

Comments (2)