Tutorials - Implementing Callback Procedures

'

Introduction

I find that Function and Procedure Callbacks are often underused, yet the benefits of using the can be tremendous. For example they can help to limit duplicate code, and make your code easier to read and more importantly easier to maintain.

What Is a Callback

A callback is a means of passing a procedure(or function) as a parameter into another procedure, so that when a certain event occurs in the procedure that you called, the callback function is called (being passed any parameters that you need) when the callback procedure has completed, control is passed back to the original procedure.

This is best explained with an example: Say you have an array of objects and you often wish to run a certain method on all of them. This is easily achieved by looping through the array calling the required method. Now imagine that you have five different methods that you want to use on all of the objects at different times. Again its easy to do with a bit of copying and pasting but the use of a callback here would be beneficial. All you would need would be the master procedure that looped through the objects, and for each one it would call the callback procedure passed to it with each of the objects as a parameter.

A Sample Callback

For this sample I will show you how to declare the required callback and then use it. The actual routine is used to search directories and call the callback function with each filename.

The Declaration

In the type section of your unit we need to declare what the callback is:

TFileCallbackProcedure = procedure(filename:string) 
                         of object;

All we are saying here is that TFileCallbackProcedure is a procedure with a string parameter and it is a method of an object.


Next we need to declare the procedure that will actually call the callback procedure:
Procedure RecurseDirectory(
    Dir : String;
    IncludeSubs : boolean; 
    callback : TFileCallbackProcedure);

This procedure is like any other, except that one of its parameters is of type TFileCallbackProcedure all this means is that we need another function as a parameter.

The Code

Next we need to implement the RecurseDirectory procedure, it is done like this:

Procedure TForm1.RecurseDirectory(
  Dir : String;
  IncludeSubs : boolean;
  callback : TFileCallbackProcedure);
var
  SearchRec :TSearchRec;
  Result : LongInt;
begin
  Result := FindFirst(Dir+'\*.*', faAnyFile , SearchRec);
  while Result = 0 do
  begin
    { This makes sure its not the . or .. directorys}
    if not(SearchRec.name[1]='.') then
    begin
      if (SearchRec.attr and faDirectory) <> 0 then
      begin
      { its a dir so do a recursive call 
        if subdirectories wanted }
        if IncludeSubs then
          RecurseDirectory(Dir +'\' + SearchRec.name,
                           IncludeSubs, callback);
      end
      else
        { Call are callback function}
        callback(dir+'\'+SearchRec.name);
      end; //if . ..
    Result := FindNext(SearchRec);
  end;
end;
 

I will not explain here everything that the function is doing because I did not plan this article to cover recursion just callbacks (watch for a future article). What it basically does is scan through the Directory it was passed in the Dir parameter, if it finds a file it then calls the callback function that it was passed giving the filename (including the path) of the current file. The recursive bit works by that if it finds another directory in the current directory it will call itself (this is recursion) with the only different parameter being the dir is changed to the new dir that it has found.

All we now need to do is write a callback function and and call the RecurseDirectory procedure.

Start a New Application and place a TButton and a TListBox on the form, using the default names that Delphi provides. Now declare and implement the RecurseDirectory procedure as previously shown.

Next we need to Define and write a simple callback procedure. The declaration in the type part of the unit goes in the private declarations (though this can be changed if you need access to from another class):

   Procedure MyCallback(filename:string);

For this sample all I will do is add the filename to ListBox1. This is done by:

Procedure TForm1.MyCallback(filename:string);
begin
 listbox1.items.add(filename);
end;
 

Finally we need to call the RecurseDirectory procedure using our new Callback. To do this I attached the following to Button1ūs on click event:

procedure TForm1.Button1Click(Sender: TObject);
begin
  RecurseDirectory('c:',true,MyCallback);
end;

All this does is call the RecurseDirectory procedure and for each file that it finds it will call MyCallback which in turn will add the filename to the ListBox.

Your sample app is now ready to run. When you do you should find that when you click on the button the listbox is filled with names and paths of all the files on you C drive, if you have a large drive this may take a while.

Conclusion

I hope you have found this article useful and more importantly can see the use and benefits of using callbacks in your own programs. The sample I gave is a very simple one and can be extended to your hearts consent. Other uses may be: Counting the number of pas files in a directory. Deleting back up files. etc.

Or if you feel more adventurous, looping through all the forms in your application and loading or saving their positions to the registry. The possibilities are endless but in some circumstances this is not the best approach (i.e. only having one possible callback) but its nice to know that you can do it and also that you know how to do it.


Google
Web www.Delphi-Central.com
Delphi Central - Delphi Programming Tutorials, Hints and Tips