1. Technology
Records in Delphi - Part 2
Why and when to use variant records, plus creating an array of records.
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 More of this Feature
• Part 1: Records in Delphi
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• A Beginner's Guide to Delphi Programming
• Arrays in Delphi

Article submitted by: Kevin S. Gallagher

A record is a special kind of user-defined data type. A record is a container for a mixture of related variables of diverse types, referred to as fields, collected into one type. Records are sometimes called complex types, because they are made up of other data types. Other data types by comparison, are often referred to as simple data types.

Essentially, a record data structure can mix any of Delphi's built in types including any types you have created.

Record Types
I can hear beginners saying "I really don't have a use for them…" or "I will learn them later when I am not so busy". Well, later is not always the best time to learn things, especially when "later" may be crunch time when unwelcome bugs habitually creep into applications!

Records are commonly used in Microsoft Windows API calls, where they are referred to as "structures", which is C++ programming language terminology for a very similar thing.

Suppose you are writing an application and you need to determine a form's original state before minimizing or maximizing the form, or get/set the size a form can shrink to or grow to. Some of this can be done with plain old Delphi code while other parts need to be done using API calls. If you need to restrict form sizing then you are going to have to use WM_GETMINMAXINFO from the API. As you might have guessed, that uses a record. In Delphi Win32 Help you will find that the record used is defined as:

typedef struct tagMINMAXINFO { // mmi 
  POINT ptReserved;
  POINT ptMaxSize; 
  POINT ptMaxPosition; 
  POINT ptMinTrackSize; 
  POINT ptMaxTrackSize; 
} MINMAXINFO; 

The Delphi architects thoughtfully wrote the interface translation to handle this, but you need to search though the Delphi source code to find the information. The following record is defined in Messages.pas:

TWMGetMinMaxInfo = record
  Msg: Cardinal;
  Unused: Integer;
  MinMaxInfo: PMinMaxInfo;
  Result: Longint;
end;

The rest is found in the Windows unit:

type
{ Struct pointed to by WM_GETMINMAXINFO lParam }
  PMinMaxInfo = ^TMinMaxInfo;
  TMinMaxInfo = packed record 
  ptReserved: TPoint;
  ptMaxSize: TPoint;
  ptMaxPosition: TPoint;
  ptMinTrackSize: TPoint;
  ptMaxTrackSize: TPoint;
end;

Of course you could say that not much knowledge is needed for accomplishing the task we set up, but what if Delphi didn't have code for the above? Obviously you would have had to write it yourself, and without the proper knowledge it would be impossible to code. At times you need many pieces of information about a form. One of the API calls to acquire the information is called GetWindowPlacement, and to change form stuff that Delphi does not directly handle you might need to call SetWindowPlacement. Both require the use of this record:

typedef struct _WINDOWPLACEMENT { // wndpl 
  UINT length;
  UINT flags; 
  UINT showCmd; 
  POINT ptMinPosition; 
  POINT ptMaxPosition; 
  RECT rcNormalPosition; 
} WINDOWPLACEMENT;

Delphi has defined it, but if they had not, we would be translating it ourselves!

Tkg_WINDOWPLACEMENT = Record
  Length : Integer;
  Flags : Integer;
  ShowCmd : Integer;
  ptMinPosition : TPoint;
  ptMaxPosition : TPoint;
  rcNormalPosition : TRect;
end; 

Variant records
This demonstrates the importance of knowing how to work with records for API calls. While everyday cases are not obvious, they do arise. One is hinted at in Delphi Help, with an example:

TPerson = record 
  FirstName : string[40];
  LastName : string[40]; 
{ Fixed portion of record begins here }
  BirthDate: TDate;
  case 
     Citizen: Boolean of 
{ variant portion of record begins here }
      True: (BirthPlace: string[40]);
      False: 
        (
        Country: string[20];
        EntryPort: string[20];
        EntryDate: TDate;
        ExitDate: TDate
        );
end; 

As you can see, record type can have a variant part, which looks like a case statement. The first part of the declaration - up to the reserved word case - is the same as that of a standard record type. The remainder of the declaration - from case to the optional final semicolon - is called the variant part.

The above variant record declaration has a section (which must follow the fixed section) that can have multiple personalities. In the above example,

if Citizen equaled true then we would have:

TPerson = record
  FirstName, LastName: string[40];
  BirthDate: TDate;
  BirthPlace: string[40];
end; 

If Citizen was false:

TPerson = record
  FirstName, LastName: string[40];
  BirthDate: TDate;
  Country: string[20];
  EntryPort: string[20];
  EntryDate: TDate;
  ExitDate: TDate;
end; 

Stacking Records into An Array
Now we have a understanding of records, let's step into another dimension and create an array of records which allows you to store multiple records which can be returned to a calling form. Place the following declaration into the interface section of a form.

TPerson = record
  FirstName, LastName: string[40] ;
  BirthDate: TDate ;
  BirthPlace: string[40] ;
end;

Add a Memo control and a button to the form, enter the code below:

procedure TForm1.Button1Click(Sender: TObject);
var
  MyPeople: Array[0..2] of TPerson ;
  i:Integer ;
begin
  Memo1.Clear ;
  for i := 0 to 2 do 
  begin
    MyPeople[i].FirstName := 'MyPeople[' + IntToStr(i) + '].FirstName' ;
    MyPeople[i].LastName := 'MyPeople[' + IntToStr(i) + '].LastName';
    MyPeople[i].BirthDate := Now;
    MyPeople[i].BirthPlace := 'MyPeople[' + IntToStr(i) + '].BirthPlace';
  end ;

  for i := 0 to 2 do 
  begin
    with Memo1.Lines do 
    begin
     Add(MyPeople[i].FirstName + ' ' + MyPeople[i].LastName);
     Add(DateToStr(MyPeople[i].BirthDate)); 
     Add(MyPeople[i].BirthPlace);
     Add('');
    end;
  end;
end;

Pressing the button populates the array of records with dummy information, then displays the records in the memo control. This should give you a starting point to working with record arrays. The main thing to remember about the example above is that we created a record type, supplied the name of TPerson, then created a local variable called MyPeople which is an array of type TPerson which can hold three (3) rows of information.

That's it for now.

Related Video
Convert Color Photos to Black and White in Photoshop

©2014 About.com. All rights reserved.