How to get the location of the user’s “My Documents” folder

Your application allows your users to create and save files. You help your users by remembering where the last file was stored they don’t have to navigate their entire folder structure every time they want to save a file.

procedure TMainForm.SaveButtonClick(Sender: TObject);
begin
  SaveDialog1.InitialDir := UserSettings.LastUsedFolder;
  SaveDialog1.Execute();
  UserSettings.LastUsedFolder := ExcludeTrailingPathDelimiter(ExtractFilePath(SaveDialog1.FileName));
end;

So far so good.

First time experience

To make you application even better, you want to improve the experience of saving something for the very first time after installation. Right now, as the value for the “last save location” is still an empty string, the save dialog takes you straight to the user’s “Documents” library. [1, 2, 3]

While not bad, what you really want is to send your users straight to the “Your Beautiful App” folder in their “My Documents” folder.

You have already changed your save code to look like:

procedure TMainForm.SaveButtonClick(Sender: TObject);
begin
  if UserSettings.LastUsedFolder = '' then
    SaveDialog1.InitialDir := IncludeTrailingPathDelimiter({PathToMyDocuments}'') + 'Your Beautiful App'
  else
    SaveDialog1.InitialDir := UserSettings.LastUsedFolder;

  if SaveDialog1.Execute() then
    UserSettings.LastUsedFolder := ExcludeTrailingPathDelimiter(ExtractFilePath(SaveDialog1.FileName));
end;

All you need now is a way to find the correct path for someone’s “My Documents” folder.

So where is this thing?

Trouble ahead?

Well… that happens to depend on a the Windows version and localization that your application is running under. Different Windows versions have put the user’s “My Documents” folder in different locations. It has travelled from C:\Documents and Settings\Marjan\My Documents (XP and earlier) to C:\Users\Marjan\My Documents (Vista and up). [4]

Then there is localization. What you know as the My Documents folder is the Mijn documenten folder on my Dutch Windows.

And roaming profiles for network users where the entire user profile isn’t even on the C: drive, but somewhere on their network.

Nah…

Luckily, you don’t need to worry about all this. Windows has it all covered for you. All you need to do is ask Windows for the path you need. Depending on the Delphi version you use, that is a breeze or a slightly stiffer wind.

Delphi XE5 and up

If use Delphi XE5 or later, you are home free. Getting the location of the user’s My Documents folder is a simple matter of calling the GetDocumentsPathmethod of the TPath record in System.IOUtils:

Memo1.Lines.Add('Home path (%USERPROFILE%): ' + TPath.GetHomePath);
Memo1.Lines.Add('Documents (My Documents): ' + TPath.GetDocumentsPath);
Memo1.Lines.Add('Pictures (My Pictures): ' + TPath.GetPicturesPath);
Memo1.Lines.Add('Music (My Music): ' + TPath.GetMusicPath);
Memo1.Lines.Add('Movies (My Videos): ' + TPath.GetMoviesPath);
Memo1.Lines.Add('Temporary files (TEMP): ' + TPath.GetTempPath);

The TPath record in XE5 and up has support for a whole slew of platform agnostic folders such as the “Home”, “Pictures”, “Music”, and “Temp” folders. On the Windows platform these are conceptually known as Known Folders. Quite nice stuff. You can even add custom folders to Windows’ default set.

The platform agnostic folders that TPath supports are documented on
Standard RTL Path Functions across the Supported Target Platforms

Pre XE5

If you are using an earlier Delphi version, then you will have to call the Windows API functions yourself.

On Windows 7 and up you can use SHGetKnownFolderPath. On earlier Windows versions you will need to use its predecessor SHGetFolderPath or SHGetSpecialFolderPath

But … SHGetFolderPath is marked deprecated on MSDN. And SHGetSpecialFolderPath is marked “not supported. Instead, use SHGetFolderPath”.

Eeeks. Now what?

Hang on to your hat. Deprecated is not the same as “gone”.

In fact, Delphi (XE6 in this case) doesn’t yet include an external function declaration for SHGetKnownFolderPath. It only declares SHGetFolderPath in the Winapi.SHFolder unit. And indeed the TPath methods in XE5+ use that function as well..

What’s more Delphi 2009 doesn’t even have the SHGetFolderPath declaration yet. It only has a declaration for SHGetSpecialFolderPath. And that still works when used on Windows 7. [5]

If all your customers are on Windows 7 and up, you could go with SHGetKnownFolderPath and ignore the older functions. If however some of your users are still on Vista, then you will have to use SHGetFolderPath (as well) to support them.

I have opted to just go with SHGetFolderPath as newer Windows versions still support it and it is preferred over SHGetSpecialFolderPath in the MSDN documentation.

If you are on a Delphi version that does not provide an external declaration for SHGetFolderPath it is easy enough for you to add it yourself. You need a declaration

function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle; dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;

and an “implementation”:

function SHGetFolderPath; external 'SHFolder.dll' name 'SHGetFolderPathW';

Instead of just adding these to some “utils” unit and calling them directly where I need to get a folder location, I prefer to isolate my code from API’s and third party libraries. So I have created a “helper” unit with a record to hide the nitty gritty of calling the Windows API. You could, of course, also opt for a “static” class (so you don’t have to instantiate it).

Benefits

  • When Microsoft does decide to remove the SHGetFolderPath API, you only need to change a single unit. All your other code can remain unchanged.
  • You create code that is better unit testable.
    As unit tests should be independent of outside influences, you would also have to use some mock, stub or shim to “fake” the dll that provides the API.
unit FolderHelper;

interface

type
  RFolderHelper = record
  strict private
    class function GetFolder(const aCSIDL: Integer): string; static;
  public
    class function GetMyDocumentsFolder: string; static;
    class function GetMyMusicFolder: string; static;
    // ... any other folders in which you are interested
  end;

implementation

uses
  ShlObj, // Needed for the CSIDL constants
  Windows;

  function SHGetFolderPath(hwnd: HWND; csidl: Integer; hToken: THandle;
    dwFlags: DWord; pszPath: LPWSTR): HRESULT; stdcall;
    forward;
  function SHGetFolderPath; external 'SHFolder.dll' name 'SHGetFolderPathW';

class function RFolderHelper.GetFolder(const aCSIDL: Integer): string;
var
  FolderPath: array[0 .. MAX_PATH] of Char;
begin
  SetLastError(ERROR_SUCCESS);
  if SHGetFolderPath(0, aCSIDL, 0, 0, @FolderPath) = S_OK then
    Result := FolderPath;
end;

class function RFolderHelper.GetMyDocumentsFolder: string;
begin
  Result := GetFolder(CSIDL_PERSONAL);
end;

class function RFolderHelper.GetMyMusicFolder: string;
begin
  Result := GetFolder(CSIDL_MYMUSIC);
end;

  // ... similar implementations for the other folders

end.

Just as with classes the fact that the GetMyDocumentsFolder is declared as a class method means you can call it without an instance variable:

SaveDialog1.InitialDir := IncludeTrailingPathDelimiter(RFolderHelper.GetMyDocumentsFolder) + 'Your Beautiful App'

Enjoy!


Notes on the FolderHelper unit

  • A convention my colleagues and I adopted some time ago with regard to record declarations is to prefix their names with an “R” rather than “T” as is the custom in Delphi. I find it very convenient that the type name makes it obvious that it is a record not a class. It means I don’t have to Ctrl-Click through to the declaration of that type to check whether or not the lack of calls to construct or free it is as it should be.
  • class methods in records require that you mark them with static as well. I have no clue as to why. The compiler simply demands it. Funny quirk that.
    I have always found that class methods in Delphi should not have been named “class methods” as it introduces a lot of confusion when talking about the methods of classes.
    It would have been so nice if they had been named “static methods” just like other languages have done.
    Unfortunately, in Delphi, static was used to name the default binding method where the declared (compile-time) type of the class or object variable determines which implementation to activate. In other words, in Delphi, all methods that are not marked as virtual or dynamic are understood to be static.
    So why do class methods in a record need to be explicitly declared as static? It’s the default after all? And last time I checked records do not support inheritance, so virtual and dynamic binding is not even in the picture for them?
  • I follow the “always initialize your variables” rule.
    May seem strange then that the GetFolder method is lacking a Result := ''; statement.
    But it isn’t. Not really.
    The compiler does it.
    The result is of type string.
    A string is a managed type and managed types are always initialized by the compiler.

Footnotes

  1. This is the default on Windows 7. On earlier Windows versions this may have been different. Though I don’t know when the desktop became the default, IIRC it once defaulted to the current dir, which of course is the location of your application’s executable unless you start that through a short cut with a different “start in” location.
  2. When you run with debugging, opening the save dialog for the first time is incredibly slow. Probably because of the debug hooks and the number of dll’s being loaded to support the save dialog. The second time you open the dialog is is a lot faster than the first time, but to all intents and purposes it is still slow.
  3. When you run with debugging, the first time you open the save dialog it goes straight to the … Desktop ?! wtf? Interestingly, when you cancel the dialog and reopen it, it does go to the user’s “Documents” library… ?! But with a non blank value in InitialDir it keeps going to the desktop regardless of how many times it has been opened?!
  4. Even though Windows reports my “My Documents” folder as C:\Users\Marjan\Documents, it actually lives on my D: drive… :) But Windows handles that in a completely transparent manner for all applications so you don’t need to worry about it.
  5. Interestingly, the ShlObj unit in D2009 does have a declaration for SHGetFolderLocation (which gets the location of a folder relative to the desktop) and mentions SHGetFolderPath in a comment.
Posted in Software Development
Tags: , , , , , , , , ,

How to find all instantiated forms in your project at run time – Take two

Boy, do I feel stupid!

As David pointed out in the comments to take one of this subject, there is a much easier way to get all the instantiated forms at run time:

var
  CustomForm: TCustomForm;
  idx: Integer;
begin
  for idx := 0 to Screen.CustomFormCount - 1 do
  begin
    CustomForm := Screen.CustomForms[idx];
    Memo1.Lines.Add(Format('%s (%s)', [CustomForm.Name, CustomForm.ClassName]));
  end;
end;

Forgot all about Screen.CustomForms existence. And it’s been there since at least D5… My only excuse is that I haven’t done much UI programming in the last, what, seven years.

There is also Screen.Forms, but that only lists TForm descendants. While Screen.CustomForms lists all TCustomForm descendants including for example property pages.

Caveat
There is a caveat in using either. According to the docs, the order in which instantiated forms appear in both lists is affected by the Z-order of all forms. So while you are iterating over one of these lists, you shouldn’t do anything that might affect the Z-order, such as creating and/or activating another form in your application, as that would change the contents of the list!

If you need to do anything like that, then your best bet is still to use Application.Components like I showed in take one.

Posted in Software Development
Tags: , , , , , , ,

How to find all instantiated forms in your project at run time

For reasons quite unfathomable by me, you would like to get your hands on a list of all forms in your project. (*)

But wait. Do you want this at design time? Or at run time?

Oh, at run time. Pfffew. Ok. I can do that. (**)

But wait. Do you want a list of the forms that are instantiated? Or all the forms?

Please say instantiated. I can do that. It’s easy.

All forms is harder. Unless… you are using D2010 or higher.

Let’s ease into this. Let’s start with finding the instantiated forms first. I’ll cover finding all forms in a future post (if I forget, feel free to remind me).

All instantiated forms

**Edit**
As David mentioned in the comments, there is a much easier way of getting all instantiated forms in your application at run time. It has a caveat though. See take two on this subject for more information.

Finding the instantiated forms should be easy. Just iterate over the Application’s components and check each to see whether it is a descendant of TCustomForm.

Why the check? Because the Application’s component list can include components that are not forms. Any data modules that you have instantiated are in there. As well as any other components that you have instantiated passing the Application as their owner.

Why not check for descendants of TForm? Because there is nothing that says that a forms should descend from TForm. TForm just happens to be the class that forms created through the IDE descend from. And all it does when compared with TCustomForm is give a whole slew of properties published visibility so the Object Inspector can see them. (For the nitpickers amongst us: it also adds a class constructor and destructor to register and unregister a style hook.)

Our first try would thus look like:

var
  Component: TComponent;
  idx: Integer;
begin
  for idx := 0 to Application.ComponentCount - 1 do
  begin
    Component := Application.Components[idx];
    if Component.InheritsFrom(TCustomForm) then
      // Whatever you want to do with them. Me, I am just adding them to a Memo.
      Memo1.Lines.Add(Format('%s (%s)', [Component.Name, Component.ClassName]));
  end;
end;

That should get you a list of all instantiated forms in your application.

[Grumble]
By the way, I hate that we still have to use an ordinary for loop here. I would much prefer to use a for-in loop:

for Component in Application.Components do

I really don’t understand why the powers that be haven’t seen fit to enable this. All it takes is an enumerator and it would make a lot of code that much more readable and less (off-by-one) error prone.
[/Grumble]

But wait…

The code above will only get you the instantiated forms that have Application as their owner. That is forms instantiated by either:

Application.CreateForm(TMyForm, MyForm);

or

MyForm := TMyForm.Create(Application);

It won’t get you any of the forms that were instantiated without an owner:

SomeForm := TMySpecialForm.Create(nil);

Neither will it get you forms that were instantiated with a component other than Application as their owner:

SecondForm := TSecondForm.Create(FirstForm);
ThirdForm := TThirdForm.Create(MyDataModule);

You could dig down from Application and examine the entire component tree recursively.

procedure DigDownComponent(const aComponent: TComponent; const aList: TStrings; const aPrefix: string); forward;

procedure DigDownApplicationComponents;
var
  FormList: TStringList;
begin
  FormList := TStringList.Create;
  try
    DigDownComponent(Application, FormList, '');
  finally
    FormList.Free;
  end;
end;

procedure DigDownComponent(const aComponent: TComponent; const aList: TStrings; const aPrefix: string);
var
  idx: Integer;
  Current: TComponent;
begin
  for idx := 0 to aComponent.ComponentCount - 1 do
  begin
    Current := aComponent.Components[idx];

    if Current.InheritsFrom(TCustomForm) then
      aList.Add(Format('%s%s (%s)', [aPrefix, Current.Name, Current.ClassName]));

    if Current.ComponentCount > 0 then
      DigDownComponent(Current, aList, aPrefix + '  ');
  end;
end;

That is a lot more complete. It will get you all forms instantiated with either Application or another component as their owner.

It still won’t get you the forms that were instantiated without an owner though.

There isn’t much you can do about that. Not unless you are prepared to do some heavy lifting in the realms of hooking, enumerating handles, posting and responding to messages, or some other means of getting your hands on otherwise elusive players.

I’d say it would be far easier to set up some form factory or registry of your own.

Assuming you are gunning for some consistency in your forms, you will be using your own base form already anyway, so getting all forms to register themselves upon instantiation should be simple (and may be the subject of a future post).

Footnotes

(*) What does “in your project” mean exactly? Just the forms in units that are included in the dpr’s uses clause? Or also forms in units that are included in uses clauses of other units? But what about forms included in the VCL and RTL then? Or in third party libraries for that matter? Or perhaps in your own libraries? Where do you draw the line? And how would you be able to recognize that line in code?

(**) Getting all the forms in your project at design time is possible too of course. It does need a different approach though. One I would have to do quite some research on. Research that isn’t of much interest to me at the moment as I don’t do a lot of UI development nowadays. That said, I might still dig into it out of curiosity. Couple of gentle pushes from the community might help :)

Posted in Software Development
Tags: , , , , , , ,

How do you know your software works? And keeps working?

Which scenario do you like better?

You wake up in the morning. The sun shines brightly through your window, but for all you care it might as well be pouring with rain. The bug reports are stacked high and low on your desk. The minute you walk in someone from support will be on your back about clients X, Y and Z who have effectively been unable to use your software for days now, making it impossible for them to serve their clients. You hope the coffee machine is working. Otherwise you will have to go down to the floor housing sales and product management. They will jump at the chance to ask you how features A, B and C are coming along. Features they have been waiting on for months now. Is Gary back from holiday today? Darn. That means that you will have to explain why it took so long to fix the export to Excel thingie you thought would only take half a day. Gary is talking about hiring more developers to regain some speed. Nice and all, but where are you going to find the time to train them? The prospect of all this weighs you down, draining all the energy you regained while asleep and then some. You drag yourself out of bed and to work. Not because you enjoy it, but because you need money on the table to pay your bills.

You wake up in the morning. The sun shines brightly through your window. You squint a little clearing the sleepiness from your head. Today is going to be a good day. You are going to finish that cool new feature client C requested two weeks ago. It was an interesting challenge. You learned a lot in the process. It isn’t the last. Plenty more features in the pipeline that will be fun to build. Justin, the product manager, will be around this afternoon to discuss a couple more ideas. Sales have a few prospects that are requesting special features and Gene, the support manager, would like some extra reporting so he can detect hot spots before they become a problem. Oh and Bill is coming over for lunch. Lunches with Bill are the best. Bouncing around ideas and coming up with better ways to do stuff always gets both of you going. Would be interesting to take the new hire along. See how he responds. The prospect of a good day gets you out of bed with eager anticipation.

Me?

I am absolutely partial to the second scenario.

Yesterday I ran across a post on software quality.

It struck a chord with me. It articulates exactly what I feel.

I care deeply about software quality. Not to alleviate my innate fear of failure. Not out of perfectionism. Not to do things “right”.

I care deeply about software quality because it keeps my work fun and interesting.

I’d much rather be building new features than fixing bugs. I prefer to work without the pressure of “fixing” things as fast as possible because a client is screaming hell and bloody murder.

Building quality software is the only way I know how to make this happen and to keep it happening.

All other ways of building software may seem to have very big advantages, but those advantages only manifest in the short term.

Like delivering results quickly.

I like delivering results quickly just as much as anybody else.

Even if you need to take shortcuts to do that.

Shortcuts are fine only as long as you redo them properly immediately after delivery of the feature. If you don’t, those shortcuts come back to haunt you.

Unless you address them immediately and ruthlessly, every shortcut, whether in code, in tests or in documentation, makes new features a teensy bit harder, less reliable and more work to implement.

Which slows down delivery of new features.

And which can introduce subtle bugs that you will have to fix at a later stage when you are no longer familiar with the subject.

Fixing bugs also slows down delivery of new features.

Taking one shortcut may not seem like a big deal. The effect is limited. But unless you fix it, that tiny bit of extra work needs to be done every time you deal with that code. It cumulates quickly.

You think you can take that hit. It is in some part of your code that doesn’t get changed often, so the effect remains limited. No biggie.

However, there is more to accepting a shortcut than accepting the hit on effort required for future changes.

It sets a standard.

That shortcuts are somehow ok.

Soon you won’t have one shortcut but two, then three, five, nine and more.

The more shortcuts you take, the bigger the effect.

After some time of taking shortcuts, the speed at which new features are delivered really starts dropping.

Bug lists get longer. Implementing new features requires more and more knowledge that hasn’t been made explicit anywhere. New hires take a long time to get up to speed. Seniors are stretched beyond breaking point implementing new features only they have the experience with your code base to do properly and guiding less experienced colleagues.

Parts of your code will be in dire need of refactoring.

There will be parts of your code you don’t dare touch for fear of introducing bugs or inadvertently disabling features that you no longer even know exist but that your clients rely on.

The calls for a rewrite will start to be heard and grow in frequency and intensity.

The first scenario looms large overhead.

The only way I know how to avoid getting into that first scenario is relentless attention to and striving for quality software by everyone on the team. Just like Bob describes it in the last paragraph of his post

Posted in Software Development
Tags:

How to get a ListView to display your values in columns

Can’t get a ListView to display the values from your database in columns properly?

You want to display information from your database. You would like it to be displayed in a list with columns. A ListView can do that.

Should be as easy as 1-2-3.

Just set up a query, loop through the records and add the field values to the ListView. Something like this:

    begin
      SQLQuery.SQL.Text := 'SELECT FirstName, LastName, Country FROM Famous_Actresses';
      SQLQuery.Open;
      while not SQLQuery.Eof do
      begin
        ListView.AddItem('First name: ' + SQLQuery['FirstName'], nil);
        ListView.AddItem('Last name: ' + SQLQuery['LastName'], nil);
        ListView.AddItem('Country: ' + SQLQuery['Country'], nil);
      end;
    end;

Right?

Wrong.

When you do it like this, it comes out as a long list of values all in a single column.

ListView displaying values as a list despite vsReport ViewStyle

That’s not what you want.

You want a nice orderly list in which every field has its own column. Just like how Windows Explorer displays files in its details view.

How Windows Explorer displays files in Details view style

Relax. You’re half way there.

Just two things left to do:
– Tell the ListView about the columns you want it to display.
– Add the values from your database to their specific columns.

Setting up columns for a ListView

You can add columns to a ListView in the visual form designer by double clicking the Columns property of the ListView and adding the columns you need. While easy to do, you now have tied that ListView to a specific set of columns. And I bet you that you will have plenty more lists to show in any application worth using.

When you know how to do those things in code, you give yourself the option of reusing a single form for many lists.

So, let’s do it in code:

    var
      Column: TListColumn;
    begin
      // Set up columns
      Column := ListView.Columns.Add;
      Column.Caption := 'First Name';
      Column.Alignment := taLeftJustify;
      Column.Width := -1;

      Column := ListView.Columns.Add;
      Column.Caption := 'Last Name';
      Column.Alignment := taLeftJustify;
      Column.Width := -1;

      Column := ListView.Columns.Add;
      Column.Caption := 'Country';
      Column.Alignment := taLeftJustify;
      Column.Width := 140;
    end;

Tips
– Set a column’s Width to -1 to make the column display its entire text.
– Set a column’s AutoSize to true to make it resize proportionally when the Width of the ListView changes.

Adding values to specific columns

Now that you have the columns set up, you are all set to make the ListView display your information as you want it. The only thing left to do is to add the values from your database in the way the ListView expects it for the vsReport ViewStyle.

You already got most of the code right in your first try. Just change the lines where you added items to the ListView to:

    var
      Item: TListItem;

      begin
        Item := ListView.Items.Add;
        Item.Caption := SQLQuery['FirstName'];
        Item.SubItems.Add(SQLQuery['LastName']);
        Item.SubItems.Add(SQLQuery['Country']);
      end;

And now you have a well behaved ListView that displays your database values in columns properly.

ListView with Proper Columns

Posted in Software Development
Tags: , , ,

Why does my panel display the wrong color?

You are working on your application and want to add some color to spruce it up a bit. Setting colors using Delphi’s Object Inspector is a breeze. That is, when you use the predefined colors from the color property’s drop-down list.

Object Inspector Color Drop-Down

Or when you use the color dialog that opens when you double click the Object Inspector’s edit for the color property.

Color Dialog

True, the dialog allows for a lot more colors than are available as predefined colors. But you find it cumbersome to use. It requires a double click to open the dialog and an extra click to open the “custom colors” part. It even isn’t smart enough to remember that you had the custom colors open the last time you used it. And it’s annoyingly fiddly to set the hue, saturation and brightness using the rectangle and slider. Using the edit boxes when you want more precise control over the color isn’t much better. In fact it is … cumbersome.

All you want to do is paste the color code for the nice orange that you found on the web into the color property’s edit box in the Object Inspector.

Nice Orange Color

And Delphi allows you to do just that. All you need to do is copy the color value, paste it into the edit box and replace the ‘#’ with a ‘$’ because Delphi expects a hex value there.

Typing Color Into Object Inspector

But when you do that …

You get a totally different result. Your nice orange color turns blue in the face.

Resulting Color

Gah!

Relax. The solution is simple. Colors on the web are specified as hexadecimal RGB values. Unfortunately Delphi’s TColor type assumes a different order. It interprets the hexadecimal value as if it were specified as BGR.

If you want:

  • red you need to specify $000000FF
  • green you need to specify $0000FF00
  • blue you need to specify $00FF0000

So, to get your nice orange, simple reorder the hexadecimal value you pulled from the web, turning

$00F16E00

into

$00006EF1

Et voilà, there is your nice orange on your panel.

Correct Color

Posted in Software Development
Tags: , , ,

How to store enums without losing your coding freedom

Enums are nice!

You have found out about enumeration types and you clearly see the advantages they hold over plain, tired, old integers. They are specific, self documenting, type safe – you can’t pass one type of enum when another is expected. Oh yes, you love enums. And enum sets. No more flags and bit operations for you.

But …

They are not supported directly by databases. Or INI files. Or Json. Or XML. Or any other type of storage for that matter.

Enums as integers

You know you can store their integer value equivalents. It isn’t exactly hard.

To get the integer equivalent of any enum type, simply use the Ord function:

type
  TMyEnum = (meOne, meTwo, meThree);
var
  MyEnum: TMyEnum;
  MyInteger: Integer;
begin
  MyEnum := meTwo;
  MyInteger := Ord(MyEnum);
end;

And the other way around is just as simple:

begin
  MyInteger := 2;
  MyEnum := TMyEnum(MyInteger);
end;

Easy enough, but storing enums using their integer equivalents has a couple of humongous drawbacks:

  • The values you store in your database lose all their meaning. Would you know, off hand, what `5` means for some `TAccountType` enum?
    While you might be able to memorize the integer equivalents of a couple of enums, doing so for all enums in your code… Eeks. Personally, I prefer to use my brain power for something more interesting.
  • You lose almost all freedom to change your enum declarations. Yes, you can change the names of your enum members and you can add new members at the end. But removing obsolete members, adding new members somewhere in the middle, reordering members so the order makes more functional sense… Big no-no’s! If you did anything like that you would effectively be changing the meaning of integer values that have been stored using the previous declaration.

Storing enums as their integer equivalents is not such a bright idea then. The walls of Jericho may not come tumbling down on you, but you could very well face support staffers beating down your door equipped with tar and feathers because they’d be dealing with disgruntled users after they upgraded to a new version because that changed the meaning of whatever they stored using the previous version of your software. Ouch…

So, what to do?

Enums with specific ordinal values

In all but the earliest couple of Delphi versions, you can give specific enum members specific ordinal values:

type
  TMyEnum2 = (meFive = 4, meSix, meSeven);

Seems nice?

Yeah. Until … you are some changes down the road and your enum declaration becomes a big mess, or … until you read the fine print that says that when you do so, you lose Run-time type identification on that enum.

Run-time type identification

What? Run-time type identification? What is that? Why do I care?

Run-time type identification, more commonly referred to as Run-time type information and RTTI for short, is what allows you to do stuff like:

Log(Format('AccountType has a value of [%s], which is invalid in this context.', 
  [GetEnumName(TypeInfo(TAccountType), Ord(AccountType))]));

and see a message like this in your logs:

AccountType has a value of [atBusinessOwner], which is invalid in this context.

instead of:

AccountType has a value of [7], which is invalid in this context.

Without it, all you can do is cast to an enum’s underlying basic type: byte, word, integer, …

Enums as strings

Storing enums as strings makes your data much more friendly for human eyes.

And … you regain your coding freedoms.

The freedom to order your enum members as you see fit, is pretty obvious. You are now storing names after all, and restoring values by reading names is not dependent on the position of that name in the declaration.

With that automatically comes the freedom to add new members anywhere in the declaration that makes the most sense functionally. New members can’t after all be stored yet, and reading existing names isn’t dependent on their position in the declaration.

The freedom to remove obsolete members is slightly trickier. You need a way to handle the fact that the now obsolete names still exist in stored data. For enum sets you could simply ignore them. For enums you need a sensible fallback value.

The freedom to rename members is the trickiest of the bunch, but is perfectly feasible. Any rename after all can be seen as a combination of deletion and addition. And thus can be dealt with by using the member with the new name as the fallback value for the old one.

Most significantly, inadvertently changing the meaning of stored data because someone rearranged an enum declaration that he shouldn’t have … well, it is no longer an issue. And that is a huge advantage as it makes your software a lot less brittle.

How to …

Best news is, all of this isn’t very hard to achieve.

The TypInfo unit contains the GetEnumName and GetEnumValue functions which do the work for “single” enums:

// ToString
AccountTypeAsString := GetEnumName(TypeInfo(TAccountType), Ord(AccountType));

// FromString
AccountTypeAsInteger := GetEnumValue(TypeInfo(TAccountType), AccountTypeAsString);
if AccountTypeAsInteger >= 0 then
    AccountType := TAccountType(AccountTypeAsInteger);

This still involves quite a bit of type casting. And you need the same kind of statements for every enum in your code, so you will find yourself creating copy-pasta just to avoid having to retype them again and again.

Type casting and creating copy-pasta are two things you should avoid. The combination should have you trembling at the knees.

Preserving type safety

Hiding the casting ugliness behind a couple of functions would be a lot better. A function like:

function AccountTypeToString(const aAccountType: TAccountType): string;
begin
  Result := GetEnumName(TypeInfo(TAccountType), Ord(aAccountType));
end;

function AccountTypeFromString(const aAccountTypeString: string; 
  const aDefault: TAccountType): TAccountType;
var
  AccountTypeInteger: Integer;
begin
  AccountTypeInteger := GetEnumValue(TypeInfo(TAccountType), aAccountTypeString);
  // Value < 0 indicates the string is not a known TAccountType member name.
  if AccountTypeInteger < 0 then
    Result := aDefault
  else
    Result := TAccountType(AccountTypeInteger);
  end;

allows the rest of your code to remain perfectly type specific and type safe:

begin
   MyLogString := AccountTypeToString(AccountType);
   AccountType := AccountTypeFromString(SomeString, atBasic);
end;

You would however need to create ...FromString and ...ToString functions for every enum and enum set in your code. (Unless you can use generics, see further down.)

No fun, I’ll readily admit. But something I have done a lot and think you should do a lot too, because it is a small sacrifice to gain the benefits of less copy-pasta and more expressive and readable code. After all:

AccountTypeToString(AccountType);

reads a lot better than

GetEnumName(TypeInfo(TAccountType), Ord(AccountType));

Generics with extended RTTI

If you are on a Delphi version that supports generics as well as the extended RTTI (D2010+), you are in luck. You can forget about type casting and forget about creating all manner of conversion function pairs. You get completely type safe conversions with just two methods in a “static” (*) class:

(*) If anybody knows the proper “Delphi” way to refer to a class that only contains class methods, I am interested.

type
  TEnum<T> = class(TObject)
  public
    // Use reintroduce to prevent compiler complaint about hiding virtual method from 
    // TObject. Breaking polymorphism like this is no problem here, because we don't 
    // need polymorphism for this class.
    class function ToString(const aEnumValue: T): string; reintroduce;
    class function FromString(const aEnumString: string; const aDefault: T): T;
  end;
  
  class function TEnum<T>.ToString(const aEnumValue: T): string;
begin
  Assert(PTypeInfo(TypeInfo(T)).Kind = tkEnumeration, 
    'Type parameter must be an enumeration');

  Result := GetEnumName(TypeInfo(T), TValue.From<T>(aEnumValue).AsOrdinal);
end;

class function TEnum<T>.FromString(const aEnumString: string; const aDefault: T): T;
var
  OrdValue: Integer;
begin
  Assert(PTypeInfo(TypeInfo(T)).Kind = tkEnumeration, 
    'Type parameter must be an enumeration');

  OrdValue := GetEnumValue(TypeInfo(T), aEnumString);
  if OrdValue < 0 then
    Result := aDefault
  else
    Result := TValue.FromOrdinal(TypeInfo(T), OrdValue).AsType<T>;
end;

You use it like:

type
  TAccountType = (atBasic, atDual, atBusiness);
  TMonsterType = (mtGravatar, mtWavatar, mtMysteryMan, mtRetro, mtIdenticon);

procedure RunExample;
var
  AccountType: TAccountType;
  MonsterType: TMonsterType;
  StringValue: string;
begin
    // ToString

    AccountType := atDual;
    WriteLn(Format('AccountType atDual to string: %s', 
      [TEnum<TAccountType>.ToString(AccountType)]));

    MonsterType := mtWavatar;
    WriteLn(Format('MonsterType mtWavatar to string: %s', 
      [TEnum<TMonsterType>.ToString(MonsterType)]));

    // FromString with a known value

    StringValue := 'atBusiness';
    AccountType := TEnum<TAccountType>.FromString(StringValue, atDual);
    WriteLn(Format(
      'AccountType from string "atBusiness" with atDual as default: %d (%s)', 
      [Ord(AccountType), TEnum<TAccountType>.ToString(AccountType)]));

    StringValue := 'mtMysteryMan';
    MonsterType := TEnum<TMonsterType>.FromString(StringValue, mtIdenticon);
    WriteLn(Format(
      'MonsterType from string "mtMysteryMan" with mtIdenticon as default: %d (%s)', 
      [Ord(MonsterType), TEnum<TMonsterType>.ToString(MonsterType)]));

    // FromString with an unknown value

    StringValue := 'Unknown';
    AccountType := TEnum<TAccountType>.FromString(StringValue, atDual);
    WriteLn(Format(
      'AccountType from string "Unknown" with atDual as default: %d (%s)', 
      [Ord(AccountType), TEnum<TAccountType>.ToString(AccountType)]));

    StringValue := 'Unknown';
    MonsterType := TEnum<TMonsterType>.FromString(StringValue, mtIdenticon);
    WriteLn(Format(
      'MonsterType from string "Unknown" with mtIdenticon as default: %d (%s)', 
      [Ord(MonsterType), TEnum<TMonsterType>.ToString(MonsterType)]));

Which produces the following output:

AccountType atDual to string: atDual
MonsterType mtWavatar to string: mtWavatar
AccountType from string "atBusiness" with atDual as default: 2 (atBusiness)
MonsterType from string "mtMysteryMan" with mtIdenticon as default: 2 (mtMysteryMan)
AccountType from string "Unknown" with atDual as default: 1 (atDual)
MonsterType from string "Unknown" with mtIdenticon as default: 4 (mtIdenticon)

As I said this is completely type safe code without any casting (apart from what TValue may do internally). The compiler will sling errors at you when you do something like this:

MonsterType := TEnum<TMonsterType>.FromString(StringValue, atDual);

or this:

AccountType := TEnum<TMonsterType>.FromString(StringValue, mtMysteryMan);

Because the compiler knows that atDual is not a member of TMonsterType and that FromString does not return a TAccountType.

Me like!

Posted in Software Development
Tags: , , , ,

How to make your forms behave consistently without repeating yourself

You are in the Delphi IDE and need to make a new form. It looks like another form you already have, but not quite. You could copy and tweak it. That’s the quick way to get the result you need. That’s the way most of your predecessors have gone about creating new forms. You know, because every time you change something in one form, a seemingly endless stream of “this one too” requests starts flowing your way. And you suspect, or actually you know, that there will be another request for a similar form quite soon.

Wouldn’t it be great if you could change them all in one place and be done with it?

Well you can.

Delphi supports visual form inheritance. That’s not just inheriting from another form’s class, but also inheriting from its layout, its controls and the way they have been wired together, including the ability to “override” individual settings and adding controls just for the descendant form you are working on.

For new forms visual form inheritance is just a matter of adding a new form to your project through [File | New | Other], navigating to [Inheritable items] and selecting the form that you want to use as the ancestor for your new form.

“All well and dandy, but what about existing forms?”, you ask.

That’s slightly more complicated, but only slightly.

  1. You start by adding a new form that will become your application’s “master form”. Do this in the normal way you would add a form: [File | New | Form - Delphi]. Let’s name this form [BaseForm] and save it as [BaseForm_Form.pas].
  2. Add any stuff that you want all your application’s forms to share, controls, other components such as image lists, event handlers, whatever you like.
  3. Open one of your existing forms and show its .pas file. (Hint: use PF12.)
  4. Add BaseForm to the uses clause.
  5. Change TForm3 = class(TForm) to TForm3 = class(TBaseForm). Of course here TBaseForm is the class name of the BaseForm form you just created and TForm3 stands for the class name of your form.
  6. Go back to the visual representation of your form. (Hint: use PF12.) You won’t see any of the stuff you added to your BaseForm yet.
  7. Right click anywhere on the form’s surface and click [View as Text].
  8. Notice the first word in the textual representation of your form. It is object.
  9. Change that from object to inherited so the first line becomes inherited Form3: TForm3.
  10. Right click anywhere in the form’s textual representation and click [View as Form].

And hey presto! Your form shows the stuff that is on your BaseForm. Now all you need to do is clean up the form so its own controls do not cover any of the BaseForm’s stuff and add an inherited; call to all the event handlers that are implemented both in [BaseForm] and your form that is now inheriting from it.

Posted in Software Development
Tags: ,

Is unit testing literal values pointless?

Well, yes. And no.

You shouldn’t be using literal values anyway. You should be using constants. And when you use constants everywhere, there is absolutely no point in testing whether a constant actually has the literal value assigned to it. No sense in testing the compiler. After all, if you can’t trust your compiler to get this basic stuff right, why are you using it?

And yet…

Some literals are more important than others. If you change these literals, your whole application could break down. Not literally, it won’t crash, but it will no longer function as expected. Despite you being very diligent in your use of constants in lieu of literals. And you won’t find out about it until hours, days, weeks, maybe even months later.

You find out about it when a client is screaming hell and bloody murder because your software stopped doing what it was supposed to do months back. Right after that important update. And now their computer crashed and they find out that they don’t have any data in their backups of your software. How can that be? Nobody has touched the backup functionality for ages …

Turns out that when adding some new functionality somebody inadvertently and unintentionally changed

const
  API_BACKUP = 'backup';

into

const
  API_BACKUP = 'baekup';

Of course this should be covered by integration tests. But integration tests usually take a lot longer than unit tests. When dealing with large code bases often are not part of the developer’s submit checks. And that is hoping that automated integration and regression tests are in place. Quite often integration tests and regression tests are not done by developers if at all… Serving coffee to developers in shops where regression testing is not part of the build or even the release process would leave me bankrupt. I wouldn’t even like to serve coffee to developers in shops where (automated) integration testing is done, but selectively based on the functionality or the units that were touched by a change. Heck, even today someone asked about how to limit the test set because it took so long to run…

Quite apart from the question of integration and regression tests, there is the little matter of the effort involved in addressing the problem once it surfaces. Catching these kinds of “blips” as early as possible helps reduce developer effort. Far easier to solve a failing unit test that pinpoints the cause of the failure than having to figure out why some integration test fell over the next day, or even later or not at all…

So what can you yourself do to prevent the horror scenario described above?

Test a literal against a literal? How pointless can you get?

Ah yes. We don’t want to be pointless.

Still, in the case of literal values that can come from the outside and affect what your software does or how it does it, there absolutely is a point to it. These tests only seem pointless because of their implementation of testing a literal value against the same literal value or – better – testing a constant against its literal value. The point is not in exercising the compiler or the unit test framework, but in guarding against inadvertent and unintentional changes.

Posted in Software Development
Tags: ,

Hello world!

Definition of "on a string"

A developer by trade, an eternal student by heart, I am now turning to teaching.

Or rather: I am now turning to sharing and hoping that someone might learn something from it so I can lay claim to being a teacher in the future.

On the one hand I intend to share what I have learned in almost three decades of software development. On the other hand I will be telling stories of entering fields that are largely unfamiliar to me.

Over the last couple of years I have grown more and more interested in designing software in a technology-agnostic way. It’s how I learned to program way back in the eighties: with flow charts and the design patterns of batch and online transaction processing of the time. I have found that thinking about a software implementation of a requirement without immediately getting down into the details of coding it helps a lot in getting clean solutions. That doesn’t mean a lot of design and design documents up-front. Heck no. I am as allergic to those as most. It can mean drawing informal diagrams with lines and boxes. If only because visualizing something helps understanding it and helps talking with your colleagues about it.

And then there are such things as the development process and designing User Interfaces that create a good User Experience.

I am, or rather have been and sometimes still are, active on three StackExchange sites: StackOverflowProgrammers and UX.

 

Posted in Uncategorized

Like this?

Subscribe and get more posts like this delivered straight into your inbox.
* = required field